diff --git a/CMakeLists.txt b/CMakeLists.txt index 35a86c98ae..6b16cd453e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,12 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules") include(CheckCXXCompiler.cmake) include(CheckIncludeFileCXX) +if(UNIX) + add_definitions(-DUNIX) +else() + add_definitions(-DNOT_UNIX) +endif() + # getgit revision number find_program( GIT_EXEC git ) if(GIT_EXEC) @@ -18,16 +24,18 @@ else(GIT_EXEC) message(STATUS "Couldn't fetch version from git repo" ) endif() + # If from command line, version info is not passed, use the git to generate a # version file. If GIT fails, use the previous known version. if(NOT VERSION_MOOSE) - set(VERSION_MOOSE "3.2.0-${GIT_HEAD}") + string(TIMESTAMP TODAY "%Y%m%d") + set(VERSION_MOOSE "3.2.dev${TODAY}") endif() add_definitions( -DMOOSE_VERSION="${VERSION_MOOSE}") message( STATUS "MOOSE Version ${VERSION_MOOSE}" ) -# Write VERSION to a file VERSION so that setup.cmake.py can use it. +# Write VERSION to a file VERSION so that setup.py can use it. set(VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/python/VERSION) file(WRITE ${VERSION_FILE} ${VERSION_MOOSE} ) message(STATUS "| Writing ${VERSION_MOOSE} to ${VERSION_FILE}" ) @@ -265,11 +273,13 @@ add_subdirectory(synapse) add_subdirectory(intfire) ###################################### LINKING ################################# +# WARN: If library A depends on library B, then library A must appear before +# library B in the following list. list(APPEND MOOSE_LIBRARIES - moose_builtins - msg benchmarks shell + moose_builtins + msg randnum scheduling moose_mpi @@ -326,10 +336,8 @@ else(APPLE) endif(APPLE) add_dependencies(moose.bin libmoose) -target_link_libraries(moose.bin moose ${CMAKE_DL_LIBS}) -if( WITH_BOOST ) - target_link_libraries( moose.bin ${Boost_LIBRARIES} ) -endif( WITH_BOOST ) +target_link_libraries(moose.bin ${MOOSE_LIBRARIES} ${STATIC_LIBRARIES} ${CMAKE_DL_LIBS}) +target_link_libraries(moose.bin ${SYSTEM_SHARED_LIBS} ) ######################### BUILD PYMOOSE ######################################## @@ -361,49 +369,58 @@ endif() # Target for creating bdist. This is handy for creating bdist for packaging. # Save the binary distribution (tar.gz) to PYMOOSE_BDIST_DIR. find_package(PythonInterp REQUIRED) -if(NOT PYMOOSE_BDIST_DIR) - set(PYMOOSE_BDIST_DIR ${CMAKE_BINARY_DIR}/bdist) -endif( ) -# If no one hsa set the PYMOOSE bdist installation directory, use default. -if(NOT PYMOOSE_BDIST_INSTALL_DIR) - set(PYMOOSE_BDIST_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/pymoose_bdist_install) -endif() -file(MAKE_DIRECTORY ${PYMOOSE_BDIST_INSTALL_DIR}) - -# We need a custom name for bdist; same on all platform. -set(_platform "CMAKE" ) -set(PYMOOSE_BDIST_FILE ${PYMOOSE_BDIST_DIR}/pymoose-${VERSION_MOOSE}.${_platform}.tar.gz) -message(STATUS "binary distribution file ${PYMOOSE_BDIST_FILE}") -add_custom_target(bdist ALL DEPENDS ${PYMOOSE_BDIST_FILE} ) - -# Any command using setup.cmake.py must run in the same directory. Use option -# `--relative` to prefix is aways fixed. -add_custom_command( OUTPUT ${PYMOOSE_BDIST_FILE} - COMMAND ${PYTHON_EXECUTABLE} setup.cmake.py bdist_dumb -p ${_platform} +if(UNIX) + + if(NOT PYMOOSE_BDIST_DIR) + set(PYMOOSE_BDIST_DIR ${CMAKE_CURRENT_BINARY_DIR}/bdist) + endif( ) + + # If no one hsa set the PYMOOSE bdist installation directory, use default. + if(NOT PYMOOSE_BDIST_INSTALL_DIR) + set(PYMOOSE_BDIST_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/pymoose_bdist_install) + endif() + file(MAKE_DIRECTORY ${PYMOOSE_BDIST_INSTALL_DIR}) + + # We need a custom name for bdist; same on all platform. + set(_platform "CMAKE" ) + set(PYMOOSE_BDIST_FILE ${PYMOOSE_BDIST_DIR}/pymoose-${VERSION_MOOSE}.${_platform}.tar.gz) + message(STATUS "binary distribution file ${PYMOOSE_BDIST_FILE}") + add_custom_target(bdist ALL DEPENDS ${PYMOOSE_BDIST_FILE} ) + + # Any command using setup.py must run in the same directory. Use option + # `--relative` to prefix is aways fixed. + add_custom_command( OUTPUT ${PYMOOSE_BDIST_FILE} + COMMAND ${PYTHON_EXECUTABLE} setup.py bdist_dumb -p ${_platform} -d ${PYMOOSE_BDIST_DIR} --relative - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python - COMMENT "bdist is saved to ${PYMOOSE_BDIST_DIR}" - VERBATIM - ) -add_custom_command(TARGET bdist POST_BUILD - COMMAND ${CMAKE_COMMAND} -E chdir ${PYMOOSE_BDIST_INSTALL_DIR} tar xf ${PYMOOSE_BDIST_FILE} - COMMENT "Unarchiving bdist file ${PYMOOSE_BDIST_FILE} to ${PYMOOSE_BDIST_INSTALL_DIR}" - VERBATIM - ) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python + COMMENT "bdist is saved to ${PYMOOSE_BDIST_DIR}" + VERBATIM + ) -# Copy python files from source to current binary directory. Make sure to call -# this before building bdist. -file(GLOB_RECURSE PYTHON_SRCS "${CMAKE_SOURCE_DIR}/python/*") -add_custom_target(copy_pymoose DEPENDS ${PYTHON_SRCS} - COMMAND ${CMAKE_COMMAND} -E copy_directory + add_custom_command(TARGET bdist POST_BUILD + COMMAND ${CMAKE_COMMAND} -E chdir ${PYMOOSE_BDIST_INSTALL_DIR} tar xf ${PYMOOSE_BDIST_FILE} + COMMENT "Unarchiving bdist file ${PYMOOSE_BDIST_FILE} to ${PYMOOSE_BDIST_INSTALL_DIR}" + VERBATIM + ) + + # Copy python files from source to current binary directory. Make sure to call + # this before building bdist. + file(GLOB_RECURSE PYTHON_SRCS "${CMAKE_SOURCE_DIR}/python/*") + add_custom_target(copy_pymoose DEPENDS ${PYTHON_SRCS} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/python - COMMENT "Copying required python files and other files to build directory" - VERBATIM - ) + COMMENT "Copying required python files and other files to build directory" + VERBATIM + ) -add_dependencies( bdist copy_pymoose ) -add_dependencies( copy_pymoose _moose ) + add_dependencies( bdist copy_pymoose ) + add_dependencies( copy_pymoose moose ) + +else(MSYS) + message(STATUS "Warn: I won't generate install target under MSYS but I'll create a wheel") + # see the pymoose/CMakeLists.txt file. +endif() ######################### INSTALL ############################################## @@ -421,7 +438,7 @@ if(${CMAKE_BUILD_TOOL} MATCHES "make") message( "=======================================\n" "If cmake did not report any error, run \n" - " 'make' to build MOOSE \n" + " 'make' to build MOOSE \n" "=======================================\n" ) endif() diff --git a/biophysics/DifShell.cpp b/biophysics/DifShell.cpp index c24c965886..0783c2e836 100644 --- a/biophysics/DifShell.cpp +++ b/biophysics/DifShell.cpp @@ -9,6 +9,7 @@ **********************************************************************/ #include "../basecode/header.h" +#include "../utility/numutil.h" #include "DifShellBase.h" #include "DifShell.h" diff --git a/biophysics/ReadCell.cpp b/biophysics/ReadCell.cpp index 50f2ef08b4..d900e5a344 100644 --- a/biophysics/ReadCell.cpp +++ b/biophysics/ReadCell.cpp @@ -11,6 +11,7 @@ #include "../shell/Shell.h" #include "ReadCell.h" #include "../utility/utility.h" +#include "../utility/numutil.h" #include "CompartmentBase.h" #include "Compartment.h" #include "SymCompartment.h" diff --git a/builtins/CMakeLists.txt b/builtins/CMakeLists.txt index 9b4e63994f..752f2c34c1 100644 --- a/builtins/CMakeLists.txt +++ b/builtins/CMakeLists.txt @@ -65,10 +65,14 @@ set(SRCS Stats.cpp Interpol2D.cpp SpikeStats.cpp - SocketStreamer.cpp testBuiltins.cpp ) +if(UNIX) + list(APPEND SRCS SocketStreamer.cpp) +endif() + + if(WITH_NSDF AND HDF5_FOUND) list(APPEND SRCS HDF5WriterBase.cpp diff --git a/builtins/SocketStreamer.cpp b/builtins/SocketStreamer.cpp index 38cbae5e09..d82493c17a 100644 --- a/builtins/SocketStreamer.cpp +++ b/builtins/SocketStreamer.cpp @@ -20,7 +20,10 @@ #include "../scheduling/Clock.h" #include "../utility/utility.h" #include "../shell/Shell.h" + +#ifdef POSIX_PLATFORM #include "SocketStreamer.h" +#endif const Cinfo* SocketStreamer::initCinfo() { diff --git a/builtins/SocketStreamer.h b/builtins/SocketStreamer.h index 7e811c817b..7a5d043506 100644 --- a/builtins/SocketStreamer.h +++ b/builtins/SocketStreamer.h @@ -31,6 +31,9 @@ #define _XOPEN_SOURCE_EXTENDED 1 // cmake should set include path. +#ifdef WINDOWS +#include +#else #include #include #include @@ -38,6 +41,7 @@ #include #include #include +#endif // MSG_MORE is not defined in OSX. So stupid! #ifndef MSG_MORE diff --git a/pymoose/CMakeLists.txt b/pymoose/CMakeLists.txt index 22f4613e8e..8b96ed0291 100644 --- a/pymoose/CMakeLists.txt +++ b/pymoose/CMakeLists.txt @@ -8,7 +8,7 @@ find_package(PythonInterp REQUIRED) # Find Numpy find_package(NumPy REQUIRED) include_directories(${NUMPY_INCLUDE_DIRS}) -add_definitions( -std=c++11 ) +add_definitions(-std=c++11) add_definitions(-DUSE_NUMPY) add_definitions(-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) @@ -39,7 +39,7 @@ set(PYMOOSE_SRCS # Build python module in source directory and them copy everything to # current binary directory using cmake. -add_library( _moose MODULE ${PYMOOSE_SRCS} ) +add_library(moose MODULE ${PYMOOSE_SRCS} ) set(PYMOOSE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../python/moose") message(STATUS "Python module will be saved to ${PYMOOSE_OUTPUT_DIRECTORY}" ) @@ -60,7 +60,7 @@ else() OUTPUT_STRIP_TRAILING_WHITESPACE ) message( STATUS "Python include flags: ${PYTHON_INCLUDE_FLAGS}" ) - set_target_properties(_moose PROPERTIES + set_target_properties(moose PROPERTIES COMPILE_DEFINITIONS "PYMOOSE" COMPILE_FLAGS "${PYTHON_INCLUDE_FLAGS}" ) @@ -69,9 +69,9 @@ endif() # Remove prefix lib from python module. if(NOT(PYTHON_SO_EXTENSION STREQUAL "")) - set_target_properties(_moose PROPERTIES SUFFIX ${PYTHON_SO_EXTENSION}) + set_target_properties(moose PROPERTIES SUFFIX ${PYTHON_SO_EXTENSION}) endif() -set_target_properties(_moose PROPERTIES +set_target_properties(moose PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PYMOOSE_OUTPUT_DIRECTORY} PREFIX "" SUFFIX ${PYTHON_SO_EXTENSION} @@ -79,7 +79,7 @@ set_target_properties(_moose PROPERTIES # see issue #80 if(HDF5_FOUND AND WITH_NSDF) - set_target_properties( _moose PROPERTIES LINK_FLAGS "-L${HDF5_LIBRARY_DIRS}" ) + set_target_properties(moose PROPERTIES LINK_FLAGS "-L${HDF5_LIBRARY_DIRS}" ) endif() if(APPLE) @@ -89,20 +89,20 @@ endif(APPLE) # cmake --help-policy CMP0042 if(APPLE) - set_target_properties( _moose PROPERTIES MACOSX_RPATH OFF) + set_target_properties(moose PROPERTIES MACOSX_RPATH OFF) endif(APPLE) if(APPLE) - target_link_libraries( _moose + target_link_libraries(moose "-Wl,-all_load" ${MOOSE_LIBRARIES} ${STATIC_LIBRARIES} ) - target_link_libraries(_moose + target_link_libraries(moose ${SYSTEM_SHARED_LIBS} ) else(APPLE) - target_link_libraries(_moose + target_link_libraries(moose "-Wl,--whole-archive" ${MOOSE_LIBRARIES} ${STATIC_LIBRARIES} @@ -110,12 +110,28 @@ else(APPLE) ${SYSTEM_SHARED_LIBS} ) endif(APPLE) -add_custom_command(TARGET _moose POST_BUILD - COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan + +if(MSYS) + message(STATUS "Building under MSYS") + find_package(PythonLibs REQUIRED) + target_link_libraries(moose ${PYTHON_LIBRARIES}) +endif() + +if(UNIX) + add_custom_command(TARGET moose POST_BUILD + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "MOOSE python extention is successfully built. Now " " Run 'sudo make install' to install it. " " " "NOTE: Run 'pip uninstall moose' to uninstall moose." - VERBATIM - ) - + VERBATIM + ) +elseif(MSYS) + add_custom_command(TARGET moose POST_BUILD + COMMAND ${PYTHON_EXECUTABLE} setup.py bdist_wheel + -p win_amd64 -d ${CMAKE_BINARY_DIR} -q + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/python + COMMENT "Generating wheel" + VERBATIM + ) +endif() diff --git a/pymoose/moosemodule.cpp b/pymoose/moosemodule.cpp index 96e37d66f9..514835f2fc 100644 --- a/pymoose/moosemodule.cpp +++ b/pymoose/moosemodule.cpp @@ -1738,12 +1738,15 @@ PyObject * moose_start(PyObject * dummy, PyObject * args ) return NULL; } +#ifdef UNIX // This is from http://stackoverflow.com/questions/1641182/how-can-i-catch-a-ctrl-c-event-c struct sigaction sigHandler; sigHandler.sa_handler = handle_keyboard_interrupts; sigemptyset(&sigHandler.sa_mask); sigHandler.sa_flags = 0; sigaction(SIGINT, &sigHandler, NULL); +#endif + SHELLPTR->doStart( runtime, notify ); Py_RETURN_NONE; } @@ -3121,7 +3124,7 @@ static struct PyModuleDef MooseModuleDef = #define MODINIT(name) init##name() #endif -PyMODINIT_FUNC MODINIT(_moose) +PyMODINIT_FUNC MODINIT(moose) { clock_t modinit_start = clock(); diff --git a/python/moose/__init__.py b/python/moose/__init__.py index 3c39803398..086da81ea3 100644 --- a/python/moose/__init__.py +++ b/python/moose/__init__.py @@ -1,18 +1,684 @@ # -*- coding: utf-8 -*- +# Author: Subhasis Ray +# Maintainer: Dilawar Singh, Harsha Rani, Upi Bhalla from __future__ import absolute_import, division, print_function +import warnings +import os +import pydoc +from io import StringIO +from contextlib import closing + +# Import function from C++ module into moose namespace. +import moose.moose as cppmoose + +import moose.utils as mu + # Use this format in all logger inside logger. Define it before any moose # related module is imported. LOGGING_FORMAT = '%(asctime)s %(message)s' # Bring everything from c++ module to global namespace. Not everything is # imported by the pervios import statement. -from moose._moose import * +from moose.moose import * # Bring everything from moose.py to global namespace. # IMP: It will overwrite any c++ function with the same name. -from moose.moose import * from moose.server import * # create a shorthand for version() call here. -__version__ = version() +__version__ = cppmoose.VERSION + +def version(): + return __version__ + + +# sbml import. +sbmlImport_, sbmlError_ = True, '' +try: + import moose.SBML.readSBML as _readSBML + import moose.SBML.writeSBML as _writeSBML +except Exception as e: + sbmlImport_ = False + sbmlError_ = '%s' % e + +# NeuroML2 import. +nml2Import_, nml2ImportError_ = True, '' +try: + import moose.neuroml2 as _neuroml2 +except Exception as e: + nml2Import_ = False + nml2ImportError_ = ' '.join( [ + "NML2 support is disabled because `libneuroml` and " + , "`pyneuroml` modules are not found.\n" + , " $ pip install pyneuroml libneuroml \n" + , " should fix it." + , " Actual error: %s " % e ] + ) + +chemImport_, chemError_ = True, '' +try: + import moose.chemUtil as _chemUtil +except Exception as e: + chemImport_ = False + chemError_ = '%s' % e + +kkitImport_, kkitImport_error_ = True, '' +try: + import moose.genesis.writeKkit as _writeKkit +except ImportError as e: + kkitImport_ = False + kkitImport_err_ = '%s' % e + +mergechemImport_, mergechemError_ = True, '' +try: + import moose.chemMerge as _chemMerge +except Exception as e: + mergechemImport_ = False + mergechemError_ = '%s' % e + +def loadModel(filename, modelpath, solverclass="gsl"): + """loadModel: Load model from a file to a specified path. + + Parameters + ---------- + filename: str + model description file. + modelpath: str + moose path for the top level element of the model to be created. + method: str + solver type to be used for simulating the model. + TODO: Link to detailed description of solvers? + + Returns + ------- + object + moose.element if succcessful else None. + """ + + if not os.path.isfile( os.path.realpath(filename) ): + mu.warn( "Model file '%s' does not exists or is not readable." % filename ) + return None + + extension = os.path.splitext(filename)[1] + if extension in [".swc", ".p"]: + return cppmoose.loadModelInternal(filename, modelpath, "Neutral" ) + + if extension in [".g", ".cspace"]: + # only if genesis or cspace file and method != ee then only + # mooseAddChemSolver is called. + ret = cppmoose.loadModelInternal(filename, modelpath, "ee") + sc = solverclass.lower() + if sc in ["gssa","gillespie","stochastic","gsolve"]: + method = "gssa" + elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: + method = "gsl" + elif sc in ["exponential euler","exponentialeuler","neutral"]: + method = "ee" + else: + method = "ee" + + if method != 'ee': + chemError = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, method) + return ret + else: + mu.error( "Unknown model extenstion '%s'" % extension) + return None + + +# Tests +from moose.moose_test import test + +sequence_types = ['vector', + 'vector', + 'vector', + 'vector', + 'vector', + 'vector', + 'vector', + 'vector', + 'vector'] + +known_types = ['void', + 'char', + 'short', + 'int', + 'unsigned int', + 'double', + 'float', + 'long', + 'unsigned long', + 'string', + 'vec', + 'melement'] + sequence_types + +# SBML related functions. +def mooseReadSBML(filepath, loadpath, solver='ee',validate="on"): + """Load SBML model. + + Parameter + -------- + filepath: str + filepath to be loaded. + loadpath : str + Root path for this model e.g. /model/mymodel + solver : str + Solver to use (default 'ee'). + Available options are "ee", "gsl", "stochastic", "gillespie" + "rk", "deterministic" + For full list see ?? + """ + global sbmlImport_ + if sbmlImport_: + modelpath = _readSBML.mooseReadSBML(filepath, loadpath, solver, validate) + sc = solver.lower() + if sc in ["gssa","gillespie","stochastic","gsolve"]: + method = "gssa" + elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: + method = "gsl" + elif sc in ["exponential euler","exponentialeuler","neutral"]: + method = "ee" + else: + method = "ee" + + if method != 'ee': + chemError = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath[0].path, method) + + return modelpath + else: + print( sbmlError_ ) + return False + +def mooseWriteSBML(modelpath, filepath, sceneitems={}): + """mooseWriteSBML: Writes loaded model under modelpath to a file in SBML format. + + Parameters + ---------- + modelpath : str + model path in moose e.g /model/mymodel \n + filepath : str + Path of output file. \n + sceneitems : dict + UserWarning: user need not worry about this layout position is saved in + Annotation field of all the moose Object (pool,Reaction,enzyme). + If this function is called from + * GUI - the layout position of moose object is passed + * command line - NA + * if genesis/kkit model is loaded then layout position is taken from the file + * otherwise auto-coordinates is used for layout position. + """ + if sbmlImport_: + return _writeSBML.mooseWriteSBML(modelpath, filepath, sceneitems) + else: + print( sbmlError_ ) + return False + + +def mooseWriteKkit(modelpath, filepath, sceneitems={}): + """Writes loded model under modelpath to a file in Kkit format. + + Parameters + ---------- + modelpath : str + Model path in moose. + filepath : str + Path of output file. + """ + global kkitImport_, kkitImport_err_ + if not kkitImport_: + print( '[WARN] Could not import module to enable this function' ) + print( '\tError was %s' % kkitImport_error_ ) + return False + return _writeKkit.mooseWriteKkit(modelpath, filepath,sceneitems) + + +def mooseDeleteChemSolver(modelpath): + """mooseDeleteChemSolver + deletes solver on all the compartment and its children. + + Notes + ----- + This is neccesary while created a new moose object on a pre-existing modelpath, + this should be followed by mooseAddChemSolver for add solvers on to compartment + to simulate else default is Exponential Euler (ee) + """ + if chemImport_: + return _chemUtil.add_Delete_ChemicalSolver.mooseDeleteChemSolver(modelpath) + else: + print( chemError_ ) + return False + + +def mooseAddChemSolver(modelpath, solver): + """mooseAddChemSolver: + Add solver on chemical compartment and its children for calculation + + Parameters + ---------- + + modelpath : str + Model path that is loaded into moose. + solver : str + Exponential Euler "ee" is default. Other options are Gillespie ("gssa"), + Runge Kutta ("gsl") etc. Link to documentation? + """ + if chemImport_: + chemError_ = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, solver) + return chemError_ + else: + print( chemError_ ) + return False + +def mergeChemModel(src, des): + """mergeChemModel: Merges two chemical model. + File or filepath can be passed source is merged to destination + """ + #global mergechemImport_ + if mergechemImport_: + return _chemMerge.merge.mergeChemModel(src,des) + else: + return False + +# NML2 reader and writer function. +def mooseReadNML2( modelpath, verbose = False ): + """Read NeuroML model (version 2) and return reader object. + """ + global nml2Import_ + if not nml2Import_: + mu.warn( nml2ImportError_ ) + raise RuntimeError( "Could not load NML2 support." ) + + reader = _neuroml2.NML2Reader( verbose = verbose ) + reader.read( modelpath ) + return reader + +def mooseWriteNML2( outfile ): + raise NotImplementedError( "Writing to NML2 is not supported yet" ) + +################################################################ +# Wrappers for global functions +################################################################ +def pwe(): + """Print present working element. Convenience function for GENESIS + users. If you want to retrieve the element in stead of printing + the path, use moose.getCwe() + + """ + pwe_ = cppmoose.getCwe() + print(pwe_.getPath()) + return pwe_ + + +def le(el=None): + """List elements under `el` or current element if no argument + specified. + + Parameters + ---------- + el : str/melement/vec/None + The element or the path under which to look. If `None`, children + of current working element are displayed. + + Returns + ------- + List of path of child elements + + """ + if el is None: + el = cppmoose.getCwe() + elif isinstance(el, str): + if not cppmoose.exists(el): + raise ValueError('no such element') + el = cppmoose.element(el) + elif isinstance(el, cppmoose.vec): + el = el[0] + print('Elements under', el.path) + for ch in el.children: + print(ch.path) + return [child.path for child in el.children] + +ce = cppmoose.setCwe # ce is a GENESIS shorthand for change element. + +def syncDataHandler(target): + """Synchronize data handlers for target. + + Parameters + ---------- + target : melement/vec/str + Target element or vec or path string. + + Raises + ------ + NotImplementedError + The call to the underlying C++ function does not work. + + Notes + ----- + This function is defined for completeness, but currently it does not work. + + """ + raise NotImplementedError('The implementation is not working for IntFire - goes to invalid objects. \ +First fix that issue with SynBase or something in that line.') + if isinstance(target, str): + if not cppmoose.exists(target): + raise ValueError('%s: element does not exist.' % (target)) + target = cppmoose.vec(target) + cppmoose.syncDataHandler(target) + + +def showfield(el, field='*', showtype=False): + """Show the fields of the element `el`, their data types and + values in human readable format. Convenience function for GENESIS + users. + + Parameters + ---------- + el : melement/str + Element or path of an existing element. + + field : str + Field to be displayed. If '*' (default), all fields are displayed. + + showtype : bool + If True show the data type of each field. False by default. + + Returns + ------- + None + + """ + if isinstance(el, str): + if not cppmoose.exists(el): + raise ValueError('no such element') + el = cppmoose.element(el) + if field == '*': + value_field_dict = cppmoose.getFieldDict(el.className, 'valueFinfo') + max_type_len = max(len(dtype) for dtype in value_field_dict.values()) + max_field_len = max(len(dtype) for dtype in value_field_dict.keys()) + print('\n[', el.path, ']') + for key, dtype in sorted(value_field_dict.items()): + if dtype == 'bad' or key == 'this' or key == 'dummy' or key == 'me' or dtype.startswith( + 'vector') or 'ObjId' in dtype: + continue + value = el.getField(key) + if showtype: + typestr = dtype.ljust(max_type_len + 4) + # The following hack is for handling both Python 2 and + # 3. Directly putting the print command in the if/else + # clause causes syntax error in both systems. + print(typestr, end=' ') + print(key.ljust(max_field_len + 4), '=', value) + else: + try: + print(field, '=', el.getField(field)) + except AttributeError: + pass # Genesis silently ignores non existent fields + + +def showfields(el, showtype=False): + """Convenience function. Should be deprecated if nobody uses it. + """ + warnings.warn( + 'Deprecated. Use showfield(element, field="*", showtype=True) instead.', + DeprecationWarning) + showfield(el, field='*', showtype=showtype) + +# Predefined field types and their human readable names +finfotypes = [('valueFinfo', 'value field'), + ('srcFinfo', 'source message field'), + ('destFinfo', 'destination message field'), + ('sharedFinfo', 'shared message field'), + ('lookupFinfo', 'lookup field')] + +def listmsg(el): + """Return a list containing the incoming and outgoing messages of + `el`. + + Parameters + ---------- + el : melement/vec/str + MOOSE object or path of the object to look into. + + Returns + ------- + msg : list + List of Msg objects corresponding to incoming and outgoing + connections of `el`. + + """ + obj = cppmoose.element(el) + ret = [] + for msg in obj.inMsg: + ret.append(msg) + for msg in obj.outMsg: + ret.append(msg) + return ret + + +def showmsg(el): + """Print the incoming and outgoing messages of `el`. + + Parameters + ---------- + el : melement/vec/str + Object whose messages are to be displayed. + + Returns + ------- + None + + """ + obj = cppmoose.element(el) + print('INCOMING:') + for msg in obj.msgIn: + print( + msg.e2.path, + msg.destFieldsOnE2, + '<---', + msg.e1.path, + msg.srcFieldsOnE1) + print('OUTGOING:') + for msg in obj.msgOut: + print( + msg.e1.path, + msg.srcFieldsOnE1, + '--->', + msg.e2.path, + msg.destFieldsOnE2) + + +def getfielddoc(tokens, indent=''): + """Return the documentation for field specified by `tokens`. + + Parameters + ---------- + tokens : (className, fieldName) str + A sequence whose first element is a MOOSE class name and second + is the field name. + + indent : str + indentation (default: empty string) prepended to builtin + documentation string. + + Returns + ------- + docstring : str + string of the form + `{indent}{className}.{fieldName}: {datatype} - {finfoType}\n{Description}\n` + + Raises + ------ + NameError + If the specified fieldName is not present in the specified class. + """ + assert(len(tokens) > 1) + classname = tokens[0] + fieldname = tokens[1] + while True: + try: + classelement = cppmoose.element('/classes/' + classname) + for finfo in classelement.children: + for fieldelement in finfo: + baseinfo = '' + if classname != tokens[0]: + baseinfo = ' (inherited from {})'.format(classname) + if fieldelement.fieldName == fieldname: + # The field elements are + # /classes/{ParentClass}[0]/{fieldElementType}[N]. + finfotype = fieldelement.name + return '{indent}{classname}.{fieldname}: type={type}, finfotype={finfotype}{baseinfo}\n\t{docs}\n'.format( + indent=indent, classname=tokens[0], + fieldname=fieldname, + type=fieldelement.type, + finfotype=finfotype, + baseinfo=baseinfo, + docs=fieldelement.docs) + classname = classelement.baseClass + except ValueError: + raise NameError('`%s` has no field called `%s`' + % (tokens[0], tokens[1])) + + +def toUnicode(v, encoding='utf8'): + # if isinstance(v, str): + # return v + try: + return v.decode(encoding) + except (AttributeError, UnicodeEncodeError): + return str(v) + + +def getmoosedoc(tokens, inherited=False): + """Return MOOSE builtin documentation. + + Parameters + ---------- + tokens : (className, [fieldName]) + tuple containing one or two strings specifying class name + and field name (optional) to get documentation for. + + inherited: bool (default: False) + include inherited fields. + + Returns + ------- + docstring : str + Documentation string for class `className`.`fieldName` if both + are specified, for the class `className` if fieldName is not + specified. In the latter case, the fields and their data types + and finfo types are listed. + + Raises + ------ + NameError + If class or field does not exist. + + """ + indent = ' ' + docstring = StringIO() + with closing(docstring): + if not tokens: + return "" + try: + class_element = cppmoose.element('/classes/%s' % (tokens[0])) + except ValueError as e: + raise NameError('name \'%s\' not defined.' % (tokens[0])) + if len(tokens) > 1: + docstring.write(toUnicode(getfielddoc(tokens))) + else: + docstring.write(toUnicode('%s\n' % (class_element.docs))) + append_finfodocs(tokens[0], docstring, indent) + if inherited: + mro = eval('cppmoose.%s' % (tokens[0])).mro() + for class_ in mro[1:]: + if class_ == cppmoose.melement: + break + docstring.write(toUnicode( + '\n\n#Inherited from %s#\n' % (class_.__name__))) + append_finfodocs(class_.__name__, docstring, indent) + if class_ == cppmoose.Neutral: # Neutral is the toplevel moose class + break + return docstring.getvalue() + + +def append_finfodocs(classname, docstring, indent): + """Append list of finfos in class name to docstring""" + try: + class_element = cppmoose.element('/classes/%s' % (classname)) + except ValueError: + raise NameError('class \'%s\' not defined.' % (classname)) + for ftype, rname in finfotypes: + docstring.write(toUnicode('\n*%s*\n' % (rname.capitalize()))) + try: + finfo = cppmoose.element('%s/%s' % (class_element.path, ftype)) + for field in finfo.vec: + docstring.write(toUnicode( + '%s%s: %s\n' % (indent, field.fieldName, field.type))) + except ValueError: + docstring.write(toUnicode('%sNone\n' % (indent))) + + +# the global pager is set from pydoc even if the user asks for paged +# help once. this is to strike a balance between GENESIS user's +# expectation of control returning to command line after printing the +# help and python user's expectation of seeing the help via more/less. +pager = None + + +def doc(arg, inherited=True, paged=True): + """Display the documentation for class or field in a class. + + Parameters + ---------- + arg : str/class/melement/vec + A string specifying a moose class name and a field name + separated by a dot. e.g., 'Neutral.name'. Prepending `moose.` + is allowed. Thus moose.doc('moose.Neutral.name') is equivalent + to the above. + It can also be string specifying just a moose class name or a + moose class or a moose object (instance of melement or vec + or there subclasses). In that case, the builtin documentation + for the corresponding moose class is displayed. + + paged: bool + Whether to display the docs via builtin pager or print and + exit. If not specified, it defaults to False and + moose.doc(xyz) will print help on xyz and return control to + command line. + + Returns + ------- + None + + Raises + ------ + NameError + If class or field does not exist. + + """ + # There is no way to dynamically access the MOOSE docs using + # pydoc. (using properties requires copying all the docs strings + # from MOOSE increasing the loading time by ~3x). Hence we provide a + # separate function. + global pager + if paged and pager is None: + pager = pydoc.pager + tokens = [] + text = '' + if isinstance(arg, str): + tokens = arg.split('.') + if tokens[0] == 'moose': + tokens = tokens[1:] + elif isinstance(arg, type): + tokens = [arg.__name__] + elif isinstance(arg, cppmoose.melement) or isinstance(arg, cppmoose.vec): + text = '%s: %s\n\n' % (arg.path, arg.className) + tokens = [arg.className] + if tokens: + text += getmoosedoc(tokens, inherited=inherited) + else: + text += pydoc.getdoc(arg) + if pager: + pager(text) + else: + print(text) diff --git a/python/moose/alternate.py b/python/moose/alternate.py deleted file mode 100644 index a03f97a6bc..0000000000 --- a/python/moose/alternate.py +++ /dev/null @@ -1,1037 +0,0 @@ -# -*- coding: utf-8 -*- -# moose.py --- -# -# Filename: moose.py -# Description: THIS IS OUTDATED CODE AND KEPT FOR HISTORICAL REFERENCE -# moose.py in this directory is the replacement for this -# script. -# -# Author: Subhasis Ray -# Maintainer: -# Copyright (C) 2010 Subhasis Ray, all rights reserved. -# Created: Sat Mar 12 14:02:40 2011 (+0530) -# Version: -# Last-Updated: Mon Apr 9 03:32:28 2012 (+0530) -# By: Subhasis Ray -# Update #: 1672 -# URL: -# Keywords: -# Compatibility: -# -# - -# Commentary: -# -# THIS IS OUTDATED CODE AND KEPT FOR HISTORICAL REFERENCE -# -# - -# Change log: -# -# -# - -# Code: - -""" -MOOSE = Multiscale Object Oriented Simulation Environment. - -Classes: - -Id:pf[key] - -this is the unique identifier of a MOOSE object. Note that you -can create multiple references to the same MOOSE object in Python, but -as long as they have the same Id, they all point to the same entity in -MOOSE. - -Methods: - -getValue() -- unsigned integer representation of id - -getPath() -- string representing the path corresponding this id - -getShape -- tuple containing the dimensions of this id - -Id also implements part of the sequence protocol: - -len(id) -- the first dimension of id. - -id[n] -- the n-th ObjId in id. - -id[n1:n2] -- a tuple containing n1 to n2-th (exclusive) ObjId in id. - -objid in id -- True if objid is contained in id. - - - -ObjId: - -Unique identifier of an element in a MOOSE object. It has three components: - -Id id - the Id containing this element - -unsigned integer dataIndex - index of this element in the container - -unsigned integer fieldIndex - if this is a tertiary object, i.e. acts -as a field in another element (like synapse[0] in IntFire[1]), then -the index of this field in the containing element. - -Methods: - -getId -- Id object containing this ObjId. - -getDataIndex() -- unsigned integer representing the index of this -element in containing MOOSE object. - -getFieldIndex() -- unsigned integer representing the index of this -element as a field in the containing Element. - -getFieldType(field) -- human readable datatype information of field - -getField(field) -- get value of field - -setField(field, value) -- assign value to field - -getFieldNames(fieldType) -- tuple containing names of all the fields -of type fieldType. fieldType can be valueFinfo, lookupFinfo, srcFinfo, -destFinfo and sharedFinfo. If nothing is passed, a union of all of the -above is used and all the fields are returned. - -connect(srcField, destObj, destField, msgType) -- connect srcField of -this element to destField of destObj. - -getMsgSrc(fieldName) -- return a tuple containing the ObjIds of all -the elements from which a message is entering the field specified by -fieldName. - -getMsgDesr(fieldName) -- return a tuple containing list of ObjIds of -elements that recieve messages from the fieldName of this element. - - -NeutralArray: - -The base class. Each NeutralArray object has an unique Id (field _id) and -that is the only data directly visible under Python. All operation are -done on the objects by calling functions on the Id. - -A NeutralArray object is actually an array. The individual elements in a -NeutralArray are of class Neutral. To access these individual -elements, you can index the NeutralArray object. - -A NeutralArray object can be constructed in many ways. The most basic one -being: - -neutral = moose.NeutralArray('my_neutral_object', [3]) - -This will create a NeutralArray object with name 'my_neutral_object' -containing 3 elements. The object will be created as a child of the -current working entity. Any class derived from NeutralArray can also be -created using the same constructor. Actually it takes keyword -parameters to do that: - -intfire = moose.NeutralArray(path='/my_neutral_object', dims=[3], type='IntFire') - -will create an IntFire object of size 3 as a child of the root entity. - -If the above code is already executed, - -duplicate = moose.NeutralArray(intfire) - -will create a duplicate reference to the existing intfire object. They -will share the same Id and any changes made via the MOOSE API to one -will be effective on the other. - -Neutral -- The base class for all elements in object of class -NeutralArray or derivatives of NeutralArray. A Neutral will always point -to an index in an existing entity. The underlying data is ObjId (field -oid_) - a triplet of id, dataIndex and fieldIndex. Here id is the Id -of the NeutralArray object containing this element. dataIndex is the index -of this element in the container. FieldIndex is a tertiary index and -used only when this element acts as a field of another -element. Otherwise fieldIndex is 0. - -Indexing a NeutralArray object returns a Neutral. - -i_f = intfire[0] will return a reference to the first element in the -IntFire object we created earlier. All field-wise operations are done -on Neutrals. - -A neutral object (and its derivatives) can also be created in the -older way by specifying a path to the constructor. This path may -contain an index. If there is a pre-existing NeutralArray object with -the given path, then the index-th item of that array is returned. If -the target object does not exist, but all the objects above it exist, -then a new Array object is created and its first element is -returned. If an index > 0 is specified in this case, that results in -an IndexOutOfBounds exception. If any of the objects higher in the -hierarchy do not exist (thus the path up to the parent is invalid), a -NameError is raised. - -a = Neutral('a') # creates /a -b = IntFire(a/b') # Creates /a/b -c = IntFire(c/b') # Raises NameError. -d = NeutralArray('c', 10) -e = Neutral('c[9]') # Last element in d - -Fields: - -childList - a list containing the children of this object. - -className - class of the underlying MOOSE object. The corresponding -field in MOOSE is 'class', but in Python that is a keyword, so we use -className instead. This is same as Neutral.getField('class') - - -dataIndex - data index of this object. This should not be needed for -normal use. - -dimensions - a tuple representation dimensions of the object. If it is -empty, this is a singleton object. - -fieldIndex - fieldIndex for this object. Should not be needed for -ordinary use. - -fieldNames - list fields available in the underlying MOOSE object. - - - -Methods: - -children() - return the list of Ids of the children - -connect(srcField, destObj, destField) - a short hand and backward -compatibility function for moose.connect(). It creates a message -connecting the srcField on the calling object to destField on the dest -object. - -getField(fieldName) - return the value of the specified field. - -getFieldNames() - return a list of the available field names on this object - -getFieldType(fieldName) - return the data type of the specified field. - -getSources(fieldName) - return a list of (source_element, source_field) for all -messages coming in to fieldName of this object. - -getDestinations(fieldName) - return a list of (destination_elemnt, destination_field) -for all messages going out of fieldName. - - -More generally, Neutral and all its derivatives will have a bunch of methods that are -for calling functions via destFinfos. help() for these functions -should show something like: - - lambda self, arg_0_{type}, arg_1_{type} unbound moose.{ClassName} method - -These are dynamically defined methods, and calling them with the right -parameters will cause the corresponding moose function to be -called. Note that all parameters are converted to strings, so you may -loose some precision here. - -[Comment - subha: This explanation is no less convoluted than the -implementation itself. Hopefully I'll have the documentation -dynamically dragged out of Finfo documentation in future.] - -module functions: - -element(path) - returns a reference to an existing object converted to -the right class. Raises NameError if path does not exist. - -arrayelement(path) - returns a reference to an existing object -converted to the corresponding Array class. Raises NameError if path -does not exist. - -copy(src=, dest=, name=, n=, -copyMsg= of every object that -matches on clock no. . - -setClock(tick, dt) -- set dt of clock no . - -start(runtime) -- start simulation of time. - -reinit() -- reinitialize simulation. - -stop() -- stop simulation - -isRunning() -- true if simulation is in progress, false otherwise. - -exists(path) -- true if there is a pre-existing object with the specified path. - -loadModel(filepath, modelpath) -- load file in into node - of the moose model-tree. - -setCwe(obj) -- set the current working element to - which can be -either a string representing the path of the object in the moose -model-tree, or an Id. -cwe(obj) -- an alias for setCwe. - -getCwe() -- returns Id of the current working element. -pwe() -- an alias for getCwe. - -showfields(obj) -- print the fields in object in human readable format - -le(obj) -- list element under object, if no parameter specified, list -elements under current working element - -""" -from __future__ import print_function -from functools import partial -import warnings -from collections import defaultdict -from . import _moose -from ._moose import __version__, VERSION, SVN_REVISION, useClock, setClock, start, reinit, stop, isRunning, loadModel, getFieldDict, getField, Id, ObjId, exists, seed -from ._moose import wildcardFind as _wildcardFind # We override the original -import __main__ as main - -sequence_types = [ 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector' ] -known_types = ['void', - 'char', - 'short', - 'int', - 'unsigned int', - 'double', - 'float', - 'long', - 'unsigned long', - 'string', - 'Id', - 'ObjId'] + sequence_types - -# Dict of available MOOSE class names. This is faster for look-up -_moose_classes = dict([(child[0].name, True) for child in Id('/classes')[0].getField('children')]) - -# class _MooseDescriptor(object): -# """Descriptor to give access to MOOSE class' ValueFinfo attributes""" -# def __init__(self, name): -# self.name = name - -# def __get__(self, obj, objtype=None): -# return obj.oid_.getField(self.name) - -# def __set__(self, obj, value): -# obj.oid_.setField(self.name, value) - -# def __delete__(self, obj): -# raise AttributeError('ValueFinfos cannot be deleted.') - -class _LFDescriptor(object): - def __init__(self, name): - self.name = name - def __get__(self, obj, objtype=None): - return _LookupField(obj.oid_, self.name) - -class _LookupField(object): - def __init__(self, obj, name): - self.name = name - self.obj = obj - def __getitem__(self, key): - if isinstance(self.obj, ObjId): - return self.obj.getLookupField(self.name, key) - elif isinstance(self.obj, Neutral): - return self.obj.oid_.getLookupField(self.name, key) - else: - raise TypeError('obj is neither an ObjId nor a Neutral or subclass instance.') - def __setitem__(self, key, value): - if isinstance(self.obj, ObjId): - self.obj.setLookupField(self.name, key, value) - elif isinstance(self.obj, Neutral): - self.obj.oid_.setLookupField(self.name, key, value) - else: - raise TypeError('obj is neither an ObjId nor a Neutral or subclass instance.') - -class NeutralArray(object): - """ - The base class. Each NeutralArray object has an unique Id (field - id_) and that is the only data directly visible under Python. All - operation are done on the objects by calling functions on the Id. - - A NeutralArray object is actually an array. The individual - elements in a NeutralArray are of class Neutral. To access these - individual elements, you can index the NeutralArray object. - - """ - def __init__(self, *args, **kwargs): - """ - A NeutralArray object can be constructed in many ways. The - most basic one being: - - neutral = moose.NeutralArray('my_neutral_object', [3]) - - This will create a NeutralArray object with name - 'my_neutral_object' containing 3 elements. The object will be - created as a child of the current working entity. Any class - derived from NeutralArray can also be created using the same - constructor. Actually it takes keyword parameters to do that: - - intfire = moose.NeutralArray(path='/my_neutral_object', dims=[3], type='IntFire') - - will create an IntFire object of size 3 as a child of the root entity. - - If the above code is already executed, - - duplicate = moose.NeutralArray(intfire) - - will create a duplicate reference to the existing intfire - object. They will share the same Id and any changes made via - the MOOSE API to one will be effective on the other. - - """ - path = None - dims = None - self.id_ = None - try: - className = kwargs['type'] - self.className = className - except KeyError: - # This code is messy and depends on the class name. I - # could not find a way to pass the element data type to - # the class definition dynamically - if not hasattr(self, 'className'): - self.className = 'Neutral' - try: - dims = kwargs['dims'] - except KeyError: - pass - try: - path = kwargs['path'] - except KeyError: - pass - if len(args) > 0: - if isinstance(args[0], str): - path = args[0] - elif isinstance(args[0], Id): - self.id_ = args[0] - elif isinstance(args[0], int): - self.id_ = Id(args[0]) - if len(args) > 1: - dims = args[1] - if len(args) > 2: - self.className = args[2] - # No existing Array element ot Id specified, create new - # ArrayElement - if self.id_ is None: - if path is None: - raise TypeError('A string path or an existing Id or an int value for existing Id must be the first argument to __init__') - if exists(path): - self.id_ = _moose.Id(path=path) - # Check if specified dimensions match the existing - # object's dimensions - if dims is not None: - shape = self.id_.getShape() - if isinstance(dims, int): - if shape[0] != dims: - raise ValueError('Specified dimensions do not match that of existing array object') - else: - if len(shape) != len(dims): - raise ValueError('Specified dimensions do not match that of existing array object') - for ii in range(len(shape)): - if shape[ii] != dims[ii]: - raise ValueError('Specified dimensions do not match that of existing array object') - else: - dims = (1) - # Create a new ArrayElement - _base_class = self.__class__ - _class_name = self.__class__.__name__ - if _class_name.endswith('Array'): - _class_name = _class_name[:-len('Array')] - # For classes extended in Python get to the first MOOSE base class - while _class_name not in _moose_classes: - _base_class = self.__base__ - if _base_class == object: - raise TypeError('Class %s does not inherit any MOOSE class.' % (self.__class__.__name__)) - _class_name = _base_class.__name__ - if _class_name.endswith('Array'): - _class_name = _class_name[:-len('Array')] - self.id_ = _moose.Id(path=path, dims=dims, type=_class_name) - # Check for conversion from instance of incompatible MOOSE - # class. - orig_classname = self.id_[0].getField('class') + 'Array' - if self.__class__.__name__ != orig_classname: - orig_class = eval(orig_classname) - self_class = self.__class__ - while self_class != object and self_class not in orig_class.mro(): - self_class = self_class.__base__ - if self_class == object: - self.id_ = None - raise TypeError('Cannot convert %s to %s' % (orig_class, self.__class__)) - - def getFieldNames(self, ftype=''): - """Return a list of fields available in this object. - - Parameters: - - str ftype -- (default '') category of field, valid values are: - valueFinfo, lookupFinfo, srcFinfo, destFinfo or sharedFinfo. - - If empty string or not specified, returns names of fields from - all categories. - """ - - return self.id_[0].getFieldNames(ftype) - - def getFieldType(self, field, ftype=''): - """Return the data type of the field as a string.""" - return self.id_[0].getFieldType(field, ftype) - - def __getitem__(self, index): - objid = self.id_[index] - retclass = eval(self.id_[0].getField('class')) - ret = retclass(objid) - return ret - - def __len__(self): - return len(self.id_) - - def __contains__(self, other): - if isinstance(other, Neutral): - return other.oid_ in self.id_ - elif isinstance(other, ObjId): - return other in self.id_ - else: - return False - - def __repr__(self): - return self.id_.getPath() - - path = property(lambda self: self.id_.getPath()) - fieldNames = property(lambda self: self.id_[0].getFieldNames('valueFinfo')) - name = property(lambda self: self.id_[0].name) - shape = property(lambda self: self.id_.getShape()) - - - -class Neutral(object): - """Corresponds to a single entry in a NeutralArray. Base class for - all other MOOSE classes for single entries in Array elements. - - A Neutral object wraps an ObjId (field oid_) - a triplet of id, - dataIndex and fieldIndex. Here id is the Id of the NeutralArray object - containing this element. dataIndex is the index of this element in the - container. FieldIndex is a tertiary index and used only when this - element acts as a field of another element. Otherwise fieldIndex is 0. - - Indexing a NeutralArray object returns a Neutral. - - A neutral object (and its derivatives) can also be created in the - older way by specifying a path to the constructor. This path may - contain an index. If there is a pre-existing NeutralArray object - with the given path, then the index-th item of that array is - returned. If the target object does not exist, but all the objects - above it exist, then a new Array object is created and its first - element is returned. If an index > 0 is specified in this case, - that results in an IndexOutOfBounds exception. If any of the - objects higher in the hierarchy do not exist (thus the path up to - the parent is invalid), a NameError is raised. - - a = Neutral('a') # creates /a - b = IntFire(a/b') # Creates /a/b - c = IntFire(c/b') # Raises NameError. - d = NeutralArray('c', 10) - e = Neutral('c[9]') # Last element in d - """ - def __init__(self, *args, **kwargs): - """Initialize a Neutral object. - - Arguments: - - arg1 : A path or an existing ObjId or an Id or a NeutralArray - or another Neutral object. - - path -- a string specifying the path for the Neutral - object. If there is already a Neutral object with the given - path, a reference to that object is created. Otherwise, a new - NeutralArray is created with the given path. In the latter - case, the path must be valid up to the last '/'. For example, - if you specify '/a/b/c', then for correct operation, there - must be an element named 'a' at the top level and a child - element of 'a' named 'b'. This works like 'mkdir' or 'md' - command in some operating systems. - - ObjId -- if the first argument is an ObjId, then the Neutral - object refers to the existing object with the specified ObjId. - - Id -- if the first argument is an Id then the Neutral object - will refer to some entry in the ArrayElement with this Id. The - exact index pointed to will be determined by the second - argument, or the first entry if no second argument is - specified. - - NeutralArray -- Same as Id (as if the Id of the NeutralArray - was passed). - - Neutral -- create a new reference to the existing Neutral - object. - - arg2 : if there is a second argument, then this is taken as - the dataindex into an existing array element. - - arg3: if there is a third argument, this is taken as the - fieldindex into an existing array field. - """ - id_ = None - self.oid_ = None - dindex = None - findex = None - numFieldBits = None - if len(args) >= 1: - if isinstance(args[0], ObjId): - self.oid_ = args[0] - elif isinstance(args[0], Id): - id_ = args[0].getValue() - elif isinstance(args[0], Neutral): - self.oid_ = args[0].oid_ - elif isinstance(args[0], NeutralArray): - id_ = args[0].id_ - elif isinstance(args[0], str): - try: - self.oid_ = _moose.ObjId(args[0]) - except ValueError: - # A non-existing path has been provided. Try to - # construct a singleton array element and create - # reference to the first element. - self_class = self.__class__ - while (self_class.__name__ not in _moose_classes) and (self_class != object): # Handle class extension in Python. - self_class = self_class.__base__ - if self_class == object: - raise TypeError('Class %s does not inherit any MOOSE class.' % (self.__class__.__name__)) - id_ = Id(path=args[0], dims=(1,), type=self_class.__name__) - else: - raise TypeError('First non-keyword argument must be a number or an existing Id/ObjId/Neutral/NeutralArray object or a path.') - if len(args) >= 2: - dindex = args[1] - if len(args) >= 3: - findex = args[2] - if len(args) >= 4: - numFieldBits = args[3] - if (kwargs): - try: - id_ = kwargs['id'] - except KeyError: - pass - try: - dindex = kwargs['dataIndex'] - except KeyError: - pass - try: - findex = kwargs['fieldIndex'] - except KeyError: - pass - try: - numFieldBits = kwargs['numFieldBits'] - except KeyError: - pass - if self.oid_ is None: - if id_ is not None: - if dindex is None: - self.oid_ = _moose.ObjId(id_) - elif findex is None: - self.oid_ = _moose.ObjId(id_, dindex) - elif numFieldBits is None: - self.oid_ = _moose.ObjId(id_, dindex, findex) - else: - self.oid_ = _moose.ObjId(id_, dindex, findex, numFieldBits) - # Check for conversion from instance of incompatible MOOSE - # class. - orig_classname = self.oid_.getField('class') - if self.__class__.__name__ != orig_classname: - orig_class = eval(orig_classname) - self_class = self.__class__ - while self_class != object and self_class not in orig_class.mro(): - self_class = self_class.__base__ - if self_class == object: - self.oid_ = None - raise TypeError('Cannot convert %s to %s' % (orig_class, self.__class__)) - - def getField(self, field): - """Return the field value""" - return self.oid_.getField(field) - - def getFieldType(self, field, ftype=''): - """Return the type of the specified field in human readable format""" - return self.oid_.getFieldType(field, ftype) - - def getFieldNames(self, ftype=''): - """Return a list of the fields of specified fieldType.""" - return self.oid_.getFieldNames(ftype) - - def getNeighbors(self, fieldName): - if fieldName in getFieldDict(self.className): - return [eval('%s("%s")' % (id_[0].getField('class'), id_.getPath())) for id_ in self.oid_.getNeighbors(fieldName)] - raise ValueError('%s: no such field on %s' % (fieldName, self.path)) - - def connect(self, srcField, dest, destField, msgType='Single'): - return self.oid_.connect(srcField, dest.oid_, destField, msgType) - - def getInMessageDict(self): - """Returns a dictionary mapping fields with incoming messages - to lists containing (source_element, source_field) - pairs.""" - msg_dict = defaultdict(list) - for msg in self.msgIn: - e1 = msg.getField('e1') - e2 = msg.getField('e2') - for (f1, f2) in zip(msg.getField('srcFieldsOnE2'), msg.getField('destFieldsOnE1')): - msg_dict[f1].append((element(e2), f2)) - return msg_dict - - def getOutMessageDict(self): - """Returns a dictionary mapping fields with outgoing messages - to lists containing (destination_element, destination_field) - pairs.""" - msg_dict = defaultdict(list) - for msg in self.msgOut: - e1 = msg.getField('e1') - e2 = msg.getField('e2') - for (f1, f2) in zip(msg.getField('srcFieldsOnE1'), msg.getField('destFieldsOnE2')): - msg_dict.append((element(e2), f2)) - return msg_dict - - def inMessages(self): - msg_list = [] - for msg in self.msgIn: - e1 = msg.getField('e1') - e2 = msg.getField('e2') - for (f1, f2) in zip(msg.getField('srcFieldsOnE2'), msg.getField('destFieldsOnE1')): - msg_str = '[%s].%s <- [%s].%s' % (e1.getPath(), f1, e2.getPath(), f2) - msg_list.append(msg_str) - return msg_list - - def outMessages(self): - msg_list = [] - for msg in self.msgOut: - e1 = msg.getField('e1') - e2 = msg.getField('e2') - for (f1, f2) in zip(msg.getField('srcFieldsOnE1'), msg.getField('destFieldsOnE2')): - msg_str = '[%s].%s -> [%s].%s' % (e1.getPath(), f1, e2.getPath(), f2) - msg_list.append(msg_str) - return msg_list - - - - className = property(lambda self: self.oid_.getField('class')) - fieldNames = property(lambda self: self.oid_.getFieldNames()) - name = property(lambda self: self.oid_.getField('name')) - path = property(lambda self: '%s[%d]' % (self.oid_.getField('path'), self.oid_.getDataIndex())) - id_ = property(lambda self: self.oid_.getId()) - fieldIndex = property(lambda self: self.oid_.getFieldIndex()) - dataIndex = property(lambda self: self.oid_.getDataIndex()) - # We have a lookup field called neighbors already, this should override that - @property - def neighborDict(self): - """defaultdict whose keys are field names and values are list - of objects that are connected to that field""" - neighbors = defaultdict(list) - for finfotype in ['srcFinfo', 'destFinfo', 'sharedFinfo']: - for field in self.oid_.getFieldNames(finfotype): - tmp = self.oid_.getNeighbors(field) - neighbors[field] += [eval('%s("%s")' % (nid[0].getField('class'), nid.getPath())) for nid in tmp] - return neighbors - - - childList = property(lambda self: [eval('%s("%s")' % (ch[0].getField('class'), ch.getPath())) for ch in self.oid_.getField('children')]) - -################################################################ -# Special function to generate objects of the right class from -# a given path. -################################################################ - -def element(path): - """Return a reference to an existing object as an instance of the - right class. If path does not exist, raises NameError. - - Id or ObjId can be provided in stead of path""" - if isinstance(path, Id): - oid = path[0] - path = path.getPath() - elif isinstance(path, ObjId): - oid = path - path = oid.getField('path') - elif isinstance(path, str): - if not _moose.exists(path): - raise NameError('Object %s not defined' % (path)) - oid = _moose.ObjId(path) - else: - raise TypeError('expected argument: Id/ObjId/str') - className = oid.getField('class') - return eval('%s("%s")' % (className, path)) - -def arrayelement(path, className='Neutral'): - """Return a reference to an existing object as an instance of the - right class. If path does not exist, className is used for - creating an instance of that class with the given path""" - if not _moose.exists(path): - raise NameError('Object %s not defined' % (path)) - oid = _moose.ObjId(path) - className = oid.getField('class') - return eval('%sArray("%s")' % (className, path)) - - -################################################################ -# Wrappers for global functions -################################################################ - -def copy(src, dest, name, n=1, toGlobal=False, copyExtMsg=False): - if isinstance(src, NeutralArray): - src = src.id_ - if isinstance(dest, NeutralArray): - dest = dest.id_ - new_id = _moose.copy(src, dest, name, n=n, toGlobal=toGlobal, copyExtMsg=copyExtMsg) - return new_id - -def move(src, dest): - if isinstance(src, NeutralArray): - src = src.id_ - if isinstance(dest, NeutralArray): - dest = dest.id_ - _moose.move(src, dest) - -def delete(target): - """Explicitly delete a MOOSE object. This will invalidate all - existing references. They will all point to the default root - object.""" - if isinstance(target, NeutralArray): - target = target.id_ - if not isinstance(target, Id): - raise TypeError('Only Id or Array objects can be deleted: received %s' % (target.__class__.__name__)) - _moose.delete(target) - -def setCwe(element): - """Set present working element""" - if isinstance(element, NeutralArray): - _moose.setCwe(element.id_) - elif isinstance(element, Neutral): - _moose.setCwe(element.oid_) - else: - _moose.setCwe(element) - -def getCwe(): - _id = _moose.getCwe() - obj = NeutralArray(_id) - return obj - -def pwe(): - """Print present working element. Convenience function for GENESIS - users.""" - print(_moose.getCwe().getPath()) - -def connect(src, srcMsg, dest, destMsg, msgType='Single'): - """Connect src object's source field specified by srcMsg to - destMsg field of target object.""" - if isinstance(src, Neutral): - src = src.oid_ - if isinstance(dest, Neutral): - dest = dest.oid_ - return src.connect(srcMsg, dest, destMsg, msgType) - -def le(element=None): - """List elements. """ - if element is None: - element = getCwe()[0] - elif isinstance(element, str): - element = Neutral(element) - print('Elements under', element.path) - for ch in element.children: - print(ch) - -ce = setCwe - -def syncDataHandler(target): - """Synchronize data handlers for target. - - Parameter: - target -- target element or path or Id. - """ - raise NotImplementedError('The implementation is not working for IntFire - goes to invalid objects. First fix that issue with SynBase or something in that line.') - if isinstance(target, str): - if not _moose.exists(target): - raise ValueError('%s: element does not exist.' % (target)) - target = Id(target) - _moose.syncDataHandler(target) - -def showfield(element, field='*', showtype=False): - """Show the fields of the element, their data types and values in - human readable format. Convenience function for GENESIS users. - - Parameters: - - element -- Element or path of an existing element or ObjId of an element. - - field -- Field to be displayed. If '*', all fields are displayed. - - showtype -- If True show the data type of each field. - - """ - if isinstance(element, str): - if not _moose.exists(element): - raise ValueError('%s -- no such moose object exists.' % (element)) - element = Neutral(element) - if not isinstance(element, Neutral): - if not isinstance(element, ObjId): - raise TypeError('Expected argument of type ObjId or Neutral or a path to an existing object. Found %s' % (type(element))) - element = Neutral(element) - if field == '*': - value_field_dict = getFieldDict(element.className, 'valueFinfo') - max_type_len = max(len(dtype) for dtype in value_field_dict.values()) - max_field_len = max(len(dtype) for dtype in value_field_dict.keys()) - print() - print('[', element.path, ']') - for key, dtype in list(value_field_dict.items()): - if dtype == 'bad' or key == 'this' or key == 'dummy' or key == 'me' or dtype.startswith('vector') or 'ObjId' in dtype: - continue - value = element.oid_.getField(key) - if showtype: - print(dtype.ljust(max_type_len + 4), end=' ') - print(key.ljust(max_field_len + 4), '=', value) - else: - try: - print(field, '=', element.getField(field)) - except AttributeError: - pass # Genesis silently ignores non existent fields - -def showfields(element, showtype=False): - """Convenience function. Should be deprecated if nobody uses it.""" - warnings.warn('Deprecated. Use showfield(element, field="*", showtype=True) instead.', DeprecationWarning) - showfield(element, field='*', showtype=showtype) - -def wildcardFind(cond): - """Search for objects that match condition cond.""" - return [eval('%s("%s")' % (id_[0].getField('class'), id_.getPath())) for id_ in _wildcardFind(cond)] - -####################################################### -# This is to generate class definitions automatically -####################################################### - -def update_class(cls, class_id): - class_name = class_id[0].name - num_valueFinfo = getField(class_id[0], 'num_valueFinfo', 'unsigned') - num_destFinfo = getField(class_id[0], 'num_destFinfo', 'unsigned') - num_lookupFinfo = getField(class_id[0], 'num_lookupFinfo', 'unsigned') - valueFinfo = Id('/classes/%s/valueFinfo' % (class_name)) - destFinfo = Id('/classes/%s/destFinfo' % (class_name)) - lookupFinfo = Id('/classes/%s/lookupFinfo' % (class_name)) - destFinfoNames = set([ObjId(destFinfo, 0, ii, 0).name for ii in range(num_destFinfo)]) - for ii in range(num_valueFinfo): - field = ObjId(valueFinfo, 0, ii, 0) - fieldName = field.name - fget = partial(ObjId.getField, fieldName) - fset = None - if 'get_%s' % (fieldName) in destFinfoNames: - fset = partial(ObjId.setField, fieldName) - doc = None - # The following is to avoid duplicating the documentation - # in non-interactive mode. __main__.__file__ is not - # defined in interactive mode and that is when we create - # the documentation. - if not hasattr(main, '__file__'): - doc = field.docs - setattr(cls, fieldName, property(fget=fget, fset=fset, doc=doc)) - - for ii in range(num_lookupFinfo): - field = ObjId(lookupFinfo, 0, ii, 0) - fieldName = field.name - fget = partial(_LookupField, fieldName) - # The following is to avoid duplicating the documentation - # in non-interactive mode. __main__.__file__ is not - # defined in interactive mode and that is when we create - # the documentation. - doc = None - if not hasattr(main, '__file__'): - doc = field.docs - setattr(cls, fieldName, property(fget=fget, doc=doc)) - - # Go through the destFinfos and make them look like methods - for ii in range(num_destFinfo): - field = ObjId(destFinfo, 0, ii, 0) - fieldName = field.name - # get_ and set_ are internal - # destFinfos generated for each valueFinfo (the latter - # created for writable ones). They are not to be accessed - # by users. - if fieldName.startswith('get_') or fieldName.startswith('set_'): - continue - # Can we handle the arguments required for this destFinfo? - # Start optimistically. - manageable = True - # We keep gathering the function signature in fnsig - # This will be a lambda function, lambda x, y, ...: expr - # where expr is evaluated and the result returned. - fnsig = 'lambda self' - # We gather the formal arguments in fnargs. field name - # must be the first argument passed to the - # ObjId.setField function. - fnargs = '"%s"' % (fieldName) - # 'type' is a string field in Finfos specifying the type - # (retrieved from the template parameters specified in the - # C++ definition). for DestFinfo with OpFuncN will have a type string: - # "type1,type2,...,typeN". We split this string to find - # out the formal arguments of the lambda - argtypes = field.type.split(',') - for index in range(len(argtypes)): - # Check if we know how to handle this argument type - if argtypes[index] not in known_types: - manageable = False - break - elif argtypes[index] != 'void': - # The replacements are for types with space (like - # unsigned int) and templated types (like - # vector) - arg = 'arg_%d_%s' % (index, argtypes[index].replace(' ', '_').replace('<', '_').replace('>', '_')) - fnsig += ', %s' % (arg) - fnargs += ', %s' % (arg) - if manageable: - # The final function will be: - # lambda self, arg_1_type1, arg_2_type2, ..., arg_N_typeN: - # self.oid_.setField(fieldName, arg_1_type1, arg_2_type2, ..., arg_N_typeN) - function_string = '%s: self.oid_.setDestField(%s)' % (fnsig, fnargs) - setattr(cls, fieldName, eval(function_string)) - -def define_class(class_id): - """Define a class based on Cinfo element with Id=class_id.""" - class_name = class_id[0].getField('name') - if class_name in globals(): - return - base = class_id[0].getField('baseClass') - if base != 'none': - try: - base_class = globals()[base] - except KeyError: - define_class(Id('/classes/'+base)) - base_class = globals()[base] - else: - base_class = object - class_obj = type(class_name, (base_class,), {}) - update_class(class_obj, class_id) - # Add this class to globals dict - globals()[class_name] = class_obj - array_class_name = class_name + 'Array' - if base != 'none': - base_class = globals()[base + 'Array'] - else: - base_class = object - array_class_obj = type(array_class_name, (base_class,), {}) - globals()[array_class_name] = array_class_obj - -classes_Id = Id('/classes') - -for child in classes_Id[0].children: - define_class(child) - - -# -# moose.py ends here diff --git a/python/moose/chemMerge/merge.py b/python/moose/chemMerge/merge.py index d3724c8cff..6b59b87eaa 100644 --- a/python/moose/chemMerge/merge.py +++ b/python/moose/chemMerge/merge.py @@ -257,6 +257,7 @@ def mergeChemModel(src,des): else: print ('\nSource file has no objects to copy(\'%s\')' %(modelA)) return moose.element('/') + def functionMerge(comptA,comptB,key): funcNotallowed, funcExist = [], [] comptApath = moose.element(comptA[key]).path diff --git a/python/moose/fixXreacs.py b/python/moose/fixXreacs.py index 7009390f3f..8f060a2fe1 100644 --- a/python/moose/fixXreacs.py +++ b/python/moose/fixXreacs.py @@ -14,15 +14,15 @@ #################################################################### import sys -import moose._moose as _moose +import moose.moose as cppmoose msgSeparator = "_xMsg_" def findCompt( elm ): - elm = _moose.element( elm ) + elm = cppmoose.element( elm ) pa = elm.parent while pa.path != '/': - if _moose.Neutral(pa).isA[ 'ChemCompt' ]: + if cppmoose.Neutral(pa).isA[ 'ChemCompt' ]: return pa.path pa = pa.parent print( 'Error: No compartment parent found for ' + elm.path ) @@ -33,7 +33,7 @@ def checkEqual(lst): return not lst or lst.count(lst[0]) == len(lst) def findXreacs( basepath, reacType ): - reacs = _moose.wildcardFind( basepath + '/##[ISA=' + reacType + 'Base]' ) + reacs = cppmoose.wildcardFind( basepath + '/##[ISA=' + reacType + 'Base]' ) ret = [] for i in reacs: reacc = findCompt( i ) @@ -50,38 +50,38 @@ def findXreacs( basepath, reacType ): return ret def removeEnzFromPool( pool ): - kids = _moose.wildcardFind( pool.path + "/#" ) + kids = cppmoose.wildcardFind( pool.path + "/#" ) for i in kids: if i.isA[ 'EnzBase' ]: - _moose.delete( i ) + cppmoose.delete( i ) elif i.isA[ 'Function' ]: - _moose.delete( i ) + cppmoose.delete( i ) # If a pool is not in the same compt as reac, make a proxy in the reac # compt, connect it up, and disconnect the one in the old compt. def proxify( reac, reacc, direction, pool, poolc ): - reacc_elm = _moose.element( reacc ) - reac_elm = _moose.element( reac ) + reacc_elm = cppmoose.element( reacc ) + reac_elm = cppmoose.element( reac ) # Preserve the rates which were set up for the x-compt reacn - #_moose.showfield( reac ) - dupname = pool.name + '_xfer_' + _moose.element(poolc).name + #cppmoose.showfield( reac ) + dupname = pool.name + '_xfer_' + cppmoose.element(poolc).name #print "#############", pool, dupname, poolc - if _moose.exists( reacc + '/' + dupname ): - duppool = _moose.element( reacc + '/' + dupname ) + if cppmoose.exists( reacc + '/' + dupname ): + duppool = cppmoose.element( reacc + '/' + dupname ) else: # This also deals with cases where the duppool is buffered. - duppool = _moose.copy(pool, reacc_elm, dupname ) + duppool = cppmoose.copy(pool, reacc_elm, dupname ) duppool.diffConst = 0 # diffusion only happens in original compt removeEnzFromPool( duppool ) disconnectReactant( reac, pool, duppool ) - _moose.connect( reac, direction, duppool, 'reac' ) - #_moose.showfield( reac ) - #_moose.showmsg( reac ) + cppmoose.connect( reac, direction, duppool, 'reac' ) + #cppmoose.showfield( reac ) + #cppmoose.showmsg( reac ) def enzProxify( enz, enzc, direction, pool, poolc ): if enzc == poolc: return - enze = _moose.element( enz ) + enze = cppmoose.element( enz ) # kcat and k2 are indept of volume, just time^-1 km = enze.numKm proxify( enz, enzc, direction, pool, poolc ) @@ -90,7 +90,7 @@ def enzProxify( enz, enzc, direction, pool, poolc ): def reacProxify( reac, reacc, direction, pool, poolc ): if reacc == poolc: return - reac_elm = _moose.element( reac ) + reac_elm = cppmoose.element( reac ) kf = reac_elm.numKf kb = reac_elm.numKb proxify( reac, reacc, direction, pool, poolc ) @@ -108,26 +108,26 @@ def identifyMsg( src, srcOut, dest ): def disconnectReactant( reacOrEnz, reactant, duppool ): outMsgs = reacOrEnz.msgOut infoPath = duppool.path + '/info' - if _moose.exists( infoPath ): - info = _moose.element( infoPath ) + if cppmoose.exists( infoPath ): + info = cppmoose.element( infoPath ) else: - info = _moose.Annotator( infoPath ) + info = cppmoose.Annotator( infoPath ) - #_moose.le( reactant ) + #cppmoose.le( reactant ) notes = "" - #_moose.showmsg( reacOrEnz ) + #cppmoose.showmsg( reacOrEnz ) for i in outMsgs: #print "killing msg from {} to {}\nfor {} and {}".format( reacOrEnz.path, reactant.path, i.srcFieldsOnE1[0], i.srcFieldsOnE2[0] ) if i.e1 == reactant: msgStr = identifyMsg( i.e2, i.e2.srcFieldsOnE2[0], i.e1 ) if len( msgStr ) > 0: notes += msgStr - _moose.delete( i ) + cppmoose.delete( i ) elif i.e2 == reactant: msgStr = identifyMsg( i.e1[0], i.srcFieldsOnE1[0], i.e2[0] ) if len( msgStr ) > 0: notes += msgStr - _moose.delete( i ) + cppmoose.delete( i ) #print "MSGS to rebuild:", notes info.notes += notes @@ -154,7 +154,7 @@ def fixXreacs( basepath ): def getOldRates( msgs ): if len( msgs ) > 1 : m1 = msgs[1].split( msgSeparator )[0] - elm = _moose.element( m1.split( ' ' )[0] ) + elm = cppmoose.element( m1.split( ' ' )[0] ) if elm.isA[ 'ReacBase' ]: return [elm.numKf, elm.numKb] elif elm.isA[ 'EnzBase' ]: @@ -166,7 +166,7 @@ def restoreOldRates( oldRates, msgs ): #print oldRates, msgs if len( msgs ) > 1 : m1 = msgs[1].split( msgSeparator )[0] - elm = _moose.element( m1.split( ' ' )[0] ) + elm = cppmoose.element( m1.split( ' ' )[0] ) if elm.isA[ 'ReacBase' ]: elm.numKf = oldRates[0] elm.numKb = oldRates[1] @@ -176,20 +176,20 @@ def restoreOldRates( oldRates, msgs ): def restoreXreacs( basepath ): - proxyInfo = _moose.wildcardFind( basepath + "/##/#_xfer_#/info" ) + proxyInfo = cppmoose.wildcardFind( basepath + "/##/#_xfer_#/info" ) for i in proxyInfo: msgs = i.notes.split( msgSeparator ) oldRates = getOldRates( msgs ) #print( "Deleting {}".format( i.parent.path ) ) #print msgs - _moose.delete( i.parent ) + cppmoose.delete( i.parent ) for j in msgs[1:]: if len( j ) > 0: args = j.split( ' ' ) assert( len( args ) == 4 ) - #_moose.showfield( args[0] ) - _moose.connect( args[0], args[1], args[2], args[3] ) + #cppmoose.showfield( args[0] ) + cppmoose.connect( args[0], args[1], args[2], args[3] ) #print( "Reconnecting {}".format( args ) ) - #_moose.showfield( args[0] ) + #cppmoose.showfield( args[0] ) restoreOldRates( oldRates, msgs ) diff --git a/python/moose/moose.py b/python/moose/moose.py deleted file mode 100644 index 191d26d4cb..0000000000 --- a/python/moose/moose.py +++ /dev/null @@ -1,674 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function, division, absolute_import - -# Author: Subhasis Ray -# Maintainer: Dilawar Singh, Harsha Rani, Upi Bhalla - -import warnings -import os -import pydoc -from io import StringIO -from contextlib import closing - -# Import function from C++ module into moose namespace. -import moose._moose as _moose -import moose.utils as mu - -# sbml import. -sbmlImport_, sbmlError_ = True, '' -try: - import moose.SBML.readSBML as _readSBML - import moose.SBML.writeSBML as _writeSBML -except Exception as e: - sbmlImport_ = False - sbmlError_ = '%s' % e - -# NeuroML2 import. -nml2Import_, nml2ImportError_ = True, '' -try: - import moose.neuroml2 as _neuroml2 -except Exception as e: - nml2Import_ = False - nml2ImportError_ = ' '.join( [ - "NML2 support is disabled because `libneuroml` and " - , "`pyneuroml` modules are not found.\n" - , " $ pip install pyneuroml libneuroml \n" - , " should fix it." - , " Actual error: %s " % e ] - ) - -chemImport_, chemError_ = True, '' -try: - import moose.chemUtil as _chemUtil -except Exception as e: - chemImport_ = False - chemError_ = '%s' % e - -kkitImport_, kkitImport_error_ = True, '' -try: - import moose.genesis.writeKkit as _writeKkit -except ImportError as e: - kkitImport_ = False - kkitImport_err_ = '%s' % e - -mergechemImport_, mergechemError_ = True, '' -try: - import moose.chemMerge as _chemMerge -except Exception as e: - mergechemImport_ = False - mergechemError_ = '%s' % e - -def loadModel(filename, modelpath, solverclass="gsl"): - """loadModel: Load model from a file to a specified path. - - Parameters - ---------- - filename: str - model description file. - modelpath: str - moose path for the top level element of the model to be created. - method: str - solver type to be used for simulating the model. - TODO: Link to detailed description of solvers? - - Returns - ------- - object - moose.element if succcessful else None. - """ - - if not os.path.isfile( os.path.realpath(filename) ): - mu.warn( "Model file '%s' does not exists or is not readable." % filename ) - return None - - extension = os.path.splitext(filename)[1] - if extension in [".swc", ".p"]: - return _moose.loadModelInternal(filename, modelpath, "Neutral" ) - - if extension in [".g", ".cspace"]: - # only if genesis or cspace file and method != ee then only - # mooseAddChemSolver is called. - ret = _moose.loadModelInternal(filename, modelpath, "ee") - sc = solverclass.lower() - if sc in ["gssa","gillespie","stochastic","gsolve"]: - method = "gssa" - elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: - method = "gsl" - elif sc in ["exponential euler","exponentialeuler","neutral"]: - method = "ee" - else: - method = "ee" - - if method != 'ee': - chemError = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, method) - return ret - else: - mu.error( "Unknown model extenstion '%s'" % extension) - return None - - -# Version -def version( ): - # Show user version. - return _moose.VERSION - -# Tests -from moose.moose_test import test - -sequence_types = ['vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector', - 'vector'] - -known_types = ['void', - 'char', - 'short', - 'int', - 'unsigned int', - 'double', - 'float', - 'long', - 'unsigned long', - 'string', - 'vec', - 'melement'] + sequence_types - -# SBML related functions. -def mooseReadSBML(filepath, loadpath, solver='ee',validate="on"): - """Load SBML model. - - Parameter - -------- - filepath: str - filepath to be loaded. - loadpath : str - Root path for this model e.g. /model/mymodel - solver : str - Solver to use (default 'ee'). - Available options are "ee", "gsl", "stochastic", "gillespie" - "rk", "deterministic" - For full list see ?? - """ - global sbmlImport_ - if sbmlImport_: - modelpath = _readSBML.mooseReadSBML(filepath, loadpath, solver, validate) - sc = solver.lower() - if sc in ["gssa","gillespie","stochastic","gsolve"]: - method = "gssa" - elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: - method = "gsl" - elif sc in ["exponential euler","exponentialeuler","neutral"]: - method = "ee" - else: - method = "ee" - - if method != 'ee': - chemError = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath[0].path, method) - - return modelpath - else: - print( sbmlError_ ) - return False - -def mooseWriteSBML(modelpath, filepath, sceneitems={}): - """mooseWriteSBML: Writes loaded model under modelpath to a file in SBML format. - - Parameters - ---------- - modelpath : str - model path in moose e.g /model/mymodel \n - filepath : str - Path of output file. \n - sceneitems : dict - UserWarning: user need not worry about this layout position is saved in - Annotation field of all the moose Object (pool,Reaction,enzyme). - If this function is called from - * GUI - the layout position of moose object is passed - * command line - NA - * if genesis/kkit model is loaded then layout position is taken from the file - * otherwise auto-coordinates is used for layout position. - """ - if sbmlImport_: - return _writeSBML.mooseWriteSBML(modelpath, filepath, sceneitems) - else: - print( sbmlError_ ) - return False - - -def mooseWriteKkit(modelpath, filepath, sceneitems={}): - """Writes loded model under modelpath to a file in Kkit format. - - Parameters - ---------- - modelpath : str - Model path in moose. - filepath : str - Path of output file. - """ - global kkitImport_, kkitImport_err_ - if not kkitImport_: - print( '[WARN] Could not import module to enable this function' ) - print( '\tError was %s' % kkitImport_error_ ) - return False - return _writeKkit.mooseWriteKkit(modelpath, filepath,sceneitems) - - -def mooseDeleteChemSolver(modelpath): - """mooseDeleteChemSolver - deletes solver on all the compartment and its children. - - Notes - ----- - This is neccesary while created a new moose object on a pre-existing modelpath, - this should be followed by mooseAddChemSolver for add solvers on to compartment - to simulate else default is Exponential Euler (ee) - """ - if chemImport_: - return _chemUtil.add_Delete_ChemicalSolver.mooseDeleteChemSolver(modelpath) - else: - print( chemError_ ) - return False - - -def mooseAddChemSolver(modelpath, solver): - """mooseAddChemSolver: - Add solver on chemical compartment and its children for calculation - - Parameters - ---------- - - modelpath : str - Model path that is loaded into moose. - solver : str - Exponential Euler "ee" is default. Other options are Gillespie ("gssa"), - Runge Kutta ("gsl") etc. Link to documentation? - """ - if chemImport_: - chemError_ = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, solver) - return chemError_ - else: - print( chemError_ ) - return False - -def mergeChemModel(src, des): - """mergeChemModel: Merges two chemical model. - File or filepath can be passed source is merged to destination - """ - #global mergechemImport_ - if mergechemImport_: - return _chemMerge.merge.mergeChemModel(src,des) - else: - return False - -# NML2 reader and writer function. -def mooseReadNML2( modelpath, verbose = False ): - """Read NeuroML model (version 2) and return reader object. - """ - global nml2Import_ - if not nml2Import_: - mu.warn( nml2ImportError_ ) - raise RuntimeError( "Could not load NML2 support." ) - - reader = _neuroml2.NML2Reader( verbose = verbose ) - reader.read( modelpath ) - return reader - -def mooseWriteNML2( outfile ): - raise NotImplementedError( "Writing to NML2 is not supported yet" ) - -################################################################ -# Wrappers for global functions -################################################################ -def pwe(): - """Print present working element. Convenience function for GENESIS - users. If you want to retrieve the element in stead of printing - the path, use moose.getCwe() - - """ - pwe_ = _moose.getCwe() - print(pwe_.getPath()) - return pwe_ - - -def le(el=None): - """List elements under `el` or current element if no argument - specified. - - Parameters - ---------- - el : str/melement/vec/None - The element or the path under which to look. If `None`, children - of current working element are displayed. - - Returns - ------- - List of path of child elements - - """ - if el is None: - el = _moose.getCwe() - elif isinstance(el, str): - if not _moose.exists(el): - raise ValueError('no such element') - el = _moose.element(el) - elif isinstance(el, _moose.vec): - el = el[0] - print('Elements under', el.path) - for ch in el.children: - print(ch.path) - return [child.path for child in el.children] - -ce = _moose.setCwe # ce is a GENESIS shorthand for change element. - -def syncDataHandler(target): - """Synchronize data handlers for target. - - Parameters - ---------- - target : melement/vec/str - Target element or vec or path string. - - Raises - ------ - NotImplementedError - The call to the underlying C++ function does not work. - - Notes - ----- - This function is defined for completeness, but currently it does not work. - - """ - raise NotImplementedError('The implementation is not working for IntFire - goes to invalid objects. \ -First fix that issue with SynBase or something in that line.') - if isinstance(target, str): - if not _moose.exists(target): - raise ValueError('%s: element does not exist.' % (target)) - target = _moose.vec(target) - _moose.syncDataHandler(target) - - -def showfield(el, field='*', showtype=False): - """Show the fields of the element `el`, their data types and - values in human readable format. Convenience function for GENESIS - users. - - Parameters - ---------- - el : melement/str - Element or path of an existing element. - - field : str - Field to be displayed. If '*' (default), all fields are displayed. - - showtype : bool - If True show the data type of each field. False by default. - - Returns - ------- - None - - """ - if isinstance(el, str): - if not _moose.exists(el): - raise ValueError('no such element') - el = _moose.element(el) - if field == '*': - value_field_dict = _moose.getFieldDict(el.className, 'valueFinfo') - max_type_len = max(len(dtype) for dtype in value_field_dict.values()) - max_field_len = max(len(dtype) for dtype in value_field_dict.keys()) - print('\n[', el.path, ']') - for key, dtype in sorted(value_field_dict.items()): - if dtype == 'bad' or key == 'this' or key == 'dummy' or key == 'me' or dtype.startswith( - 'vector') or 'ObjId' in dtype: - continue - value = el.getField(key) - if showtype: - typestr = dtype.ljust(max_type_len + 4) - # The following hack is for handling both Python 2 and - # 3. Directly putting the print command in the if/else - # clause causes syntax error in both systems. - print(typestr, end=' ') - print(key.ljust(max_field_len + 4), '=', value) - else: - try: - print(field, '=', el.getField(field)) - except AttributeError: - pass # Genesis silently ignores non existent fields - - -def showfields(el, showtype=False): - """Convenience function. Should be deprecated if nobody uses it. - """ - warnings.warn( - 'Deprecated. Use showfield(element, field="*", showtype=True) instead.', - DeprecationWarning) - showfield(el, field='*', showtype=showtype) - -# Predefined field types and their human readable names -finfotypes = [('valueFinfo', 'value field'), - ('srcFinfo', 'source message field'), - ('destFinfo', 'destination message field'), - ('sharedFinfo', 'shared message field'), - ('lookupFinfo', 'lookup field')] - -def listmsg(el): - """Return a list containing the incoming and outgoing messages of - `el`. - - Parameters - ---------- - el : melement/vec/str - MOOSE object or path of the object to look into. - - Returns - ------- - msg : list - List of Msg objects corresponding to incoming and outgoing - connections of `el`. - - """ - obj = _moose.element(el) - ret = [] - for msg in obj.inMsg: - ret.append(msg) - for msg in obj.outMsg: - ret.append(msg) - return ret - - -def showmsg(el): - """Print the incoming and outgoing messages of `el`. - - Parameters - ---------- - el : melement/vec/str - Object whose messages are to be displayed. - - Returns - ------- - None - - """ - obj = _moose.element(el) - print('INCOMING:') - for msg in obj.msgIn: - print( - msg.e2.path, - msg.destFieldsOnE2, - '<---', - msg.e1.path, - msg.srcFieldsOnE1) - print('OUTGOING:') - for msg in obj.msgOut: - print( - msg.e1.path, - msg.srcFieldsOnE1, - '--->', - msg.e2.path, - msg.destFieldsOnE2) - - -def getfielddoc(tokens, indent=''): - """Return the documentation for field specified by `tokens`. - - Parameters - ---------- - tokens : (className, fieldName) str - A sequence whose first element is a MOOSE class name and second - is the field name. - - indent : str - indentation (default: empty string) prepended to builtin - documentation string. - - Returns - ------- - docstring : str - string of the form - `{indent}{className}.{fieldName}: {datatype} - {finfoType}\n{Description}\n` - - Raises - ------ - NameError - If the specified fieldName is not present in the specified class. - """ - assert(len(tokens) > 1) - classname = tokens[0] - fieldname = tokens[1] - while True: - try: - classelement = _moose.element('/classes/' + classname) - for finfo in classelement.children: - for fieldelement in finfo: - baseinfo = '' - if classname != tokens[0]: - baseinfo = ' (inherited from {})'.format(classname) - if fieldelement.fieldName == fieldname: - # The field elements are - # /classes/{ParentClass}[0]/{fieldElementType}[N]. - finfotype = fieldelement.name - return '{indent}{classname}.{fieldname}: type={type}, finfotype={finfotype}{baseinfo}\n\t{docs}\n'.format( - indent=indent, classname=tokens[0], - fieldname=fieldname, - type=fieldelement.type, - finfotype=finfotype, - baseinfo=baseinfo, - docs=fieldelement.docs) - classname = classelement.baseClass - except ValueError: - raise NameError('`%s` has no field called `%s`' - % (tokens[0], tokens[1])) - - -def toUnicode(v, encoding='utf8'): - # if isinstance(v, str): - # return v - try: - return v.decode(encoding) - except (AttributeError, UnicodeEncodeError): - return str(v) - - -def getmoosedoc(tokens, inherited=False): - """Return MOOSE builtin documentation. - - Parameters - ---------- - tokens : (className, [fieldName]) - tuple containing one or two strings specifying class name - and field name (optional) to get documentation for. - - inherited: bool (default: False) - include inherited fields. - - Returns - ------- - docstring : str - Documentation string for class `className`.`fieldName` if both - are specified, for the class `className` if fieldName is not - specified. In the latter case, the fields and their data types - and finfo types are listed. - - Raises - ------ - NameError - If class or field does not exist. - - """ - indent = ' ' - docstring = StringIO() - with closing(docstring): - if not tokens: - return "" - try: - class_element = _moose.element('/classes/%s' % (tokens[0])) - except ValueError as e: - raise NameError('name \'%s\' not defined.' % (tokens[0])) - if len(tokens) > 1: - docstring.write(toUnicode(getfielddoc(tokens))) - else: - docstring.write(toUnicode('%s\n' % (class_element.docs))) - append_finfodocs(tokens[0], docstring, indent) - if inherited: - mro = eval('_moose.%s' % (tokens[0])).mro() - for class_ in mro[1:]: - if class_ == _moose.melement: - break - docstring.write(toUnicode( - '\n\n#Inherited from %s#\n' % (class_.__name__))) - append_finfodocs(class_.__name__, docstring, indent) - if class_ == _moose.Neutral: # Neutral is the toplevel moose class - break - return docstring.getvalue() - - -def append_finfodocs(classname, docstring, indent): - """Append list of finfos in class name to docstring""" - try: - class_element = _moose.element('/classes/%s' % (classname)) - except ValueError: - raise NameError('class \'%s\' not defined.' % (classname)) - for ftype, rname in finfotypes: - docstring.write(toUnicode('\n*%s*\n' % (rname.capitalize()))) - try: - finfo = _moose.element('%s/%s' % (class_element.path, ftype)) - for field in finfo.vec: - docstring.write(toUnicode( - '%s%s: %s\n' % (indent, field.fieldName, field.type))) - except ValueError: - docstring.write(toUnicode('%sNone\n' % (indent))) - - -# the global pager is set from pydoc even if the user asks for paged -# help once. this is to strike a balance between GENESIS user's -# expectation of control returning to command line after printing the -# help and python user's expectation of seeing the help via more/less. -pager = None - - -def doc(arg, inherited=True, paged=True): - """Display the documentation for class or field in a class. - - Parameters - ---------- - arg : str/class/melement/vec - A string specifying a moose class name and a field name - separated by a dot. e.g., 'Neutral.name'. Prepending `moose.` - is allowed. Thus moose.doc('moose.Neutral.name') is equivalent - to the above. - It can also be string specifying just a moose class name or a - moose class or a moose object (instance of melement or vec - or there subclasses). In that case, the builtin documentation - for the corresponding moose class is displayed. - - paged: bool - Whether to display the docs via builtin pager or print and - exit. If not specified, it defaults to False and - moose.doc(xyz) will print help on xyz and return control to - command line. - - Returns - ------- - None - - Raises - ------ - NameError - If class or field does not exist. - - """ - # There is no way to dynamically access the MOOSE docs using - # pydoc. (using properties requires copying all the docs strings - # from MOOSE increasing the loading time by ~3x). Hence we provide a - # separate function. - global pager - if paged and pager is None: - pager = pydoc.pager - tokens = [] - text = '' - if isinstance(arg, str): - tokens = arg.split('.') - if tokens[0] == 'moose': - tokens = tokens[1:] - elif isinstance(arg, type): - tokens = [arg.__name__] - elif isinstance(arg, _moose.melement) or isinstance(arg, _moose.vec): - text = '%s: %s\n\n' % (arg.path, arg.className) - tokens = [arg.className] - if tokens: - text += getmoosedoc(tokens, inherited=inherited) - else: - text += pydoc.getdoc(arg) - if pager: - pager(text) - else: - print(text) - - -# -# moose.py ends here diff --git a/python/setup.cmake.py b/python/setup.py similarity index 83% rename from python/setup.cmake.py rename to python/setup.py index ca67559d45..17d625c104 100644 --- a/python/setup.cmake.py +++ b/python/setup.py @@ -15,12 +15,7 @@ import os import sys - -# NOTE: Though setuptool is preferred we use distutils. -# 1. setuptool normalize VERSION even when it is compatible with PyPI -# guidelines. This caused havoc on our OBS build. -from distutils.core import setup - +from setuptools import setup # Read version from VERSION created by cmake file. This file must be present for # setup.cmake.py to work perfectly. @@ -32,7 +27,7 @@ version = f.read( ) print( 'Got %s from VERSION file' % version ) -# importlib is available only for python3. Since we build wheels, prefer .so +# importlib is available only for python3. For building wheels, prefer .so # extension. This way a wheel built by any python3.x will work with any python3. suffix = '.so' try: @@ -59,5 +54,5 @@ ], install_requires = [ 'numpy' ], package_dir = { 'moose' : 'moose', 'rdesigneur' : 'rdesigneur' }, - package_data = { 'moose' : ['_moose' + suffix, 'neuroml2/schema/NeuroMLCoreDimensions.xml','chemUtil/rainbow2.pkl'] }, + package_data = { 'moose' : ['moose' + suffix, 'neuroml2/schema/NeuroMLCoreDimensions.xml','chemUtil/rainbow2.pkl'] }, ) diff --git a/shell/Shell.cpp b/shell/Shell.cpp index fe55a708fe..d130b0a23f 100644 --- a/shell/Shell.cpp +++ b/shell/Shell.cpp @@ -21,9 +21,12 @@ #include "../msg/OneToOneMsg.h" #include "../msg/OneToAllMsg.h" #include "../msg/SparseMsg.h" -#include "../builtins/SocketStreamer.h" #include "../builtins/Streamer.h" +#ifdef UNIX +#include "../builtins/SocketStreamer.h" +#endif + #include "Shell.h" #include "Wildcard.h" diff --git a/synapse/SeqSynHandler.cpp b/synapse/SeqSynHandler.cpp index da94fc4299..a42825ef12 100644 --- a/synapse/SeqSynHandler.cpp +++ b/synapse/SeqSynHandler.cpp @@ -10,6 +10,7 @@ #include #include "../basecode/global.h" #include "../basecode/header.h" +#include "../utility/numutil.h" #include "Synapse.h" #include "SynEvent.h" #include "SynHandlerBase.h" diff --git a/tests/python/test_moose_attribs.py b/tests/python/test_moose_attribs.py index 3c8746b773..501a1556f9 100644 --- a/tests/python/test_moose_attribs.py +++ b/tests/python/test_moose_attribs.py @@ -38,7 +38,7 @@ 'Table2', 'TableBase', 'TimeTable', 'VClamp', 'VERSION', 'Variable', 'VectorTable', 'ZombieBufPool', 'ZombieCaConc', 'ZombieCompartment', 'ZombieEnz', 'ZombieFunction', 'ZombieHHChannel', 'ZombieMMenz', - 'ZombiePool', 'ZombieReac', '_moose', + 'ZombiePool', 'ZombieReac', 'moose', 'append_finfodocs', 'ce', 'chemMerge', 'chemUtil', 'closing', 'connect', 'copy', 'delete', 'division', 'doc', 'element', 'exists', 'finfotypes', 'fixXreacs', 'genesis', 'getCwe',