diff --git a/.github/workflows/neuron-ci.yml b/.github/workflows/neuron-ci.yml index e39caea04f..9dcec6d953 100644 --- a/.github/workflows/neuron-ci.yml +++ b/.github/workflows/neuron-ci.yml @@ -108,12 +108,12 @@ jobs: # Unlink and re-link to prevent errors when GitHub macOS runner images # install Python outside of brew; See actions/setup-python#577 and BlueBrain/libsonata/pull/317 brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done - brew install ccache coreutils doxygen flex bison mpich ninja xz autoconf autoconf automake libtool + brew install ccache coreutils doxygen flex bison mpich ninja xz autoconf automake libtool # We use both for dynamic mpi in nrn brew unlink mpich brew install openmpi brew install --cask xquartz - echo /usr/local/opt/flex/bin:/usr/local/opt/bison/bin >> $GITHUB_PATH + echo "$(brew --prefix)/opt/flex/bin:$(brew --prefix)/opt/bison/bin" >> $GITHUB_PATH # Core https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources echo CMAKE_BUILD_PARALLEL_LEVEL=3 >> $GITHUB_ENV echo CTEST_PARALLEL_LEVEL=3 >> $GITHUB_ENV @@ -273,7 +273,7 @@ jobs: else CC=$(command -v clang-14) CXX=$(command -v clang++-14) - symbolizer_path=$(realpath $(command -v llvm-symbolizer-14)) + symbolizer_path="$(readlink -f "$(command -v llvm-symbolizer-14)")" cmake_args+=(-DLLVM_SYMBOLIZER_PATH="${symbolizer_path}") fi fi diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d06798d067..dd586ea86b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -167,7 +167,7 @@ simulation_stack: variables: bb5_ntasks: 2 # so we block 16 cores bb5_cpus_per_task: 8 # ninja -j {this} - bb5_memory: 120G # ~1.5*16*384/80 (~1.5x more as we have seen OOMs) + bb5_memory: 160G # ~2*16*384/80 (~2x more as we have seen OOMs) .spack_intel: variables: @@ -192,7 +192,7 @@ simulation_stack: extends: [.ctest] variables: bb5_ntasks: 16 - bb5_memory: 120G # ~1.5*16*384/80 (~1.5x more as we have seen OOMs) + bb5_memory: 160G # ~2*16*384/80 (~2x more as we have seen OOMs) # Build NMODL once with GCC build:nmodl: diff --git a/CMakeLists.txt b/CMakeLists.txt index ef953d55ff..ecdec04d5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,6 +444,10 @@ if(NRN_ENABLE_THREADS) list(APPEND NRN_COMPILE_DEFS NRN_ENABLE_THREADS) endif() +# Some files are shared by NEURON and CoreNEURON and use #if CORENRN_BUILD to distinguish between +# which build is occurring. +list(APPEND NRN_COMPILE_DEFS CORENRN_BUILD=0) + # ============================================================================= # Profiler/Instrumentation Options # ============================================================================= @@ -729,7 +733,7 @@ add_custom_target( # ============================================================================= # ~~~ # Update hh.mod for CoreNEURON compatibility -# - Replace GLOBAL variable by RANHE +# - Replace GLOBAL variable by RANGE # - Comment out TABLE # ~~~ # ============================================================================= @@ -826,10 +830,12 @@ if(NRN_ENABLE_TESTS) set(neurondemo_files "${neurondemo_prefix}/special" "${neurondemo_prefix}/${CMAKE_SHARED_LIBRARY_PREFIX}nrnmech${CMAKE_SHARED_LIBRARY_SUFFIX}") + add_custom_command( OUTPUT ${neurondemo_files} COMMAND ${CMAKE_COMMAND} -E env ${NRN_RUN_FROM_BUILD_DIR_ENV} ${NRN_SANITIZER_ENABLE_ENVIRONMENT} "${PROJECT_BINARY_DIR}/bin/nrnivmodl" + DEPENDS nrnivmodl_dependency WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/share/nrn/demo/release VERBATIM) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad0d7dccaa..8e13be525c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -94,6 +94,10 @@ formatted, indented, documented, and commented. The [Neuron Development Topics](https://neuronsimulator.github.io/nrn/dev/index.html) section of the documentation provides a starting point for understanding NEURON's internals. +### Building NEURON + +To build NEURON, please follow the instructions [here](docs/install/install_instructions.md#installing-source-distributions). + ### Code Formatting Currently we have enabled CMake and Clang code formatting using [cmake-format](https://github.com/cheshirekow/cmake_format) and [clang-format](https://clang.llvm.org/docs/ClangFormat.html). Before submitting a PR, make sure to run cmake-format and clang-format as below: @@ -142,9 +146,6 @@ Or, See [cmake-format](https://github.com/cheshirekow/cmake_format) documentation for details. -make && make format-pr -``` - ## Python Contributions The Python source code is located under `share/lib/python/neuron`. Python unit tests are diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index a60c5888f4..cd537ec88e 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -36,7 +36,7 @@ include(CMakeListsNrnMech) # ============================================================================= # nrnmech_makefile (based on coreneuron Configure templates) # ============================================================================= -nrn_configure_file(nrngui bin) +configure_file("nrngui.in" "nrngui" @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sortspike ${CMAKE_CURRENT_BINARY_DIR}/sortspike COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nrnivmodl_makefile_cmake.in ${PROJECT_BINARY_DIR}/bin/nrnmech_makefile @ONLY) @@ -49,10 +49,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nrnpyenv.sh.in ${PROJECT_BINARY_DIR}/ # file(CHMOD ...) and v3.20+ support setting permissions directly in configure_file(...). set(NRN_CONFIG_EXE_FILES "nrnivmodl" "neurondemo") foreach(NRN_CONFIG_EXE_FILE ${NRN_CONFIG_EXE_FILES}) - nrn_configure_dest_src(${NRN_CONFIG_EXE_FILE} bin/tmp ${NRN_CONFIG_EXE_FILE} bin) + configure_file("${NRN_CONFIG_EXE_FILE}.in" "tmp/${NRN_CONFIG_EXE_FILE}" @ONLY) file( - COPY ${PROJECT_BINARY_DIR}/bin/tmp/${NRN_CONFIG_EXE_FILE} - DESTINATION ${PROJECT_BINARY_DIR}/bin + COPY "${CMAKE_CURRENT_BINARY_DIR}/tmp/${NRN_CONFIG_EXE_FILE}" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" FILE_PERMISSIONS OWNER_READ OWNER_WRITE @@ -62,7 +62,7 @@ foreach(NRN_CONFIG_EXE_FILE ${NRN_CONFIG_EXE_FILES}) WORLD_READ WORLD_EXECUTE) endforeach() -file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/bin/tmp) +file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/bin/tmp") # ============================================================================= # Install targets diff --git a/bin/nrnivmodl-core.in b/bin/nrnivmodl-core.in index 96726d4d9e..4668361cda 100755 --- a/bin/nrnivmodl-core.in +++ b/bin/nrnivmodl-core.in @@ -109,7 +109,7 @@ shopt -s nullglob if [ -d "$params_MODS_PATH" ]; then files=( "$params_MODS_PATH"/*.mod ) if [ ${#files} -eq 0 ]; then - echo "WARNING: No mod files found in '$(realpath ${params_MODS_PATH})', compiling default ones only!" + echo "WARNING: No mod files found in '$(readlink -f "${params_MODS_PATH}")', compiling default ones only!" fi else echo "FATAL: Invalid mods directory: '$params_MODS_PATH'" diff --git a/bin/nrnivmodl.in b/bin/nrnivmodl.in index 3df1ad6e4e..6ba80d444e 100755 --- a/bin/nrnivmodl.in +++ b/bin/nrnivmodl.in @@ -34,7 +34,7 @@ export prefix export bindir export libdir -if command -v xcrun; then +if command -v xcrun >/dev/null 2>&1; then @NRN_OSX_BUILD_TRUE@export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) @NRN_OSX_BUILD_TRUE@export MACOSX_DEPLOYMENT_TARGET="@CMAKE_OSX_DEPLOYMENT_TARGET@" if [ -z "${MACOSX_DEPLOYMENT_TARGET}" ]; then diff --git a/bin/nrnivmodl_core_makefile.in b/bin/nrnivmodl_core_makefile.in index 90acecb724..86a5138d70 100644 --- a/bin/nrnivmodl_core_makefile.in +++ b/bin/nrnivmodl_core_makefile.in @@ -71,7 +71,7 @@ CXX_LINK_EXE_CMD = $(CXX) $(CXXFLAGS) @CMAKE_EXE_LINKER_FLAGS@ CXX_SHARED_LIB_CMD = $(CXX) $(CXXFLAGS) @CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS@ @CMAKE_SHARED_LIBRARY_CXX_FLAGS@ @CMAKE_SHARED_LINKER_FLAGS@ # env variables required for mod2c or nmodl -NMODL_ENV_VAR = @CORENRN_SANITIZER_ENABLE_ENVIRONMENT_STRING@ PYTHONPATH=@CORENRN_NMODL_PYTHONPATH@:${CORENRN_LIB_DIR}/python MODLUNIT=$(CORENRN_SHARE_NMODL_DIR)/nrnunits.lib +NMODL_ENV_VAR = @CORENRN_SANITIZER_ENABLE_ENVIRONMENT_STRING@ PYTHONPATH=${PYTHONPATH}:@CORENRN_NMODL_PYTHONPATH@:${CORENRN_LIB_DIR}/python MODLUNIT=$(CORENRN_SHARE_NMODL_DIR)/nrnunits.lib ifeq (@CORENRN_ENABLE_GPU@, ON) nmodl_arguments_c=@NMODL_ACC_BACKEND_ARGS@ @NMODL_COMMON_ARGS@ @@ -145,7 +145,7 @@ endif # main target to build binary -$(SPECIAL_EXE): $(corenrnmech_lib_target) +$(SPECIAL_EXE): $(corenrnmech_lib_target) $(CORENRN_SHARE_CORENRN_DIR)/coreneuron.cpp @printf " => $(C_GREEN)Binary$(C_RESET) creating $(SPECIAL_EXE)\n" $(CXX_LINK_EXE_CMD) -o $(SPECIAL_EXE) $(CORENRN_SHARE_CORENRN_DIR)/coreneuron.cpp \ -I$(CORENRN_INC_DIR) $(INCFLAGS) \ diff --git a/bin/nrnivmodl_makefile_cmake.in b/bin/nrnivmodl_makefile_cmake.in index 71eade96ae..49164a7656 100644 --- a/bin/nrnivmodl_makefile_cmake.in +++ b/bin/nrnivmodl_makefile_cmake.in @@ -121,7 +121,7 @@ C_GREEN := \033[32m # Take the main and link with nrnmech. # RPATH is set for DESTDIR_RPATH and coreneuron lib -special: $(mech_lib) +special: $(mech_lib) $(datadir)/nrnmain.cpp @printf " => $(C_GREEN)LINKING$(C_RESET) executable \"${PWD}/$(special)\" LDFLAGS are: $(LDFLAGS)\n" $(CXX_LINK_EXE) -I $(incdir) -I $(incdir)/nrncvode -DAUTO_DLOPEN_NRNMECH=0 $(datadir)/nrnmain.cpp -o $(special) \ -L$(OBJS_DIR) -l$(mech_libname) $(NRNLIB_FLAGS) -l$(mech_libname) $(extra_lib_link) -Wl,-rpath,'$(DESTDIR_RPATH)' -Wl,-rpath,$(libdir) $(LDFLAGS) $(EXTRA_LDFLAGS) diff --git a/cmake/ConfigFileSetting.cmake b/cmake/ConfigFileSetting.cmake index d0b458222a..0e831c0861 100644 --- a/cmake/ConfigFileSetting.cmake +++ b/cmake/ConfigFileSetting.cmake @@ -1,10 +1,6 @@ # ============================================================================= # Definitions used in nrnconf.h and nmodlconf.h # ============================================================================= -set(PACKAGE_NAME "${PACKAGE}") -set(PACKAGE_TARNAME "${PACKAGE}") -set(PACKAGE_BUGREPORT "\"\"") -set(PACKAGE_URL "\"\"") set(UNQUOTED_PACKAGE_VERSION "${PROJECT_VERSION}") # ~~~ @@ -13,7 +9,6 @@ set(UNQUOTED_PACKAGE_VERSION "${PROJECT_VERSION}") # ~~~ nrn_set_string(PACKAGE "nrn") nrn_set_string(NRNHOST "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}") -nrn_set_string(PACKAGE_STRING "nrn ${PROJECT_VERSION}") nrn_set_string(PACKAGE_VERSION "${PROJECT_VERSION}") nrn_set_string(VERSION "${PROJECT_VERSION}") nrn_set_string(NRN_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib") @@ -24,7 +19,6 @@ nrn_set_string(DLL_DEFAULT_FNAME "${CMAKE_SYSTEM_PROCESSOR}/.libs/libnrnmech.so" add_definitions(-DHAVE_CONFIG_H) set(YYTEXT_POINTER 1) -set(TIME_WITH_SYS_TIME 1) # below two are universal nowadays set(prefix ${CMAKE_INSTALL_PREFIX}) set(host_cpu ${CMAKE_SYSTEM_PROCESSOR}) @@ -120,31 +114,20 @@ set(SUNDIALS_USE_GENERIC_MATH 1) nrn_check_include_files(dlfcn.h HAVE_DLFCN_H) nrn_check_include_files(execinfo.h HAVE_EXECINFO_H) nrn_check_include_files(malloc.h HAVE_MALLOC_H) -nrn_check_include_files(strings.h HAVE_STRINGS_H) nrn_check_include_files(sys/types.h HAVE_SYS_TYPES_H) nrn_check_include_files(unistd.h HAVE_UNISTD_H) -# ============================================================================= -# Check for standard headers -# ============================================================================= -check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" - STDC_HEADERS) - # ============================================================================= # Check symbol using check_cxx_symbol_exists but use ${NRN_HEADERS_INCLUDE_LIST} # ============================================================================= # note that this must be called after all *check_include_files because we use # NRN_HEADERS_INCLUDE_LIST is second argument (headers) is empty. -nrn_check_symbol_exists("bcopy" "strings.h" HAVE_BCOPY) -nrn_check_symbol_exists("bzero" "strings.h" HAVE_BZERO) -nrn_check_symbol_exists("index" "strings.h" HAVE_INDEX) nrn_check_symbol_exists("isatty" "unistd.h" HAVE_ISATTY) nrn_check_symbol_exists("iv" "" HAVE_IV) nrn_check_symbol_exists("mallinfo" "malloc.h" HAVE_MALLINFO) nrn_check_symbol_exists("mallinfo2" "malloc.h" HAVE_MALLINFO2) nrn_check_symbol_exists("mkstemp" "stdlib.h" HAVE_MKSTEMP) nrn_check_symbol_exists("posix_memalign" "stdlib.h" HAVE_POSIX_MEMALIGN) -nrn_check_symbol_exists("realpath" "stdlib.h" HAVE_REALPATH) nrn_check_symbol_exists("setenv" "stdlib.h" HAVE_SETENV) nrn_check_symbol_exists("setitimer" "sys/time.h" HAVE_SETITIMER) nrn_check_symbol_exists("sigaction" "signal.h" HAVE_SIGACTION) @@ -161,23 +144,6 @@ nrn_check_type_exists(sys/types.h pid_t int pid_t) nrn_check_type_exists(sys/types.h size_t "unsigned int" size_t) nrn_check_type_exists(sys/types.h uid_t int uid_t) -# ============================================================================= -# Set return type of signal in RETSIGTYPE -# ============================================================================= -nrn_check_signal_return_type(RETSIGTYPE) - -# ============================================================================= -# Check direcotry manipulation header -# ============================================================================= -nrn_check_dir_exists(sys/ndir.h HAVE_SYS_NDIR_H) - -# ============================================================================= -# Copy cmake specific template files -# ============================================================================= -# nrnconf.h.in and nmodlconf.h.in were originally generated from config.h.in generated by -# autoheader. We use repository copy cmake_nrnconf.h.in to create nrnconf.h and nmodlconf.h directly -# in the PROJECT_BINARY_DIR from the nrn_configure_dest_src macro. - # ============================================================================= # Generate file from file.in template # ============================================================================= @@ -185,40 +151,40 @@ set(version_strs ${NRN_PYTHON_VERSIONS}) list(TRANSFORM version_strs APPEND "\"") list(TRANSFORM version_strs PREPEND "\"") string(JOIN ", " NRN_DYNAMIC_PYTHON_LIST_OF_VERSION_STRINGS ${version_strs}) -nrn_configure_dest_src(nrnconf.h . cmake_nrnconf.h .) -nrn_configure_dest_src(nmodlconf.h . cmake_nrnconf.h .) -nrn_configure_file(nrnmpiuse.h src/oc) -nrn_configure_file(nrnconfigargs.h src/nrnoc) -nrn_configure_file(nrnneosm.h src/nrncvode) -nrn_configure_file(sundials_config.h src/sundials) -nrn_configure_dest_src(nrn.defaults share/nrn/lib nrn.defaults share/lib) +configure_file("${PROJECT_SOURCE_DIR}/cmake_nrnconf.h.in" "${PROJECT_BINARY_DIR}/nrnconf.h" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/src/oc/nrnmpiuse.h.in" + "${PROJECT_BINARY_DIR}/src/oc/nrnmpiuse.h" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/src/nrnoc/nrnconfigargs.h.in" + "${PROJECT_BINARY_DIR}/src/nrnoc/nrnconfigargs.h" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/src/nrncvode/nrnneosm.h.in" + "${PROJECT_BINARY_DIR}/src/nrncvode/nrnneosm.h" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/src/sundials/sundials_config.h.in" + "${PROJECT_BINARY_DIR}/src/sundials/sundials_config.h" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/share/lib/nrn.defaults.in" + "${PROJECT_BINARY_DIR}/share/nrn/lib/nrn.defaults" @ONLY) file(COPY ${PROJECT_SOURCE_DIR}/share/lib/nrnunits.lib DESTINATION ${PROJECT_BINARY_DIR}/share/nrn/lib) if(NRN_MACOS_BUILD) set(abs_top_builddir ${PROJECT_BINARY_DIR}) - nrn_configure_file(macdist.pkgproj src/mac) - nrn_configure_file(postinstall.sh src/mac) + configure_file("${PROJECT_SOURCE_DIR}/src/mac/macdist.pkgproj.in" + "${PROJECT_BINARY_DIR}/src/mac/macdist.pkgproj" @ONLY) + configure_file("${PROJECT_SOURCE_DIR}/src/mac/postinstall.sh.in" + "${PROJECT_BINARY_DIR}/src/mac/postinstall.sh" @ONLY) endif() if(MINGW) dospath("${CMAKE_INSTALL_PREFIX}" WIN_MARSHAL_NRN_DIR) - nrn_configure_file(nrnsetupmingw.nsi src/mswin) - nrn_configure_file(pre_setup_exe.sh src/mswin) + configure_file("${PROJECT_SOURCE_DIR}/src/mswin/nrnsetupmingw.nsi.in" + "${PROJECT_BINARY_DIR}/src/mswin/nrnsetupmingw.nsi" @ONLY) + configure_file("${PROJECT_SOURCE_DIR}/src/mswin/pre_setup_exe.sh.in" + "${PROJECT_BINARY_DIR}/src/mswin/pre_setup_exe.sh" @ONLY) # Just name and not path since setup.exe user chooses location of install. set(CXX x86_64-w64-mingw32-g++.exe) set(BUILD_MINGW_TRUE "") set(BUILD_MINGW_FALSE "#") set(nrnskip_rebase "#") - nrn_configure_file(mknrndll.mak src/mswin/lib) -endif() - -# ============================================================================= -# If Interviews is not provided, configure local files -# ============================================================================= -if(NOT NRN_ENABLE_INTERVIEWS) - nrn_configure_dest_src(config.h . cmake_nrnconf.h .) -else() - file(REMOVE "${PROJECT_BINARY_DIR}/config.h") + configure_file("${PROJECT_SOURCE_DIR}/src/mswin/lib/mknrndll.mak.in" + "${PROJECT_BINARY_DIR}/src/mswin/lib/mknrndll.mak" @ONLY) endif() # Prepare some variables for setup.py extension building (hoc_module, rx3d and music) diff --git a/cmake/MacroHelper.cmake b/cmake/MacroHelper.cmake index 482345cacb..b077ab909d 100644 --- a/cmake/MacroHelper.cmake +++ b/cmake/MacroHelper.cmake @@ -62,68 +62,6 @@ macro(nrn_check_type_exists HEADER TYPE DEFAULT_TYPE VARIABLE) file(REMOVE "conftest.cpp") endmacro() -# ============================================================================= -# Check return type of signal -# ============================================================================= -macro(nrn_check_signal_return_type VARIABLE) - # code template to check signal support - string(CONCAT CONFTEST_RETSIGTYPE "#include \n" "#include \n" - "int main () {\n" " return *(signal (0, 0)) (0) == 1\;\n" "}\n") - file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/conftest.cpp ${CONFTEST_RETSIGTYPE}) - try_compile(MY_RESULT_VAR ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/conftest.cpp) - if(MY_RESULT_VAR) - set(${VARIABLE} int) - else() - set(${VARIABLE} void) - endif() - file(REMOVE "conftest.cpp") -endmacro() - -# ============================================================================= -# Transform PROJECT_SOURCE_DIR/sdir/sfile.in to PROJECT_BINARY_DIR/bdir/bfile -# ============================================================================= -# ~~~ -# This 4 arg macro transformsPROJECT_SOURCE_DIR/sdir/sfile.in into -# PROJECT_BINARY_DIR/bdir/bfile . -# THE shorter two arg form transforms PROJECT_SOURCE_DIR/dir/file.in into -# PROJECT_BINARY_DIR/dir/file -# This first copies with some replacement the sfile.in to _cmake_tmp_bfile.in -# so that the normal cmake configure_file command works to make a proper -# cmake_file. Then that is compared to a possibly existing bfile and, -# if different, copies _cmake_tmp_bfile to bfile. This prevents recompilation of -# .o files that depend on unchanged bfile. The sdir arg is the path relative to -# PROJECT_SOURCE_DIR, the bdir arg is the path relative to PROJECT_BINARY_DIR. -# Note that everytime cmake is run, the bfile is compared to a newly created -# _cmake_tmp_bfile consistent with the current cmake args. -# Note that the sfile arg does NOT contain the .in suffix. -# ~~~ -macro(nrn_configure_dest_src bfile bdir sfile sdir) - set(infile ${PROJECT_SOURCE_DIR}/${sdir}/${sfile}.in) - set(bin_dir ${PROJECT_BINARY_DIR}/${bdir}) - file(MAKE_DIRECTORY ${bin_dir}) - execute_process( - COMMAND sed "s/\#undef *\\(.*\\)/\#cmakedefine \\1 @\\1@/" - INPUT_FILE ${infile} - OUTPUT_FILE ${bin_dir}/_cmake_tmp_${bfile}.in) - configure_file(${bin_dir}/_cmake_tmp_${bfile}.in ${bin_dir}/_cmake_tmp_${bfile} @ONLY) - execute_process(COMMAND cmp -s ${bin_dir}/_cmake_tmp_${bfile} ${bin_dir}/${bfile} - RESULT_VARIABLE result) - if(result EQUAL 0) - file(REMOVE ${bin_dir}/_cmake_tmp_${bfile}) - else() - file(RENAME ${bin_dir}/_cmake_tmp_${bfile} ${bin_dir}/${bfile}) - endif() - file(REMOVE ${bin_dir}/_cmake_tmp_${bfile}.in) - set_property( - DIRECTORY - APPEND - PROPERTY CMAKE_CONFIGURE_DEPENDS ${infile}) -endmacro() - -macro(nrn_configure_file file dir) - nrn_configure_dest_src(${file} ${dir} ${file} ${dir}) -endmacro() - # ============================================================================= # Perform check_include_files and add it to NRN_HEADERS_INCLUDE_LIST if exist Passing an optional # CXX will call check_include_files_cxx instead. diff --git a/cmake/NeuronFileLists.cmake b/cmake/NeuronFileLists.cmake index 9141aa4562..fe41a53a55 100644 --- a/cmake/NeuronFileLists.cmake +++ b/cmake/NeuronFileLists.cmake @@ -259,6 +259,12 @@ set(NRNIV_FILE_LIST symdir.cpp vrecord.cpp) +# ============================================================================= +# Files from coreneuron/permute that get compiled also for NEURON +# ============================================================================= +set(NODEORDEROPTIM_FILE_LIST balance.cpp cellorder.cpp cellorder1.cpp cellorder2.cpp + node_permute.cpp) + # ============================================================================= # Files in nrncvode directory # ============================================================================= @@ -434,6 +440,7 @@ set(NRN_OC_SRC_DIR ${PROJECT_SOURCE_DIR}/src/oc) set(NRN_NRNOC_SRC_DIR ${PROJECT_SOURCE_DIR}/src/nrnoc) set(NRN_NRNOC_BUILD_DIR ${PROJECT_BINARY_DIR}/src/nrnoc) set(NRN_IVOC_SRC_DIR ${PROJECT_SOURCE_DIR}/src/ivoc) +set(NRN_NODEORDEROPTIM_SRC_DIR ${PROJECT_SOURCE_DIR}/src/coreneuron/permute) set(NRN_NRNCVODE_SRC_DIR ${PROJECT_SOURCE_DIR}/src/nrncvode) set(NRN_NRNIV_SRC_DIR ${PROJECT_SOURCE_DIR}/src/nrniv) set(NRN_MODLUNIT_SRC_DIR ${PROJECT_SOURCE_DIR}/src/modlunit) @@ -448,6 +455,9 @@ nrn_create_file_list(NRN_OC_SRC_FILES ${NRN_OC_SRC_DIR} ${OC_FILE_LIST}) nrn_create_file_list(NRN_NRNOC_SRC_FILES ${NRN_NRNOC_SRC_DIR} ${NRNOC_FILE_LIST}) nrn_create_file_list(NRN_NRNOC_SRC_FILES ${NRN_NRNOC_BUILD_DIR} ${NRNOC_GENERATED_FILE_LIST}) nrn_create_file_list(NRN_IVOC_SRC_FILES ${NRN_IVOC_SRC_DIR} ${IVOC_FILE_LIST}) +nrn_create_file_list(NRN_NODEORDEROPTIM_SRC_FILES ${NRN_NODEORDEROPTIM_SRC_DIR} + ${NODEORDEROPTIM_FILE_LIST}) +list(APPEND NRN_NODEORDEROPTIM_SRC_FILES ${PROJECT_SOURCE_DIR}/src/coreneuron/utils/lpt.cpp) nrn_create_file_list(NRN_NRNCVODE_SRC_FILES ${NRN_NRNCVODE_SRC_DIR} ${NRNCVODE_FILE_LIST}) nrn_create_file_list(NRN_NRNIV_SRC_FILES ${NRN_NRNIV_SRC_DIR} ${NRNIV_FILE_LIST}) nrn_create_file_list(NRN_PARALLEL_SRC_FILES ${PROJECT_SOURCE_DIR}/src/nrniv diff --git a/cmake/NeuronTestHelper.cmake b/cmake/NeuronTestHelper.cmake index 35a8ec5e6c..405a5d7736 100644 --- a/cmake/NeuronTestHelper.cmake +++ b/cmake/NeuronTestHelper.cmake @@ -163,6 +163,7 @@ function(nrn_add_test_group) if(NRN_ADD_TEST_GROUP_CORENEURON AND NRN_ENABLE_CORENEURON) list(APPEND hash_components -coreneuron) list(APPEND nrnivmodl_dependencies ${CORENEURON_TARGET_TO_DEPEND}) + list(APPEND nrnivmodl_dependencies coreneuron-core) list(APPEND nrnivmodl_command -coreneuron) endif() list(APPEND nrnivmodl_command .) @@ -354,7 +355,7 @@ function(nrn_add_test) # We want to preserve directory structures, so if you pass SCRIPT_PATTERNS path/to/*.py then you # end up with {build_directory}/path/to/test_working_directory/path/to/script.py file( - GLOB script_files + GLOB script_files CONFIGURE_DEPENDS RELATIVE "${test_source_directory}/${sim_directory}" "${test_source_directory}/${sim_directory}/${script_pattern}") foreach(script_file ${script_files}) diff --git a/cmake/find_libpython.py b/cmake/find_libpython.py deleted file mode 100755 index 27568910c9..0000000000 --- a/cmake/find_libpython.py +++ /dev/null @@ -1,393 +0,0 @@ -#!/usr/bin/env python - -""" -Locate libpython associated with this Python executable. -""" - -# https://pypi.org/project/find-libpython/ - -# License -# -# Copyright 2018, Takafumi Arakaki -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -from __future__ import print_function, absolute_import - -from logging import getLogger -import ctypes.util -import functools -import os -import sys -import sysconfig - -logger = getLogger("find_libpython") - -is_windows = os.name == "nt" -is_apple = sys.platform == "darwin" - -SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") -if SHLIB_SUFFIX is None: - if is_windows: - SHLIB_SUFFIX = ".dll" - else: - SHLIB_SUFFIX = ".so" -if is_apple: - # sysconfig.get_config_var("SHLIB_SUFFIX") can be ".so" in macOS. - # Let's not use the value from sysconfig. - SHLIB_SUFFIX = ".dylib" - - -def linked_libpython(): - """ - Find the linked libpython using dladdr (in *nix). - - Calling this in Windows always return `None` at the moment. - - Returns - ------- - path : str or None - A path to linked libpython. Return `None` if statically linked. - """ - if is_windows: - return None - return _linked_libpython_unix() - - -class Dl_info(ctypes.Structure): - _fields_ = [ - ("dli_fname", ctypes.c_char_p), - ("dli_fbase", ctypes.c_void_p), - ("dli_sname", ctypes.c_char_p), - ("dli_saddr", ctypes.c_void_p), - ] - - -def _linked_libpython_unix(): - libdl = ctypes.CDLL(ctypes.util.find_library("dl")) - libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)] - libdl.dladdr.restype = ctypes.c_int - - dlinfo = Dl_info() - retcode = libdl.dladdr( - ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p), - ctypes.pointer(dlinfo), - ) - if retcode == 0: # means error - return None - path = os.path.realpath(dlinfo.dli_fname.decode()) - if path == os.path.realpath(sys.executable): - return None - return path - - -def library_name(name, suffix=SHLIB_SUFFIX, is_windows=is_windows): - """ - Convert a file basename `name` to a library name (no "lib" and ".so" etc.) - - >>> library_name("libpython3.7m.so") # doctest: +SKIP - 'python3.7m' - >>> library_name("libpython3.7m.so", suffix=".so", is_windows=False) - 'python3.7m' - >>> library_name("libpython3.7m.dylib", suffix=".dylib", is_windows=False) - 'python3.7m' - >>> library_name("python37.dll", suffix=".dll", is_windows=True) - 'python37' - """ - if not is_windows and name.startswith("lib"): - name = name[len("lib") :] - if suffix and name.endswith(suffix): - name = name[: -len(suffix)] - return name - - -def append_truthy(list, item): - if item: - list.append(item) - - -def uniquifying(items): - """ - Yield items while excluding the duplicates and preserving the order. - - >>> list(uniquifying([1, 2, 1, 2, 3])) - [1, 2, 3] - """ - seen = set() - for x in items: - if x not in seen: - yield x - seen.add(x) - - -def uniquified(func): - """Wrap iterator returned from `func` by `uniquifying`.""" - - @functools.wraps(func) - def wrapper(*args, **kwds): - return uniquifying(func(*args, **kwds)) - - return wrapper - - -@uniquified -def candidate_names(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate file names of libpython. - - Yields - ------ - name : str - Candidate name libpython. - """ - LDLIBRARY = sysconfig.get_config_var("LDLIBRARY") - if LDLIBRARY: - yield LDLIBRARY - - LIBRARY = sysconfig.get_config_var("LIBRARY") - if LIBRARY: - yield os.path.splitext(LIBRARY)[0] + suffix - - dlprefix = "" if is_windows else "lib" - sysdata = dict( - v=sys.version_info, - # VERSION is X.Y in Linux/macOS and XY in Windows: - VERSION=( - sysconfig.get_config_var("VERSION") - or "{v.major}.{v.minor}".format(v=sys.version_info) - ), - ABIFLAGS=( - sysconfig.get_config_var("ABIFLAGS") - or sysconfig.get_config_var("abiflags") - or "" - ), - ) - - for stem in [ - "python{VERSION}{ABIFLAGS}".format(**sysdata), - "python{VERSION}".format(**sysdata), - "python{v.major}".format(**sysdata), - "python", - ]: - yield dlprefix + stem + suffix - - -@uniquified -def candidate_paths(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate paths of libpython. - - Yields - ------ - path : str or None - Candidate path to libpython. The path may not be a fullpath - and may not exist. - """ - - yield linked_libpython() - - # List candidates for directories in which libpython may exist - lib_dirs = [] - append_truthy(lib_dirs, sysconfig.get_config_var("LIBPL")) - append_truthy(lib_dirs, sysconfig.get_config_var("srcdir")) - append_truthy(lib_dirs, sysconfig.get_config_var("LIBDIR")) - - # LIBPL seems to be the right config_var to use. It is the one - # used in python-config when shared library is not enabled: - # https://github.com/python/cpython/blob/v3.7.0/Misc/python-config.in#L55-L57 - # - # But we try other places just in case. - - if is_windows: - lib_dirs.append(os.path.join(os.path.dirname(sys.executable))) - else: - lib_dirs.append( - os.path.join(os.path.dirname(os.path.dirname(sys.executable)), "lib") - ) - - # For macOS: - append_truthy(lib_dirs, sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")) - - lib_dirs.append(sys.exec_prefix) - lib_dirs.append(os.path.join(sys.exec_prefix, "lib")) - - lib_basenames = list(candidate_names(suffix=suffix)) - - for directory in lib_dirs: - for basename in lib_basenames: - yield os.path.join(directory, basename) - - # In macOS and Windows, ctypes.util.find_library returns a full path: - for basename in lib_basenames: - yield ctypes.util.find_library(library_name(basename)) - - -# Possibly useful links: -# * https://packages.ubuntu.com/bionic/amd64/libpython3.6/filelist -# * https://github.com/Valloric/ycmd/issues/518 -# * https://github.com/Valloric/ycmd/pull/519 - - -def normalize_path(path, suffix=SHLIB_SUFFIX, is_apple=is_apple): - """ - Normalize shared library `path` to a real path. - - If `path` is not a full path, `None` is returned. If `path` does - not exists, append `SHLIB_SUFFIX` and check if it exists. - Finally, the path is canonicalized by following the symlinks. - - Parameters - ---------- - path : str ot None - A candidate path to a shared library. - """ - if not path: - return None - if not os.path.isabs(path): - return None - if os.path.exists(path): - return os.path.realpath(path) - if os.path.exists(path + suffix): - return os.path.realpath(path + suffix) - if is_apple: - return normalize_path(_remove_suffix_apple(path), suffix=".so", is_apple=False) - return None - - -def _remove_suffix_apple(path): - """ - Strip off .so or .dylib. - - >>> _remove_suffix_apple("libpython.so") - 'libpython' - >>> _remove_suffix_apple("libpython.dylib") - 'libpython' - >>> _remove_suffix_apple("libpython3.7") - 'libpython3.7' - """ - if path.endswith(".dylib"): - return path[: -len(".dylib")] - if path.endswith(".so"): - return path[: -len(".so")] - return path - - -@uniquified -def finding_libpython(): - """ - Iterate over existing libpython paths. - - The first item is likely to be the best one. - - Yields - ------ - path : str - Existing path to a libpython. - """ - logger.debug("is_windows = %s", is_windows) - logger.debug("is_apple = %s", is_apple) - for path in candidate_paths(): - logger.debug("Candidate: %s", path) - normalized = normalize_path(path) - if normalized: - logger.debug("Found: %s", normalized) - yield normalized - else: - logger.debug("Not found.") - - -def find_libpython(): - """ - Return a path (`str`) to libpython or `None` if not found. - - Parameters - ---------- - path : str or None - Existing path to the (supposedly) correct libpython. - """ - for path in finding_libpython(): - return os.path.realpath(path) - - -def print_all(items): - for x in items: - print(x) - - -def cli_find_libpython(cli_op, verbose): - import logging - - # Importing `logging` module here so that using `logging.debug` - # instead of `logger.debug` outside of this function becomes an - # error. - - if verbose: - logging.basicConfig(format="%(levelname)s %(message)s", level=logging.DEBUG) - - if cli_op == "list-all": - print_all(finding_libpython()) - elif cli_op == "candidate-names": - print_all(candidate_names()) - elif cli_op == "candidate-paths": - print_all(p for p in candidate_paths() if p and os.path.isabs(p)) - else: - path = find_libpython() - if path is None: - return 1 - print(path, end="") - - -def main(args=None): - import argparse - - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--verbose", "-v", action="store_true", help="Print debugging information." - ) - - group = parser.add_mutually_exclusive_group() - group.add_argument( - "--list-all", - action="store_const", - dest="cli_op", - const="list-all", - help="Print list of all paths found.", - ) - group.add_argument( - "--candidate-names", - action="store_const", - dest="cli_op", - const="candidate-names", - help="Print list of candidate names of libpython.", - ) - group.add_argument( - "--candidate-paths", - action="store_const", - dest="cli_op", - const="candidate-paths", - help="Print list of candidate paths of libpython.", - ) - - ns = parser.parse_args(args) - parser.exit(cli_find_libpython(**vars(ns))) - - -if __name__ == "__main__": - main() diff --git a/cmake_nrnconf.h.in b/cmake_nrnconf.h.in index 57436d1977..5bbd435916 100644 --- a/cmake_nrnconf.h.in +++ b/cmake_nrnconf.h.in @@ -1,154 +1,94 @@ -/* config.h.in. Generated from configure.ac by autoheader. */ - - -#ifndef H_nrnconf_included -#define H_nrnconf_included 1 - +#pragma once /* Define if building universal (internal helper macro) */ -#undef AC_APPLE_UNIVERSAL_BUILD - -/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP - systems. This function is required for `alloca.c' support on those systems. - */ -#undef CRAY_STACKSEG_END +#cmakedefine AC_APPLE_UNIVERSAL_BUILD @AC_APPLE_UNIVERSAL_BUILD@ /* if mac os x */ -#undef DARWIN +#cmakedefine DARWIN @DARWIN@ /* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H +#cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@ /* Define to 1 if you have the header file. */ -#undef HAVE_EXECINFO_H +#cmakedefine HAVE_EXECINFO_H @HAVE_EXECINFO_H@ /* Define to 1 if you have the `index' function. */ -#undef HAVE_INDEX +#cmakedefine HAVE_INDEX @HAVE_INDEX@ /* Define to 1 if you have the `isatty' function. */ -#undef HAVE_ISATTY +#cmakedefine HAVE_ISATTY @HAVE_ISATTY@ /* define if using InterViews */ -#undef HAVE_IV +#cmakedefine HAVE_IV @HAVE_IV@ /* Define to 1 if you have the `mallinfo' function. */ -#undef HAVE_MALLINFO +#cmakedefine HAVE_MALLINFO @HAVE_MALLINFO@ /* Define to 1 if you have the `mallinfo2' function. */ -#undef HAVE_MALLINFO2 +#cmakedefine HAVE_MALLINFO2 @HAVE_MALLINFO2@ /* Define to 1 if you have the header file. */ -#undef HAVE_MALLOC_H +#cmakedefine HAVE_MALLOC_H @HAVE_MALLOC_H@ /* Define to 1 if you have the `mkstemp' function. */ -#undef HAVE_MKSTEMP +#cmakedefine HAVE_MKSTEMP @HAVE_MKSTEMP@ /* Define to 1 if you have the `posix_memalign' function. */ -#undef HAVE_POSIX_MEMALIGN - -/* Define to 1 if you have the `realpath' function. */ -#undef HAVE_REALPATH +#cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ /* Define to 1 if you have the `setenv' function. */ -#undef HAVE_SETENV +#cmakedefine HAVE_SETENV @HAVE_SETENV@ /* Define to 1 if you have the `setitimer' function. */ -#undef HAVE_SETITIMER +#cmakedefine HAVE_SETITIMER @HAVE_SETITIMER@ /* Define to 1 if you have the `sigaction' function. */ -#undef HAVE_SIGACTION +#cmakedefine HAVE_SIGACTION @HAVE_SIGACTION@ /* Define to 1 if you have the `sigprocmask' function. */ -#undef HAVE_SIGPROCMASK +#cmakedefine HAVE_SIGPROCMASK @HAVE_SIGPROCMASK@ /* (Define if this signal exists) */ -#undef HAVE_SIGBUS - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H +#cmakedefine HAVE_SIGBUS @HAVE_SIGBUS@ /* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H +#cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@ /* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H +#cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ /* define if using mingw */ -#undef MINGW - -/* define if using Mike Neubig contributions */ -#undef MikeNeubig +#cmakedefine MINGW @MINGW@ /* where the lib hoc is */ -#undef NEURON_DATA_DIR +#cmakedefine NEURON_DATA_DIR @NEURON_DATA_DIR@ /* host triplet */ -#undef NRNHOST +#cmakedefine NRNHOST @NRNHOST@ /* if 1 then dlopen nrnmech instead of special */ -#undef NRNMECH_DLL_STYLE - -/* if 1 then dynamic units default is legacy units */ -#undef DYNAMIC_UNITS_USE_LEGACY_DEFAULT +#cmakedefine NRNMECH_DLL_STYLE @NRNMECH_DLL_STYLE@ /* if nrnoc can use X11 */ -#undef NRNOC_X11 +#cmakedefine NRNOC_X11 @NRNOC_X11@ /* location of NEURON libraries */ -#undef NRN_LIBDIR +#cmakedefine NRN_LIBDIR @NRN_LIBDIR@ /* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL +#cmakedefine PACKAGE @PACKAGE@ /* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define as the return type of signal handlers (`int' or `void'). */ -#undef RETSIGTYPE - -/* define if RETSIGTYPE(*)(int) is not the prototype for a signal handler */ -#undef SIGNAL_CAST - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -#undef STACK_DIRECTION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS +#cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* Define SUNDIALS data type 'realtype' as 'long double' */ -#undef SUNDIALS_DOUBLE_PRECISION +#cmakedefine SUNDIALS_DOUBLE_PRECISION @SUNDIALS_DOUBLE_PRECISION@ /* Use generic math functions */ -#undef SUNDIALS_USE_GENERIC_MATH - -/* Define to 1 if you can safely include both and . */ -#undef TIME_WITH_SYS_TIME - -/* Define to 1 if your declares `struct tm'. */ -#undef TM_IN_SYS_TIME +#cmakedefine SUNDIALS_USE_GENERIC_MATH @SUNDIALS_USE_GENERIC_MATH@ /* Version number of package */ -#undef VERSION +#cmakedefine VERSION @VERSION@ /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ @@ -158,29 +98,16 @@ # endif #else # ifndef WORDS_BIGENDIAN -# undef WORDS_BIGENDIAN +# cmakedefine WORDS_BIGENDIAN @WORDS_BIGENDIAN@ # endif #endif -/* Define to 1 if the X Window System is missing or not being used. */ -#undef X_DISPLAY_MISSING - /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ -#undef YYTEXT_POINTER - -/* Define to the type of a signed integer type of width exactly 64 bits if - such a type exists and the standard includes do not define it. */ -#undef int64_t - -/* Define to `long int' if does not define. */ -#undef off_t +#cmakedefine YYTEXT_POINTER @YYTEXT_POINTER@ /* Define to `int' if does not define. */ -#undef pid_t - -/* Define to `unsigned int' if does not define. */ -#undef size_t +#cmakedefine pid_t @pid_t@ /* __cplusplus guard still needed because this header is included from C code in * mesch (and maybe others) @@ -202,6 +129,3 @@ namespace neuron::config { #ifdef MINGW #define WIN32 1 #endif - -#endif /* H_nrnconf_included */ - diff --git a/docs/changelog.md b/docs/changelog.md index ab3b1002c5..0185999a9d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,33 @@ # NEURON 8.2 +## 8.2.4 +_Release Date_ : 08-02-2024 + + +### What's New + +This release brings no new features, just bugfixes and minor improvements. + + +### Bug Fixes +- Python 3.12 compatibility fixes + - replace distutils with setuptools + - fix segfault on exit +- updates to CI + - move from MacOS 11 to MacOS 12 + - add MUSIC + - bugfix for coverage CI +- small bugfix for edge case in RX3D +- unified setup.py +- misc cmake improvements + +### Improvements / Other Changes +- Disabled RXD code in notebook to avoid breaking docs build (see [GitHub Issue #2710](https://github.com/neuronsimulator/nrn/issues/2710)) +- Disabled RXD tests on Windows (see [GitHub Issue #2585](https://github.com/neuronsimulator/nrn/issues/2585)) + + +For the complete list of commits check [GitHub Issue #2700](https://github.com/neuronsimulator/nrn/issues/2700) + ## 8.2.3 _Release Date_ : 15-09-2023 @@ -64,7 +92,7 @@ _Release Date_ : 12-08-2022 - Documentation - added new INCF/CNS 2022 online material (#1932) - updates (dealing with sims, generating movie, modelview, more #1925 ) - - transfer from Yale website (#1867) + - transfer from Yale website (#1867) - nrnmpi_load: drop printf for already loaded lib (#1938) For the complete list of commits, see the list in [GitHub Issue #1944](https://github.com/neuronsimulator/nrn/issues/1944) @@ -76,7 +104,7 @@ _Release Date_ : 01-07-2022 * Allow multiple BEFORE/AFTER blocks of same type in a MOD file. #1722 * Several documentation updates, including randomness in NEURON models #1727, NEURON course exercise sets from 2018 #1735 and publications using NEURON #1819. -* CMake: improved documentation targets. (#1725) +* CMake: improved documentation targets. (#1725) ### Breaking Changes * Support for Python 3.6 was dropped, as it has reached end-of-life (#1733). @@ -87,7 +115,7 @@ _Release Date_ : 01-07-2022 * Declaring STATE variables as GLOBAL is now disallowed. (#1723) ### Deprecations & future changes -* NEURON is in the process of being fully migrated to a `C++` codebase. +* NEURON is in the process of being fully migrated to a `C++` codebase. Starting with next major release `9.0.0`, `MOD` files will be converted to `C++` instead of `C`. This will break compatibility with some legacy MOD files containing VERBATIM blocks and code may have to be updated given that some valid C code is not valid C++. @@ -102,7 +130,7 @@ _Release Date_ : 01-07-2022 ### Improvements / Other Changes * Support for using `mallinfo2()` (#1805) -* HOC domain for Sphinx `5.0.1` +* HOC domain for Sphinx `5.0.1` ### Upgrade Steps * If your MOD files contain VERBATIM blocks you may need to refer to the aforementioned @@ -213,7 +241,7 @@ _Release Date_ : 25-03-2022 * Internal API for saving/restoring 3D voxelization (#1476) * Prevent RxD keeping objects alive (#1270, #1103, #1072) * Improved assignment of 3D voxels to segments (#1149) - + ### Upgrade Steps * Linux wheels are now `manylinux2014`: upgrade your `pip` * Legacy internal `readline` source code is removed: install `readline` on your system @@ -246,7 +274,7 @@ For the complete list of bug fixes, see the list in [GitHub PR #1603](https://gi ### Improvements / Other Changes -- Introduce a Sphinx HOC domain for NEURON documentation +- Introduce a Sphinx HOC domain for NEURON documentation ## 8.0.0 @@ -270,7 +298,7 @@ _Release Date_ : 30-04-2021 - For 3d reaction-diffusion simulations, the voxelization and segment mapping algorithms have been adjusted, especially around the soma. Voxel indices and sometimes counts will change from previous versions. ### Deprecations -- Five functions in the `neuron` module: `neuron.init`, `neuron.run`, `neuron.psection`, `neuron.xopen`, and `neuron.quit`. +- Five functions in the `neuron` module: `neuron.init`, `neuron.run`, `neuron.psection`, `neuron.xopen`, and `neuron.quit`. - Autotools build is deprecated and will be removed in the next release. Use CMake instead. - Python 2 and Python 3.5 support is deprecated and will be removed in the next release. Use `Python >= 3.6` @@ -279,7 +307,7 @@ _Release Date_ : 30-04-2021 For the complete list of bug fixes, see the list on the [GitHub here](https://github.com/neuronsimulator/nrn/issues/1211#issuecomment-826919173). ### Improvements / Other Changes -- Allow for two point (single section) SWC somas +- Allow for two point (single section) SWC somas - GitHub Actions and Azure as primary CI systems. Travis CI removed. - GitHub [Releases](https://github.com/neuronsimulator/nrn/releases) provides full source tarballs, binary installers and python wheels. - Improved testing and CI infrastructure including GPUs @@ -287,18 +315,18 @@ For the complete list of bug fixes, see the list on the [GitHub here](https://gi - Improved integration of CoreNEURON - Support for recent numpy version - Various build improvements on Linux, MacOS and HPC platforms -- Documentation from various repositories is consolidated under `nrn` repository -- New releases via Spack and Easybuild package managers +- Documentation from various repositories is consolidated under `nrn` repository +- New releases via Spack and Easybuild package managers - Fix deadlock when compiling NEURON with AVX-512 - Add backward-cpp for better backtraces -- NEURON_MODULE_OPTIONS environment variable to pass in nrniv options before `neuron import` +- NEURON_MODULE_OPTIONS environment variable to pass in nrniv options before `neuron import` ### Upgrade Steps Existing models should work without any changes. In order to upgrade NEURON version you can: - Use python wheels provided for Linux or Mac OS platform - Use binary installer provided for windows -- Install from source, preferably using CMake build system +- Install from source, preferably using CMake build system - For new version, it's always a good idea to start over from scratch with nrnivmodl (deleting existing directory like `x86_64`) See `Installation` section under [nrn.readthedocs.io/](nrn.readthedocs.io/). In the very rare case that numerical differences exist, check selection of legacy vs modern units. diff --git a/docs/cmake_doc/options.rst b/docs/cmake_doc/options.rst index 1d9bfba9e6..7a1ef1e449 100644 --- a/docs/cmake_doc/options.rst +++ b/docs/cmake_doc/options.rst @@ -404,8 +404,8 @@ NMODL options To see all the NMODL CMake options you can look in https://github.com/BlueBrain/nmodl/blob/master/CMakeLists.txt. -NMODL_ENABLE_PYTHON_BINDINGS:BOOL=ON ------------------------------------- +NMODL_ENABLE_PYTHON_BINDINGS:BOOL=OFF +------------------------------------- Enable pybind11 based python bindings Using this option the user can use the NMODL python package to use NMODL via python. For more information look at diff --git a/docs/coreneuron/installation.rst b/docs/coreneuron/installation.rst index 0eb00f3fc4..721024b0d9 100644 --- a/docs/coreneuron/installation.rst +++ b/docs/coreneuron/installation.rst @@ -31,9 +31,6 @@ Installing from source ********************** To enable CoreNEURON when building NEURON, simply pass the ``-DNRN_ENABLE_CORENEURON=ON`` option to CMake. -By default, CoreNEURON will use the `mod2c `_ source-to-source compiler to translate MOD files into C++ code. -To use the more modern `NMODL `_ source-to-source compiler, you should additionally pass ``-DCORENRN_ENABLE_NMODL=ON`` to CMake. - Most of CoreNEURON's build dependencies (Bison, Flex, CMake, Python, MPI (optional), ...) are already dependencies of NEURON, but you may want to use a specialised compiler to get optimal performance. To enable GPU support the `NVIDIA HPC SDK `_. is also required. diff --git a/docs/hoc/modelspec/programmatic/mechanisms/nmodl.rst b/docs/hoc/modelspec/programmatic/mechanisms/nmodl.rst index 67010c3a56..dceba6a60e 100644 --- a/docs/hoc/modelspec/programmatic/mechanisms/nmodl.rst +++ b/docs/hoc/modelspec/programmatic/mechanisms/nmodl.rst @@ -742,16 +742,26 @@ Description: COMPARTMENT volume {state1 state2 . . . } where the STATE s named in the braces have the same compartment volume given by the volume - expression after the COMPARTMENT keyword. + expression after the COMPARTMENT keyword. In case a mechanism involves many compartments whose relative volumes are specified by the elements of an array the syntax is: .. code-block:: none - COMPARTMENT index, volume [ index ] { state1 state2 . . . } + COMPARTMENT index, volume { state1 state2 . . . } - where the STATEs that are diffusing are listed inside the braces. + where the STATEs that are diffusing are listed inside the braces. Note that + STATEs for the diffusing variables must be array variables, and volume + should be the expression to compute the volume for index ``index``. + + The following example states that the volume of the compartment for + ``s[i]`` is ``a[i]*b``. + + .. code-block:: + none + + COMPARTMENT i, a[i]*b { s } LONGITUDINAL_DIFFUSION diff --git a/docs/index.rst b/docs/index.rst index 00d8791a9e..06b1a34a9e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,10 +5,10 @@ The NEURON Simulator ==================== NEURON is a simulator for neurons and networks of neurons that runs efficiently on your local machine, in the cloud, or on an HPC. -Build and simulate models using Python, HOC, and/or NEURON's graphical interface. From this page you can watch :ref:`recorded NEURON classes `, +Build and simulate models using Python, HOC, and/or NEURON's graphical interface. From this page you can watch :ref:`recorded NEURON classes `, read the :ref:`Python ` or :ref:`HOC ` programmer's references, `browse the NEURON forum `_, -explore the `source code for over 800 NEURON models on ModelDB `_, +explore the `source code for over 800 NEURON models on ModelDB `_, and more (use the links on the side or search). .. toctree:: @@ -31,7 +31,7 @@ and more (use the links on the side or search). The NEURON forum publications publications-using-neuron - + .. toctree:: @@ -97,8 +97,8 @@ Installation .. code:: pip3 install neuron - - Alternatively, you can use the `PKG installer `_. + + Alternatively, you can use the `PKG installer `_. For troubleshooting, see the `detailed installation instructions `_. @@ -110,19 +110,18 @@ Installation .. code:: pip3 install neuron - For troubleshooting, see the `detailed installation instructions `_. .. tab-item:: Windows - `Download the Windows Installer `_. + `Download the Windows Installer `_. You can also install the Linux wheel via the Windows Subsystem for Linux (WSL). See `instructions `_. For troubleshooting, see the `detailed installation instructions `_. - + .. tab-item:: Cloud On `Google Colab `_ and many other cloud Jupyter providers, you can install @@ -131,10 +130,10 @@ Installation .. code:: !pip install neuron - + NEURON is already installed on `The Neuroscience Gateway `_ and on `EBRAINS `_. - + .. tab-item:: Source code View and suggest changes to the source code at: @@ -168,7 +167,7 @@ Installation nav("userAgent", "Windows"); nav("platform", "Win", "Windows"); nav("oscpu", "Windows"); - + if (osName == "MacOS") { $("#installation input")[0].checked = true; } else if (osName == "Linux") { diff --git a/docs/install/install_instructions.md b/docs/install/install_instructions.md index 7c154c20f2..9f2da7e534 100644 --- a/docs/install/install_instructions.md +++ b/docs/install/install_instructions.md @@ -211,11 +211,6 @@ The following packages are optional (see build options): - MPI (for parallel) - X11 (Linux) or XQuartz (MacOS) (for GUI) -Note that you may have to force Cython version: -```bash -pip install "cython<3" -``` - Depending on platform you can install these dependencies as follows: @@ -226,21 +221,28 @@ The easiest way to install dependencies on Mac OS is to use [brew](https://brew. once [brew is installed](https://docs.brew.sh/Installation) you can do: ```bash -brew install coreutils openmpi cmake +brew install coreutils openmpi cmake flex bison brew install --cask xquartz ``` Once these packages are installed, setup PATH as: ```bash -export PATH=/usr/local/bin/:$PATH +export PATH="/usr/local/bin:/usr/local/opt/bison/bin:/usr/local/opt/flex/bin:$PATH" ``` If the desired python version is not installed, you can install it using -[official distribution](https://www.python.org/downloads/macos/). Also, note that +[official distribution](https://www.python.org/downloads/macos/) or via brew. Also, note that [Xcode Command Line Tools](https://stackoverflow.com/questions/9329243/how-to-install-xcode-command-line-tools) needs to be installed for development. +Finally, if you are building NEURON with the Python interface, you need to install all of the Python dependencies: + +```bash +pip3 install --user --upgrade pip +pip3 install --user -r nrn_requirements.txt +``` + #### Mac OS - Apple M1 @@ -267,21 +269,18 @@ needs to be installed for development. echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> $HOME/.zprofile eval $(/opt/homebrew/bin/brew shellenv) - brew install cmake - brew install open-mpi + brew install open-mpi cmake flex bison pip3 install --user --upgrade pip - export PATH="$HOME/Library/Python/3.8/bin":$PATH - pip3 install --user cython + pip3 install --user -r nrn_requirements.txt ``` Once these packages are installed, setup PATH as: ```bash -export PATH=/opt/homebrew/opt/bison/bin/:/opt/homebrew/opt/flex/bin/:/opt/homebrew/bin/:$PATH +export PATH="/opt/homebrew/opt/bison/bin/:/opt/homebrew/opt/flex/bin/:/opt/homebrew/bin/:$PATH" ``` - #### Linux Depending on the platform (Ubuntu, CentOS, Fedora, Debian, Red Hat etc.), there are different ways to @@ -293,7 +292,7 @@ sudo apt-get install -y bison cmake flex git \ libncurses-dev libopenmpi-dev libx11-dev \ libxcomposite-dev openmpi-bin python3-dev # for python dependencies -pip install scipy numpy cython +pip install -r nrn_requirements.txt ``` We recommend using platform specific instructions provided in [nrn-build-ci](https://github.com/neuronsimulator/nrn-build-ci#scheduled-ci-builds-for-neuron) repository. diff --git a/docs/install/mac_pkg.md b/docs/install/mac_pkg.md index 8f69c73c64..86cee22d88 100644 --- a/docs/install/mac_pkg.md +++ b/docs/install/mac_pkg.md @@ -52,6 +52,12 @@ cmake .. -DCMAKE_INSTALL_PREFIX=$NRN_INSTALL \ ``` [^1] [^1]: NRN_RX3D_OPT_LEVEL=2 can build VERY slowly (cython translated cpp file can take a half hour or more). So for testing, we generally copy the script to temp.sh and modify to NRN_RX3D_OPT_LEVEL=0 +In order to build CoreNEURON support, one must add following options: +``` +-DLINK_AGAINST_PYTHON=OFF \ +-DNMODL_ENABLE_PYTHON_BINDINGS=ON \ +-DNRN_ENABLE_CORENEURON=ON +``` The default variables above will be ``` diff --git a/docs/python/modelspec/programmatic/network/parcon.rst b/docs/python/modelspec/programmatic/network/parcon.rst index 4fd8d22f0e..e5970af16a 100755 --- a/docs/python/modelspec/programmatic/network/parcon.rst +++ b/docs/python/modelspec/programmatic/network/parcon.rst @@ -3331,6 +3331,31 @@ Parallel Transfer ---- +.. method:: ParallelContext.optimize_node_order + + Syntax: + ``i= pc.optimize_node_order(i)`` + + Description: + Choose a node order (permutation) of data that + may improve memory latency and bandwidth utilization for gaussian + elmination. + Returns the node order (0-2) chosen (or currently in effect if no argument). + + 0. Nodes of a cell are adjacent. (Though all root nodes are adjacent + at the beginning of each thread's node list.) Default. + 1. Cells are interleaved, corresponding nodes of identical cells + are adjacent. Order of a given cell same as permutation 0. + 2. Depth first ordering. First, cell roots, then nodes + connecting to roots, etc. An attempt is made to order so that if + nodes are adjacent, then their parent nodes are also adjacent. + Note that 1 and 2 are identical ordering if all cells are indentical. + + Adopts the permutation and gaussian elimination methods of + CoreNEURON that were specified by the cell_permute=.. argument. + +---- + .. method:: ParallelContext.prcellstate Syntax: diff --git a/extend_depcomp.sh b/extend_depcomp.sh deleted file mode 100755 index 106aca221e..0000000000 --- a/extend_depcomp.sh +++ /dev/null @@ -1,16 +0,0 @@ -if grep -q xlc depcomp ; then - #echo "depcomp already extended to handle xlc. No change to file." - true; -else - sed '/^aix)/,/;;/ { - H - s/\.u/.d/ - s/^aix)/xlc) #following fragment is just like AIX but the extension is .d instead of .u/ - } - /^icc)/ { - x - G - } - ' depcomp > temp - mv temp depcomp -fi diff --git a/external/coding-conventions b/external/coding-conventions index 80a2c90134..3d157f9c8e 160000 --- a/external/coding-conventions +++ b/external/coding-conventions @@ -1 +1 @@ -Subproject commit 80a2c9013463b89b5c426e18403e9a2f87c59a00 +Subproject commit 3d157f9c8ea94d196a825aaa7f523265e360dc4b diff --git a/external/iv b/external/iv index 67b6b20ed8..ad4d9223b3 160000 --- a/external/iv +++ b/external/iv @@ -1 +1 @@ -Subproject commit 67b6b20ed8784da3a8070cc1042f65e11cdf8b08 +Subproject commit ad4d9223b3c94ab5906e8f25f8ad1698160b6338 diff --git a/external/nmodl b/external/nmodl index 8f7eb99fd3..2fb037e1f8 160000 --- a/external/nmodl +++ b/external/nmodl @@ -1 +1 @@ -Subproject commit 8f7eb99fd36ab886eac5c1ab050272fd2c46fa04 +Subproject commit 2fb037e1f8d60d522ba16bd968c69a318ce5a827 diff --git a/packaging/python/build_wheels.bash b/packaging/python/build_wheels.bash index 007fa581ce..8925b3e49f 100755 --- a/packaging/python/build_wheels.bash +++ b/packaging/python/build_wheels.bash @@ -144,6 +144,7 @@ build_wheel_osx() { if [ "$2" == "coreneuron" ]; then setup_args="--enable-coreneuron" clone_install_nmodl_requirements + CMAKE_DEFS="${CMAKE_DEFS},LINK_AGAINST_PYTHON=OFF" fi CMAKE_DEFS="NRN_MPI_DYNAMIC=$3" diff --git a/packaging/python/test_wheels.sh b/packaging/python/test_wheels.sh index 3e458d1f2a..0fb3adae69 100755 --- a/packaging/python/test_wheels.sh +++ b/packaging/python/test_wheels.sh @@ -74,6 +74,8 @@ run_mpi_test () { # coreneuron execution via neuron if [[ "$has_coreneuron" == "true" ]]; then rm -rf $ARCH_DIR + # also copy one MOD file containing sparse solver + cp share/examples/nrniv/nmodl/capmp.mod "test/coreneuron/mod files/" nrnivmodl -coreneuron "test/coreneuron/mod files/" $mpi_launcher -n 1 $python_exe test/coreneuron/test_direct.py diff --git a/setup.py b/setup.py index bccf1bed53..614b57d2b9 100644 --- a/setup.py +++ b/setup.py @@ -509,7 +509,21 @@ def setup_package(): else "node-and-date" }, cmdclass=dict(build_ext=CMakeAugmentedBuilder, docs=Docs), - install_requires=["numpy>=1.9.3", "packaging", "find_libpython", "setuptools"], + install_requires=[ + "numpy>=1.9.3", + "packaging", + "find_libpython", + "setuptools", + ] + + ( + [ + "sympy>=1.3", + "importlib_resources;python_version<'3.9'", + "importlib_metadata;python_version<'3.9'", + ] + if Components.CORENRN + else [] + ), tests_require=["flake8", "pytest"], setup_requires=["wheel", "setuptools_scm"] + maybe_docs diff --git a/share/lib/python/neuron/coreneuron.py b/share/lib/python/neuron/coreneuron.py index b384614a23..d20c9cf033 100644 --- a/share/lib/python/neuron/coreneuron.py +++ b/share/lib/python/neuron/coreneuron.py @@ -235,27 +235,27 @@ def model_path(self): """Data path of the model.""" return self._model_path - @sim_config.setter + @model_path.setter def model_path(self, value): - self._model_path = str(value) + self._model_path = str(value) if value else value @property def save_path(self): """Data path for save.""" return self._save_path - @sim_config.setter + @save_path.setter def save_path(self, value): - self._save_path = str(value) + self._save_path = str(value) if value else value @property def restore_path(self): """Data path for restore.""" return self._restore_path - @sim_config.setter + @restore_path.setter def restore_path(self, value): - self._restore_path = str(value) + self._restore_path = str(value) if value else value @property def skip_write_model_to_disk(self): @@ -268,7 +268,7 @@ def skip_write_model_to_disk(self): """ return self._skip_write_model_to_disk - @sim_config.setter + @skip_write_model_to_disk.setter def skip_write_model_to_disk(self, value): self._skip_write_model_to_disk = value diff --git a/share/lib/python/neuron/gui.py b/share/lib/python/neuron/gui.py index be3336122d..67d79ac787 100644 --- a/share/lib/python/neuron/gui.py +++ b/share/lib/python/neuron/gui.py @@ -3,6 +3,8 @@ It loads nrngui.hoc, and starts a thread to periodically process the NEURON GUI event loop. + +Note that python threads are not used if nrniv is launched instead of Python """ @@ -105,11 +107,7 @@ def run(self): time.sleep(self.interval) -if ( - h.nrnversion(9) == "2" - or h.nrnversion(8).find("mingw") > 0 - or h.nrnversion(8).find("Windows") -): +if h.nrnversion(9) == "2": # launched with python (instead of nrniv) timer = LoopTimer(0.1, process_events) timer.start() while not timer.started: diff --git a/share/lib/python/scripts/_binwrapper.py b/share/lib/python/scripts/_binwrapper.py index 5fd1ca16d1..1fb7e417ab 100755 --- a/share/lib/python/scripts/_binwrapper.py +++ b/share/lib/python/scripts/_binwrapper.py @@ -7,7 +7,10 @@ import shutil import subprocess import sys -from pkg_resources import working_set +from importlib.metadata import metadata, PackageNotFoundError +from importlib.util import find_spec +from pathlib import Path + from setuptools.command.build_ext import new_compiler from packaging.version import Version from sysconfig import get_config_vars, get_config_var @@ -63,21 +66,14 @@ def _check_cpp_compiler_version(): def _config_exe(exe_name): """Sets the environment to run the real executable (returned)""" + try: + metadata("neuron-nightly") + print("INFO : Using neuron-nightly Package (Developer Version)") + except PackageNotFoundError: + pass - package_name = "neuron" + NRN_PREFIX = str(Path(find_spec("neuron").origin).parent / ".data") - # determine package to find the install location - if "neuron-nightly" in working_set.by_key: - print("INFO : Using neuron-nightly Package (Developer Version)") - package_name = "neuron-nightly" - elif "neuron" in working_set.by_key: - package_name = "neuron" - else: - raise RuntimeError("NEURON package not found! Verify PYTHONPATH") - - NRN_PREFIX = os.path.join( - working_set.by_key[package_name].location, "neuron", ".data" - ) os.environ["NEURONHOME"] = os.path.join(NRN_PREFIX, "share/nrn") os.environ["NRNHOME"] = NRN_PREFIX os.environ["CORENRNHOME"] = NRN_PREFIX @@ -91,6 +87,10 @@ def _config_exe(exe_name): if "NMODL_PYLIB" not in os.environ: os.environ["NMODL_PYLIB"] = find_libpython() + # nmodl module is inside /lib directory + sys.path.insert(0, os.path.join(NRN_PREFIX, "lib")) + os.environ["PYTHONPATH"] = ":".join(sys.path) + _set_default_compiler() return os.path.join(NRN_PREFIX, "bin", exe_name) diff --git a/src/coreneuron/CMakeLists.txt b/src/coreneuron/CMakeLists.txt index 561b9896e3..3594329de6 100644 --- a/src/coreneuron/CMakeLists.txt +++ b/src/coreneuron/CMakeLists.txt @@ -162,6 +162,9 @@ check_include_files(malloc.h have_malloc_h) if(have_malloc_h) list(APPEND CORENRN_COMPILE_DEFS HAVE_MALLOC_H) endif() +# Code incompletely shared with NEURON via #if CORENRN_BUILD fragments. NRN and CORENEURON builds +# sometimes compile the same few files but, for example, with different name spaces. +list(APPEND CORENRN_COMPILE_DEFS CORENRN_BUILD=1) # ============================================================================= # Build option specific compiler flags @@ -262,8 +265,8 @@ if(nmodl_FOUND) set(CORENRN_NMODL_PYTHONPATH $ENV{PYTHONPATH}) else() set(NMODL_ENABLE_PYTHON_BINDINGS - ON - CACHE BOOL "Disable NMODL python bindings") + OFF + CACHE BOOL "Enable NMODL python bindings") nrn_add_external_project(nmodl OFF) add_subdirectory(${PROJECT_SOURCE_DIR}/external/nmodl ${CMAKE_BINARY_DIR}/external/nmodl) set(CORENRN_NMODL_BINARY ${CMAKE_BINARY_DIR}/bin/nmodl${CMAKE_EXECUTABLE_SUFFIX}) @@ -334,6 +337,7 @@ add_link_options(${CORENRN_EXTRA_LINK_FLAGS}) file( GLOB CORENEURON_CODE_FILES + CONFIGURE_DEPENDS "apps/main1.cpp" "apps/corenrn_parameters.cpp" "gpu/nrn_acc_manager.cpp" @@ -378,8 +382,14 @@ cpp_cc_build_time_copy( INPUT "${CMAKE_CURRENT_SOURCE_DIR}/${ENGINEMECH_CODE_FILE}" OUTPUT "${CMAKE_BINARY_DIR}/share/coreneuron/enginemech.cpp" NO_TARGET) -set(nrnivmodl_core_dependencies "${CMAKE_BINARY_DIR}/share/coreneuron/mod_func.c.pl" - "${CMAKE_BINARY_DIR}/share/coreneuron/enginemech.cpp") +cpp_cc_build_time_copy( + INPUT "${CMAKE_CURRENT_SOURCE_DIR}/apps/coreneuron.cpp" + OUTPUT "${CMAKE_BINARY_DIR}/share/coreneuron/coreneuron.cpp" + NO_TARGET) +set(nrnivmodl_core_dependencies + "${CMAKE_BINARY_DIR}/share/coreneuron/mod_func.c.pl" + "${CMAKE_BINARY_DIR}/share/coreneuron/enginemech.cpp" + "${CMAKE_BINARY_DIR}/share/coreneuron/coreneuron.cpp") # Set up build rules that copy builtin mod files from src/coreneuron/mechanism/mech/modfile/*.mod to # {build_dir}/share/modfile/ file(GLOB builtin_modfiles "${PROJECT_SOURCE_DIR}/src/coreneuron/mechanism/mech/modfile/*.mod") @@ -468,6 +478,7 @@ endif() # Hines solver. This cannot be included in coreneuron-core because of this issue: # https://forums.developer.nvidia.com/t/cannot-dynamically-load-a-shared-library-containing-both-openacc-and-cuda-code/210972 add_library(coreneuron-core STATIC ${CORENEURON_CODE_FILES} ${CORENRN_MPI_OBJ}) +add_dependencies(coreneuron-core coreneuron-copy-nrnivmodl-core-dependencies) if(CORENRN_ENABLE_GPU) set(coreneuron_cuda_target coreneuron-cuda) add_library(coreneuron-cuda ${COMPILE_LIBRARY_TYPE} ${CORENEURON_CUDA_FILES}) @@ -646,10 +657,6 @@ endforeach() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utils/profile/profiler_interface.h ${CMAKE_BINARY_DIR}/include/coreneuron/nrniv/profiler_interface.h COPYONLY) -# main program required for building special-core -cpp_cc_build_time_copy(INPUT ${CMAKE_CURRENT_SOURCE_DIR}/apps/coreneuron.cpp - OUTPUT ${CMAKE_BINARY_DIR}/share/coreneuron/coreneuron.cpp) - # Extract the various compiler option strings to use inside nrnivmodl-core. Sets the global property # CORENRN_LIB_LINK_FLAGS, which contains the arguments that must be added to the link line for # `special` to link against `libcorenrnmech_internal.{a,so}` diff --git a/src/coreneuron/apps/main1.cpp b/src/coreneuron/apps/main1.cpp index c5d5b3d2db..8d2e0ca688 100644 --- a/src/coreneuron/apps/main1.cpp +++ b/src/coreneuron/apps/main1.cpp @@ -139,7 +139,7 @@ void get_nrn_trajectory_requests(int bsize) { tr->varrays = varrays; tr->scatter = pvars; for (int i = 0; i < n_trajec; ++i) { - tr->gather[i] = stdindex2ptr(types[i], indices[i], nt); + tr->gather[i] = legacy_index2pointer(types[i], indices[i], nt); } delete[] types; delete[] indices; diff --git a/src/coreneuron/coreneuron.hpp b/src/coreneuron/coreneuron.hpp index bf06c0de73..7031aeb0c4 100644 --- a/src/coreneuron/coreneuron.hpp +++ b/src/coreneuron/coreneuron.hpp @@ -90,6 +90,7 @@ class CoreNeuron { * Internal lookup tables. Number of float and int variables in each mechanism and memory layout * future --> mech class */ + std::vector> nrn_array_dims; std::vector nrn_prop_param_size; std::vector nrn_prop_dparam_size; std::vector nrn_mech_data_layout; /* 1 AoS (default), 0 SoA */ @@ -159,6 +160,10 @@ class CoreNeuron { return bamech; } + auto& get_array_dims() { + return nrn_array_dims; + } + auto& get_prop_param_size() { return nrn_prop_param_size; } diff --git a/src/coreneuron/io/core2nrn_data_return.cpp b/src/coreneuron/io/core2nrn_data_return.cpp index 3aa230911a..1ec5fc5201 100644 --- a/src/coreneuron/io/core2nrn_data_return.cpp +++ b/src/coreneuron/io/core2nrn_data_return.cpp @@ -63,45 +63,66 @@ static void inverse_permute_copy(size_t n, double* permuted_src, double* dest, i } } -/** @brief SoA permuted mechanism data copied to unpermuted AoS data. - * dest is an array of n pointers to the beginning of each sz length array. - * src is a contiguous array of sz segments of size stride. The stride - * may be slightly greater than n for purposes of alignment. - * Each of the sz segments of src are permuted. - */ -static void soa2aos_inverse_permute_copy(size_t n, - int sz, - int stride, - double* src, - std::vector& dest, - int* permute) { - // src is soa and permuted. dest is n pointers to sz doubles (aos). - for (size_t instance = 0; instance < n; ++instance) { - double* s = src + permute[instance]; - for (int i = 0; i < sz; ++i) { - dest[i][instance] = s[i * stride]; +// See `soaos_copy_cnrn2nrn` for documentation. +template +static void soaos_copy_cnrn2nrn_impl(size_t n, + int stride, + double const* const src, + std::vector& dest, + const std::vector& array_dims, + int* permute) { + // i : runs over instances: 0, ..., n. + // j : runs over variables: 0, ..., array_dims.size() =: n_vars. + // k : runs over array dimension: 0, ..., array_dims[i_var] =: K. + + int n_vars = array_dims.size(); + double const* src_var = src; + for (size_t j_var = 0; j_var < n_vars; ++j_var) { + size_t K = array_dims[j_var]; + + for (size_t i = 0; i < n; ++i) { + size_t i_src = needs_permute ? static_cast(permute[i]) : i; + for (size_t k = 0; k < K; ++k) { + dest[j_var][i * K + k] = src_var[i_src * K + k]; + } } + + src_var += stride * K; } } -/** @brief SoA unpermuted mechanism data copied to unpermuted AoS data. - * dest is an array of n pointers to the beginning of each sz length array. - * src is a contiguous array of sz segments of size stride. The stride - * may be slightly greater than n for purposes of alignment. - * Each of the sz segments of src have the same order as the n pointers - * of dest. +/** @brief SoAoS unpermuted mechanism data copied to unpermuted SoAoS data. + * + * This function copies the CoreNEURON SoAoS format into the NEURON format. There + * are two differences: + * 1. NRN allocates a separate array for each variable, while CoreNEURON + * allocates a single large chunk of memory. + * 2. CoreNEURON pads the instance number. + * + * For ARRAY variables both NRN and CoreNRN store consecutive elements of the + * array consecutively in memory. + * + * Let `K` be the number of variables, counting array variables as one + * variable. Then `dest` is an array of `K` pointers, one for each variable of + * the mechanism. The pointer `dest[i]` is an array of size `n*array_dims[i]` + * and the array element `k` for instance `i` is stored at `dest[i*n + k]`. + * + * The pointer `src` points to `stride*sum(array_dims)` doubles. + * + * If `permute != nullptr`, then then the instance is permuted, i.e. the + * instance index is `permute[i]` in `src` (and `i` in `dest`). If `permute == + * nullptr` no permutation is performed. */ -static void soa2aos_unpermuted_copy(size_t n, - int sz, - int stride, - double* src, - std::vector& dest) { - // src is soa and permuted. dest is n pointers to sz doubles (aos). - for (size_t instance = 0; instance < n; ++instance) { - double* s = src + instance; - for (int i = 0; i < sz; ++i) { - dest[i][instance] = s[i * stride]; - } +static void soaos_copy_cnrn2nrn(size_t n, + int stride, + double const* const src, + std::vector& dest, + const std::vector& array_dims, + int* permute) { + if (permute == nullptr) { + soaos_copy_cnrn2nrn_impl(n, stride, src, dest, array_dims, permute); + } else { + soaos_copy_cnrn2nrn_impl(n, stride, src, dest, array_dims, permute); } } @@ -327,13 +348,10 @@ void core2nrn_data_return() { double* cndat = ml->data; int layout = corenrn.get_mech_data_layout()[mtype]; int sz = corenrn.get_prop_param_size()[mtype]; + const std::vector& array_dims = corenrn.get_array_dims()[mtype]; if (layout == Layout::SoA) { int stride = ml->_nodecount_padded; - if (permute) { - soa2aos_inverse_permute_copy(n, sz, stride, cndat, mdata, permute); - } else { - soa2aos_unpermuted_copy(n, sz, stride, cndat, mdata); - } + soaos_copy_cnrn2nrn(n, stride, cndat, mdata, array_dims, permute); } else { /* AoS */ aos2aos_copy(n, sz, cndat, mdata); } diff --git a/src/coreneuron/io/mem_layout_util.cpp b/src/coreneuron/io/mem_layout_util.cpp index 93d5d26f08..0551e049b3 100644 --- a/src/coreneuron/io/mem_layout_util.cpp +++ b/src/coreneuron/io/mem_layout_util.cpp @@ -8,6 +8,8 @@ #include "mem_layout_util.hpp" +#include + namespace coreneuron { /// calculate size after padding for specific memory layout @@ -20,8 +22,8 @@ int nrn_soa_padded_size(int cnt, int layout) { size_t nrn_soa_byte_align(size_t size) { static_assert(NRN_SOA_BYTE_ALIGN % sizeof(double) == 0, "NRN_SOA_BYTE_ALIGN should be a multiple of sizeof(double)"); - constexpr size_t dbl_align{NRN_SOA_BYTE_ALIGN / sizeof(double)}; - size_t remainder{size % dbl_align}; + constexpr size_t dbl_align = NRN_SOA_BYTE_ALIGN / sizeof(double); + size_t remainder = size % dbl_align; if (remainder) { size += dbl_align - remainder; } @@ -34,8 +36,7 @@ int nrn_i_layout(int icnt, int cnt, int isz, int sz, int layout) { case Layout::AoS: return icnt * sz + isz; case Layout::SoA: - int padded_cnt = nrn_soa_padded_size(cnt, - layout); // may want to factor out to save time + int padded_cnt = nrn_soa_padded_size(cnt, layout); return icnt + isz * padded_cnt; } @@ -43,23 +44,45 @@ int nrn_i_layout(int icnt, int cnt, int isz, int sz, int layout) { return 0; } -// file data is AoS. ie. -// organized as cnt array instances of mtype each of size sz. -// So input index i refers to i_instance*sz + i_item offset -// Return the corresponding SoA index -- taking into account the -// alignment requirements. Ie. i_instance + i_item*align_cnt. +std::array legacy2soaos_index(int legacy_index, const std::vector& array_dims) { + int variable_count = static_cast(array_dims.size()); + int row_width = std::accumulate(array_dims.begin(), array_dims.end(), 0); -int nrn_param_layout(int i, int mtype, Memb_list* ml) { - int layout = corenrn.get_mech_data_layout()[mtype]; - switch (layout) { - case Layout::AoS: - return i; - case Layout::SoA: - nrn_assert(layout == Layout::SoA); - int sz = corenrn.get_prop_param_size()[mtype]; - return nrn_i_layout(i / sz, ml->nodecount, i % sz, sz, layout); + int column_index = legacy_index % row_width; + + int instance_index = legacy_index / row_width; + int variable_index = 0; + int prefix_sum = 0; + for (size_t k = 0; k < variable_count - 1; ++k) { + if (column_index >= prefix_sum + array_dims[k]) { + prefix_sum += array_dims[k]; + variable_index = k + 1; + } else { + break; + } } - nrn_assert(false); - return 0; + int array_index = column_index - prefix_sum; + + return {instance_index, variable_index, array_index}; } + +int soaos2cnrn_index(const std::array& soaos_indices, + const std::vector& array_dims, + int padded_node_count, + int* permute) { + auto [i, j, k] = soaos_indices; + if (permute) { + i = permute[i]; + } + + int offset = 0; + for (int ij = 0; ij < j; ++ij) { + offset += padded_node_count * array_dims[ij]; + } + + int K = array_dims[j]; + return offset + i * K + k; +} + + } // namespace coreneuron diff --git a/src/coreneuron/io/mem_layout_util.hpp b/src/coreneuron/io/mem_layout_util.hpp index a6b2606011..501c451ec7 100644 --- a/src/coreneuron/io/mem_layout_util.hpp +++ b/src/coreneuron/io/mem_layout_util.hpp @@ -8,6 +8,8 @@ #pragma once +#include + #include "coreneuron/coreneuron.hpp" #include "coreneuron/nrniv/nrniv_decl.h" @@ -27,11 +29,38 @@ size_t nrn_soa_byte_align(size_t i); /// Depending of the layout some padding can be calculated int nrn_i_layout(int icnt, int cnt, int isz, int sz, int layout); -// file data is AoS. ie. -// organized as cnt array instances of mtype each of size sz. -// So input index i refers to i_instance*sz + i_item offset -// Return the corresponding SoA index -- taking into account the -// alignment requirements. Ie. i_instance + i_item*align_cnt. +/// \brief Split a legacy index into the three SoAoS indices. +/// +/// The legacy index is the AoS of the data, i.e. all values are arranged in a +/// table. Different variables and elements of those variables for array +/// variables occupy different columns, while different instances occupy +/// different rows. +/// +/// The legacy index is then simply the elements flat (one-dimensional) index +/// in that (unpadded) row-major table. +/// +/// Given this `legacy_index` and `array_dims`, i.e. the number of array +/// elements for each variable (1 for regular variables and >= 1 for array +/// variables), compute the triplet `(i, j, k)` where `i` is the index of the +/// instance, `j` of the variable, and `k` the of array element. +std::array legacy2soaos_index(int legacy_index, const std::vector& array_dims); + +/// \brief Compute the CoreNEURON index given an SoAoS index. +/// +/// The CoreNEURON index is the index in a flat array starting from a pointer +/// pointing to the beginning of the data of that mechanism. Note, that +/// CoreNEURON pads to the number of instances. +/// +/// If `permute` is not null, then the instance index `i` is permuted. Before +/// computing before computing the flat index. +/// +/// Consecutive array elements are always stored consecutively, next fastest +/// index is over instances, and the slowest index runs over variables. Note, +/// up to padding and permutation of the instances this is the same SoAoS order +/// as in NEURON. +int soaos2cnrn_index(const std::array& soaos_indices, + const std::vector& array_dims, + int padded_node_count, + int* permute); -int nrn_param_layout(int i, int mtype, Memb_list* ml); } // namespace coreneuron diff --git a/src/coreneuron/io/mk_mech.cpp b/src/coreneuron/io/mk_mech.cpp index 294ad28b0e..3750092a7b 100644 --- a/src/coreneuron/io/mk_mech.cpp +++ b/src/coreneuron/io/mk_mech.cpp @@ -92,9 +92,16 @@ static void mk_mech(std::istream& s) { /// Read all the mechanisms and their meta data for (int i = 2; i < n; ++i) { char mname[100]; - int type = 0, pnttype = 0, is_art = 0, is_ion = 0, dsize = 0, pdsize = 0; - nrn_assert(s >> mname >> type >> pnttype >> is_art >> is_ion >> dsize >> pdsize); + int type = 0, pnttype = 0, is_art = 0, is_ion = 0, dsize = 0, pdsize = 0, vsize = 0; + nrn_assert(s >> mname >> type >> pnttype >> is_art >> is_ion >> dsize >> pdsize >> vsize); nrn_assert(i == type); + + std::vector array_dims(vsize); + for (size_t i = 0; i < vsize; ++i) { + nrn_assert(s >> array_dims[i]); + } + corenrn.get_array_dims().at(type) = array_dims; + #ifdef DEBUG printf("%s %d %d %d %d %d %d\n", mname, type, pnttype, is_art, is_ion, dsize, pdsize); #endif diff --git a/src/coreneuron/io/nrn_filehandler.hpp b/src/coreneuron/io/nrn_filehandler.hpp index 3241fd3506..672af88404 100644 --- a/src/coreneuron/io/nrn_filehandler.hpp +++ b/src/coreneuron/io/nrn_filehandler.hpp @@ -132,9 +132,7 @@ class FileHandler { std::vector lfp_factors; if (total_lfp_factors > 0) { - // ASan reports container overflow on read_array with vec.reserve, resize does work - lfp_factors.resize(nseg); - read_array(&lfp_factors[0], total_lfp_factors); + lfp_factors = read_vector(total_lfp_factors); } int factor_offset = 0; diff --git a/src/coreneuron/io/nrn_setup.cpp b/src/coreneuron/io/nrn_setup.cpp index 888f07949a..285c19034a 100644 --- a/src/coreneuron/io/nrn_setup.cpp +++ b/src/coreneuron/io/nrn_setup.cpp @@ -496,7 +496,7 @@ void nrn_setup(const char* filesdat, // gap junctions // Gaps are done after phase2, in order to use layout and permutation - // information via calls to stdindex2ptr. + // information via calls to legacy_index2pointer. if (nrn_have_gaps) { nrn_partrans::transfer_thread_data_ = new nrn_partrans::TransferThreadData[nrn_nthread]; if (!corenrn_embedded) { @@ -634,16 +634,16 @@ void read_phasegap(NrnThread& nt, UserParams& userParams) { // mech_type enum or non-artificial cell mechanisms. // take into account alignment, layout, permutation // only voltage, i_membrane_ or mechanism data index allowed. (mtype 0 means time) -double* stdindex2ptr(int mtype, int index, NrnThread& nt) { +double* legacy_index2pointer(int mtype, int index, NrnThread& nt) { if (mtype == voltage) { // voltage - int ix{index}; // relative to _actual_v + int ix = index; // relative to _actual_v nrn_assert((ix >= 0) && (ix < nt.end)); if (nt._permute) { node_permute(&ix, 1, nt._permute); } return nt._actual_v + ix; } else if (mtype == i_membrane_) { // membrane current from fast_imem calculation - int ix{index}; // relative to nrn_fast_imem->nrn_sav_rhs + int ix = index; // relative to nrn_fast_imem->nrn_sav_rhs nrn_assert((ix >= 0) && (ix < nt.end)); if (nt._permute) { node_permute(&ix, 1, nt._permute); @@ -652,15 +652,19 @@ double* stdindex2ptr(int mtype, int index, NrnThread& nt) { } else if (mtype > 0 && mtype < static_cast(corenrn.get_memb_funcs().size())) { // Memb_list* ml = nt._ml_list[mtype]; nrn_assert(ml); - int ix = nrn_param_layout(index, mtype, ml); - if (ml->_permute) { - ix = nrn_index_permute(ix, mtype, ml); - } - return ml->data + ix; + + const std::vector& array_dims = corenrn.get_array_dims()[mtype]; + int padded_node_count = nrn_soa_padded_size(ml->nodecount, Layout::SoA); + + auto soaos_index = legacy2soaos_index(index, array_dims); + auto cnrn_index = + soaos2cnrn_index(soaos_index, array_dims, padded_node_count, ml->_permute); + + return ml->data + cnrn_index; } else if (mtype == 0) { // time return &nt._t; } else { - printf("stdindex2ptr does not handle mtype=%d\n", mtype); + printf("legacy_index2pointer does not handle mtype=%d\n", mtype); nrn_assert(0); } return nullptr; diff --git a/src/coreneuron/io/phase2.cpp b/src/coreneuron/io/phase2.cpp index 0502129068..19fb12e8a3 100644 --- a/src/coreneuron/io/phase2.cpp +++ b/src/coreneuron/io/phase2.cpp @@ -87,27 +87,41 @@ int (*nrn2core_get_dat2_vecplay_inst_)(int tid, namespace coreneuron { template -inline void mech_data_layout_transform(T* data, int cnt, int sz, int layout) { +void mech_data_layout_transform(T* data, int cnt, const std::vector& array_dims, int layout) { if (layout == Layout::AoS) { - return; - } - // layout is equal to Layout::SoA - int align_cnt = nrn_soa_padded_size(cnt, layout); - std::vector d(cnt * sz); - // copy matrix - for (int i = 0; i < cnt; ++i) { - for (int j = 0; j < sz; ++j) { - d[i * sz + j] = data[i * sz + j]; - } + throw std::runtime_error("AoS memory layout not implemented."); } - // transform memory layout - for (int i = 0; i < cnt; ++i) { - for (int j = 0; j < sz; ++j) { - data[i + j * align_cnt] = d[i * sz + j]; + + int n_vars = array_dims.size(); + int row_width = std::accumulate(array_dims.begin(), array_dims.end(), 0); + int padded_cnt = nrn_soa_padded_size(cnt, layout); + + std::vector tmp(padded_cnt * row_width); + std::copy(data, data + cnt * row_width, tmp.begin()); + + size_t offset_var = 0; + for (size_t i_var = 0; i_var < n_vars; ++i_var) { + size_t K = array_dims[i_var]; + + for (size_t i = 0; i < cnt; ++i) { + for (size_t k = 0; k < K; ++k) { + size_t i_dst = padded_cnt * offset_var + i * K + k; + size_t i_src = i * row_width + offset_var + k; + data[i_dst] = tmp[i_src]; + } } + + offset_var += K; } } +template +inline void mech_data_layout_transform(T* data, int cnt, int sz, int layout) { + std::vector array_dims(sz, 1); + mech_data_layout_transform(data, cnt, array_dims, layout); +} + + void Phase2::read_file(FileHandler& F, const NrnThread& nt) { n_real_cell = F.read_int(); n_output = F.read_int(); @@ -315,8 +329,9 @@ void Phase2::read_direct(int thread_id, const NrnThread& nt) { auto& dparam_sizes = corenrn.get_prop_dparam_size(); int dsz_inst = 0; size_t offset = 6 * n_data_padded; - if (n_diam > 0) + if (n_diam > 0) { offset += n_data_padded; + } for (int i = 0; i < n_mech; ++i) { auto& tml = tmls[i]; int type = mech_types[i]; @@ -351,8 +366,9 @@ void Phase2::read_direct(int thread_id, const NrnThread& nt) { nmodlrandom, tml.pointer2type); - if (dparam_sizes[type] > 0) + if (dparam_sizes[type] > 0) { dsz_inst++; + } offset += nrn_soa_padded_size(nodecounts[i], layout) * param_sizes[type]; if (nodeindices_) { std::copy(nodeindices_, nodeindices_ + nodecounts[i], tml.nodeindices.data()); @@ -690,13 +706,17 @@ void Phase2::pdata_relocation(const NrnThread& nt, const std::vector& Memb_list* eml = nt._ml_list[etype]; int edata0 = eml->data - nt._data; int ecnt = eml->nodecount; + int ecnt_padded = nrn_soa_padded_size(ecnt, Layout::SoA); int esz = corenrn.get_prop_param_size()[etype]; + const std::vector& array_dims = corenrn.get_array_dims()[etype]; for (int iml = 0; iml < cnt; ++iml) { int* pd = pdata + nrn_i_layout(iml, cnt, i, szdp, layout); - int ix = *pd; // relative to the ion data - nrn_assert((ix >= 0) && (ix < ecnt * esz)); - /* Original pd order assumed ecnt groups of esz */ - *pd = edata0 + nrn_param_layout(ix, etype, eml); + int legacy_index = *pd; // relative to the ion data + nrn_assert((legacy_index >= 0) && (legacy_index < ecnt * esz)); + + auto soaos_index = legacy2soaos_index(legacy_index, array_dims); + *pd = edata0 + + soaos2cnrn_index(soaos_index, array_dims, ecnt_padded, nullptr); } } } @@ -720,10 +740,17 @@ void Phase2::pdata_relocation(const NrnThread& nt, const std::vector& } else { Memb_list* pml = nt._ml_list[ptype]; int pcnt = pml->nodecount; + int pcnt_padded = nrn_soa_padded_size(pcnt, Layout::SoA); int psz = corenrn.get_prop_param_size()[ptype]; + const std::vector& array_dims = + corenrn.get_array_dims()[ptype]; nrn_assert((ix >= 0) && (ix < pcnt * psz)); + int elem0 = pml->data - nt._data; - *pd = elem0 + nrn_param_layout(ix, ptype, pml); + auto soaos_index = legacy2soaos_index(ix, array_dims); + *pd = + elem0 + + soaos2cnrn_index(soaos_index, array_dims, pcnt_padded, nullptr); } } } @@ -913,19 +940,17 @@ void Phase2::set_vec_play(NrnThread& nt, NrnThreadChkpnt& ntc) { nrn_assert(vecPlay.vtype == VecPlayContinuousType); #if CHKPNTDEBUG ntc.vtype[i] = vecPlay.vtype; -#endif -#if CHKPNTDEBUG ntc.mtype[i] = vecPlay.mtype; -#endif - Memb_list* ml = nt._ml_list[vecPlay.mtype]; -#if CHKPNTDEBUG ntc.vecplay_ix[i] = vecPlay.ix; #endif + Memb_list* ml = nt._ml_list[vecPlay.mtype]; + + const std::vector& array_dims = corenrn.get_array_dims()[vecPlay.mtype]; + + auto padded_nodecount = nrn_soa_padded_size(ml->nodecount, Layout::SoA); + auto soaos_index = legacy2soaos_index(vecPlay.ix, array_dims); + vecPlay.ix = soaos2cnrn_index(soaos_index, array_dims, padded_nodecount, ml->_permute); - vecPlay.ix = nrn_param_layout(vecPlay.ix, vecPlay.mtype, ml); - if (ml->_permute) { - vecPlay.ix = nrn_index_permute(vecPlay.ix, vecPlay.mtype, ml); - } nt._vecplay[i] = new VecPlayContinuous(ml->data + vecPlay.ix, std::move(vecPlay.yvec), std::move(vecPlay.tvec), @@ -1083,12 +1108,14 @@ void Phase2::populate(NrnThread& nt, const UserParams& userParams) { int n = ml->nodecount; int szp = nrn_prop_param_size_[type]; int szdp = nrn_prop_dparam_size_[type]; + + const std::vector& array_dims = corenrn.get_array_dims()[type]; int layout = corenrn.get_mech_data_layout()[type]; ml->nodeindices = (int*) ecalloc_align(ml->nodecount, sizeof(int)); std::copy(tmls[itml].nodeindices.begin(), tmls[itml].nodeindices.end(), ml->nodeindices); - mech_data_layout_transform(ml->data, n, szp, layout); + mech_data_layout_transform(ml->data, n, array_dims, layout); if (szdp) { ml->pdata = (int*) ecalloc_align(nrn_soa_padded_size(n, layout) * szdp, sizeof(int)); diff --git a/src/coreneuron/io/reports/report_handler.cpp b/src/coreneuron/io/reports/report_handler.cpp index 0fd48d7a0a..aa01432555 100644 --- a/src/coreneuron/io/reports/report_handler.cpp +++ b/src/coreneuron/io/reports/report_handler.cpp @@ -354,24 +354,6 @@ VarsToReport ReportHandler::get_synapse_vars_to_report( return vars_to_report; } -void add_clamp_current(const char* clamp, - const NrnThread& nt, - std::unordered_map>>& currents, - int gid, - const std::vector& nodes_to_gids) { - auto mech_id = nrn_get_mechtype(clamp); - Memb_list* ml = nt._ml_list[mech_id]; - if (ml) { - for (int i = 0; i < ml->nodecount; i++) { - auto segment_id = ml->nodeindices[i]; - if ((nodes_to_gids[segment_id] == gid)) { - double* var_value = get_var_location_from_var_name(mech_id, "i", ml, i); - currents[segment_id].push_back(std::make_pair(var_value, -1)); - } - } - } -} - VarsToReport ReportHandler::get_lfp_vars_to_report(const NrnThread& nt, const std::vector& gids_to_report, ReportConfiguration& report, @@ -385,11 +367,8 @@ VarsToReport ReportHandler::get_lfp_vars_to_report(const NrnThread& nt, } auto& summation_report = nt.summation_report_handler_->summation_reports_[report.output_path]; VarsToReport vars_to_report; - off_t offset_lfp = 0; + std::size_t offset_lfp = 0; for (const auto& gid: gids_to_report) { - // IClamp & SEClamp are needed for the LFP calculation - add_clamp_current("IClamp", nt, summation_report.currents_, gid, nodes_to_gids); - add_clamp_current("SEClamp", nt, summation_report.currents_, gid, nodes_to_gids); const auto& cell_mapping = mapinfo->get_cell_mapping(gid); if (cell_mapping == nullptr) { std::cerr << "[LFP] Error : Compartment mapping information is missing for gid " << gid diff --git a/src/coreneuron/mechanism/mechanism.hpp b/src/coreneuron/mechanism/mechanism.hpp index 9427423df7..45861fb410 100644 --- a/src/coreneuron/mechanism/mechanism.hpp +++ b/src/coreneuron/mechanism/mechanism.hpp @@ -143,12 +143,12 @@ struct Memb_list { NetSendBuffer_t* _net_send_buffer = nullptr; int nodecount; /* actual node count */ int _nodecount_padded; - void* instance{nullptr}; /* mechanism instance struct */ + void* instance = nullptr; /* mechanism instance struct */ // nrn_acc_manager.cpp handles data movement to/from the accelerator as the // "private constructor" in the translated MOD file code is called before // the main nrn_acc_manager methods that copy thread/mechanism data to the // device - void* global_variables{nullptr}; - std::size_t global_variables_size{}; + void* global_variables = nullptr; + std::size_t global_variables_size = 0; }; } // namespace coreneuron diff --git a/src/coreneuron/mechanism/register_mech.cpp b/src/coreneuron/mechanism/register_mech.cpp index 9853c1243a..260d420411 100644 --- a/src/coreneuron/mechanism/register_mech.cpp +++ b/src/coreneuron/mechanism/register_mech.cpp @@ -92,6 +92,7 @@ void alloc_mech(int memb_func_size_) { corenrn.get_watch_check().resize(memb_func_size_); corenrn.get_is_artificial().resize(memb_func_size_, false); corenrn.get_artcell_qindex().resize(memb_func_size_); + corenrn.get_array_dims().resize(memb_func_size_); corenrn.get_prop_param_size().resize(memb_func_size_); corenrn.get_prop_dparam_size().resize(memb_func_size_); corenrn.get_mech_data_layout().resize(memb_func_size_, 1); diff --git a/src/coreneuron/network/partrans_setup.cpp b/src/coreneuron/network/partrans_setup.cpp index 1c1a3f9010..6d474da619 100644 --- a/src/coreneuron/network/partrans_setup.cpp +++ b/src/coreneuron/network/partrans_setup.cpp @@ -160,7 +160,7 @@ void nrn_partrans::gap_mpi_setup(int ngroup) { // Note that src_index points into NrnThread.data, as it has already // been transformed using original src_type and src_index via - // stdindex2ptr. + // legacy_index2pointer. // For copying into outsrc_buf from src_gather. This is from // NrnThread._data, fixup to "from src_gather" below. ttd.gather2outsrc_indices.push_back(si.src_index[setup_info_index]); @@ -248,13 +248,13 @@ void nrn_partrans::gap_data_indices_setup(NrnThread* n) { // For copying into src_gather from NrnThread._data for (size_t i = 0; i < sti.src_sid.size(); ++i) { - double* d = stdindex2ptr(sti.src_type[i], sti.src_index[i], nt); + double* d = legacy_index2pointer(sti.src_type[i], sti.src_index[i], nt); sti.src_index[i] = int(d - nt._data); } // For copying into NrnThread._data from insrc_buf. for (size_t i = 0; i < sti.tar_sid.size(); ++i) { - double* d = stdindex2ptr(sti.tar_type[i], sti.tar_index[i], nt); + double* d = legacy_index2pointer(sti.tar_type[i], sti.tar_index[i], nt); // todo : this should be revisited once nt._data will be broken // into mechanism specific data sti.tar_index[i] = int(d - nt._data); diff --git a/src/coreneuron/network/tnode.hpp b/src/coreneuron/network/tnode.hpp index 9f85651e7c..d6448480fa 100644 --- a/src/coreneuron/network/tnode.hpp +++ b/src/coreneuron/network/tnode.hpp @@ -11,7 +11,11 @@ #include // experiment with ordering strategies for Tree Nodes +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif class TNode; using VecTNode = std::vector; @@ -63,10 +67,10 @@ size_t level_from_root(VecTNode&); * * The main steps are the following: * 1. warp_balance function creates balanced groups of cells. - * 2. The compartments/tree nodes populate the groups vector (VVVTN) based on their groudindex and + * 2. The compartments/tree nodes populate the groups vector (VVVTN) based on their groupindex and * their level (see level_from_root). * 3. The analyze() & question2() functions (operating per group) make sure that each cell is still - * a tree (treenode_order) and that the dependent nodes belong to separate warps. + * a tree (treenode_order) and that the nodes with same parents belong to separate warps. */ void group_order2(VecTNode&, size_t groupsize, size_t ncell); size_t dist2child(TNode* nd); diff --git a/src/coreneuron/nrniv/nrniv_decl.h b/src/coreneuron/nrniv/nrniv_decl.h index 00cdaef16d..32fe24dec7 100644 --- a/src/coreneuron/nrniv/nrniv_decl.h +++ b/src/coreneuron/nrniv/nrniv_decl.h @@ -13,7 +13,7 @@ #include "coreneuron/network/netcon.hpp" namespace coreneuron { -/// Mechanism type to be used from stdindex2ptr and nrn_dblpntr2nrncore (in Neuron) +/// Mechanism type to be used from legacy_index2pointer and nrn_dblpntr2nrncore (in Neuron) /// Values of the mechanism types should be negative numbers to avoid any conflict with /// mechanism types of Memb_list(>0) or time(0) passed from Neuron enum mech_type { voltage = -1, i_membrane_ = -2 }; @@ -37,7 +37,7 @@ extern void mk_mech(const char* path); extern void set_globals(const char* path, bool cli_global_seed, int cli_global_seed_value); extern void mk_netcvode(void); extern void nrn_p_construct(void); -extern double* stdindex2ptr(int mtype, int index, NrnThread&); +extern double* legacy_index2pointer(int mtype, int index, NrnThread&); extern void delete_trajectory_requests(NrnThread&); extern void nrn_cleanup(); extern void nrn_cleanup_ion_map(); diff --git a/src/coreneuron/permute/balance.cpp b/src/coreneuron/permute/balance.cpp index 4aca6c46e3..efecf1fb14 100644 --- a/src/coreneuron/permute/balance.cpp +++ b/src/coreneuron/permute/balance.cpp @@ -15,12 +15,21 @@ // few holes in warp filling. I.e., ncycle = ncompart/warpsize #include +#include +#if CORENRN_BUILD #include "coreneuron/nrnconf.h" +#endif + #include "coreneuron/network/tnode.hpp" #include "coreneuron/utils/lpt.hpp" +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif + int cellorder_nwarp = 0; // 0 means do not balance // ordering by warp, then old order diff --git a/src/coreneuron/permute/cellorder.cpp b/src/coreneuron/permute/cellorder.cpp index 30d3143b06..d8be359409 100644 --- a/src/coreneuron/permute/cellorder.cpp +++ b/src/coreneuron/permute/cellorder.cpp @@ -5,16 +5,28 @@ # See top-level LICENSE file for details. # ============================================================================= */ +#include +#include +#if CORENRN_BUILD #include "coreneuron/nrnconf.h" #include "coreneuron/sim/multicore.hpp" #include "coreneuron/utils/nrn_assert.h" +#include "coreneuron/apps/corenrn_parameters.hpp" +#else +#include "nrnoc/multicore.h" +#include "nrnoc/nrn_ansi.h" +#include "nrnoc/nrniv_mf.h" +#include "nrnoc/section.h" +#include "oc/nrnassrt.h" +#include "node_order_optim/permute_utils.hpp" +#endif + #include "coreneuron/permute/cellorder.hpp" #include "coreneuron/network/tnode.hpp" #include "coreneuron/utils/lpt.hpp" #include "coreneuron/utils/memory.h" #include "coreneuron/utils/offload.hpp" -#include "coreneuron/apps/corenrn_parameters.hpp" #include "coreneuron/permute/node_permute.h" // for print_quality @@ -22,9 +34,11 @@ #include #endif -#include - +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif int interleave_permute_type; InterleaveInfo* interleave_info; // nrn_nthread array @@ -108,6 +122,11 @@ void destroy_interleave_info() { // more precise visualization of the warp quality // can be called after admin2 static void print_quality2(int iwarp, InterleaveInfo& ii, int* p) { + // '.' p[i] = p[i-1] + 1 (but first of cacheline is 'o') + // 'o' p[i] != p[i-1] + 1 and not a child race + // 'r' p[i] = p[i-1] + 1 and race + // 'R' p[1] != p[i-1] + 1 and race + // 'X' core is unused int pc = (iwarp == 0); // print warp 0 pc = 0; // turn off printing int nodebegin = ii.lastnode[iwarp]; @@ -116,12 +135,11 @@ static void print_quality2(int iwarp, InterleaveInfo& ii, int* p) { int inode = nodebegin; - size_t nn = 0; // number of nodes in warp. '.' - size_t nx = 0; // number of idle cores on all cycles. 'X' - size_t ncacheline = 0; - ; // number of parent memory cacheline accesses. - // assmue warpsize is max number in a cachline so all o - size_t ncr = 0; // number of child race. nchild-1 of same parent in same cycle + size_t nn = 0; // number of nodes in warp. '.oRr' + size_t nx = 0; // number of idle cores on all cycles. 'X' + size_t ncacheline = 0; // number of parent memory cacheline accesses. + // assume warpsize is max number in a cacheline so all o + size_t ncr = 0; // number of child race. nchild-1 of same parent in same cycle for (int icycle = 0; icycle < ncycle; ++icycle) { int s = stride[icycle]; @@ -285,10 +303,53 @@ static void warp_balance(int ith, InterleaveInfo& ii) { #endif } +#if !CORENRN_BUILD +static void prnode(const char* mes, NrnThread& nt) { + printf("%s nrnthread %d node info\n", mes, nt.id); + for (int i = 0; i < nt.end; ++i) { + printf( + " _v_node[%2d]->v_node_index=%2d %p" + " _v_parent[%2d]->v_node_index=%2d parent[%2d]=%2d\n", + i, + nt._v_node[i]->v_node_index, + nt._v_node[i], + i, + nt._v_parent[i] ? nt._v_parent[i]->v_node_index : -1, + i, + nt._v_parent_index[i]); + } + for (auto tml = nt.tml; tml; tml = tml->next) { + Memb_list* ml = tml->ml; + printf(" %s %d\n", memb_func[tml->index].sym->name, ml->nodecount); + for (int i = 0; i < ml->nodecount; ++i) { + printf(" %2d ndindex=%2d nd=%p [%2d] pdata=%p prop=%p\n", + i, + ml->nodeindices[i], + ml->nodelist[i], + ml->nodelist[i]->v_node_index, + ml->pdata[i], + ml->prop[i]); + } + } +} + +int nrn_optimize_node_order(int type) { + if (type != interleave_permute_type) { + tree_changed = 1; // calls setup_topology. v_stucture_change = 1 may be better. + } + interleave_permute_type = type; + return type; +} +#endif // !CORENRN_BUILD + +#if CORENRN_BUILD int* interleave_order(int ith, int ncell, int nnode, int* parent) { +#else +std::vector interleave_order(int ith, int ncell, int nnode, int* parent) { +#endif // return if there are no nodes to permute if (nnode <= 0) - return nullptr; + return {}; // ensure parent of root = -1 for (int i = 0; i < ncell; ++i) { @@ -300,7 +361,7 @@ int* interleave_order(int ith, int ncell, int nnode, int* parent) { int nwarp = 0, nstride = 0, *stride = nullptr, *firstnode = nullptr; int *lastnode = nullptr, *cellsize = nullptr, *stridedispl = nullptr; - int* order = node_order( + auto order = node_order( ncell, nnode, parent, nwarp, nstride, stride, firstnode, lastnode, cellsize, stridedispl); if (interleave_info) { @@ -327,12 +388,21 @@ int* interleave_order(int ith, int ncell, int nnode, int* parent) { } if (ith == 0) { // needed for print_quality[12] and done once here to save time +#if CORENRN_BUILD int* p = new int[nnode]; +#else + std::vector p(nnode); +#endif for (int i = 0; i < nnode; ++i) { p[i] = parent[i]; } +#if CORENRN_BUILD permute_ptr(p, nnode, order); node_permute(p, nnode, order); +#else + forward_permute(p, order); + update_parent_index(p.data(), p.size(), order); +#endif ii.nnode = new size_t[nwarp]; ii.ncycle = new size_t[nwarp]; @@ -341,13 +411,23 @@ int* interleave_order(int ith, int ncell, int nnode, int* parent) { ii.child_race = new size_t[nwarp]; for (int i = 0; i < nwarp; ++i) { if (interleave_permute_type == 1) { +#if CORENRN_BUILD print_quality1(i, interleave_info[ith], ncell, p); +#else + print_quality1(i, interleave_info[ith], ncell, p.data()); +#endif } if (interleave_permute_type == 2) { +#if CORENRN_BUILD print_quality2(i, interleave_info[ith], p); +#else + print_quality2(i, interleave_info[ith], p.data()); +#endif } } +#if CORENRN_BUILD delete[] p; +#endif warp_balance(ith, interleave_info[ith]); } } @@ -355,6 +435,46 @@ int* interleave_order(int ith, int ncell, int nnode, int* parent) { return order; } +#if !CORENRN_BUILD +void nrn_permute_node_order() { + if (!interleave_permute_type) { + return; + } + // printf("enter nrn_permute_node_order\n"); + destroy_interleave_info(); + create_interleave_info(); + for (int tid = 0; tid < nrn_nthread; ++tid) { + auto& nt = nrn_threads[tid]; + auto perm = interleave_order(tid, nt.ncell, nt.end, nt._v_parent_index); + auto p = inverse_permute_vector(perm); +#if 0 + for (int i = 0; i < nt.end; ++i) { + int x = nt._v_parent_index[p[i]]; + int par = x >= 0 ? perm[x] : -1; + printf("%2d <- %2d parent=%2d\n", i, p[i], par); + } +#endif + // prnode("before perm", nt); + forward_permute(nt._v_node, nt.end, p); + forward_permute(nt._v_parent, nt.end, p); + forward_permute(nt._v_parent_index, nt.end, p); + update_parent_index(nt._v_parent_index, nt.end, perm); + for (int i = 0; i < nt.end; ++i) { + nt._v_node[i]->v_node_index = i; + } + for (auto tml = nt.tml; tml; tml = tml->next) { + Memb_list* ml = tml->ml; + for (int i = 0; i < ml->nodecount; ++i) { + ml->nodeindices[i] = perm[ml->nodeindices[i]]; + } + sort_ml(ml); // all fields in increasing nodeindex order + } + // prnode("after perm", nt); + } + // printf("leave nrn_permute_node_order\n"); +} +#endif // !CORENRN_BUILD + #if INTERLEAVE_DEBUG // only the cell per core style static int** cell_indices_debug(NrnThread& nt, InterleaveInfo& ii) { int ncell = nt.ncell; @@ -422,11 +542,19 @@ void mk_cell_indices() { } #endif // INTERLEAVE_DEBUG -#define GPU_V(i) nt->_actual_v[i] -#define GPU_A(i) nt->_actual_a[i] -#define GPU_B(i) nt->_actual_b[i] -#define GPU_D(i) nt->_actual_d[i] -#define GPU_RHS(i) nt->_actual_rhs[i] +#if CORENRN_BUILD +#define GPU_V(i) nt->_actual_v[i] +#define GPU_A(i) nt->_actual_a[i] +#define GPU_B(i) nt->_actual_b[i] +#define GPU_D(i) nt->_actual_d[i] +#define GPU_RHS(i) nt->_actual_rhs[i] +#else +#define GPU_V(i) vec_v[i] +#define GPU_A(i) vec_a[i] +#define GPU_B(i) vec_b[i] +#define GPU_D(i) vec_d[i] +#define GPU_RHS(i) vec_rhs[i] +#endif #define GPU_PARENT(i) nt->_v_parent_index[i] // How does the interleaved permutation with stride get used in @@ -439,6 +567,12 @@ static void triang_interleaved(NrnThread* nt, int nstride, int* stride, int* lastnode) { +#if !CORENRN_BUILD + auto* const vec_a = nt->node_a_storage(); + auto* const vec_b = nt->node_b_storage(); + auto* const vec_d = nt->node_d_storage(); + auto* const vec_rhs = nt->node_rhs_storage(); +#endif int i = lastnode[icell]; for (int istride = nstride - 1; istride >= 0; --istride) { if (istride < icellsize) { // only first icellsize strides matter @@ -462,6 +596,12 @@ static void bksub_interleaved(NrnThread* nt, int /* nstride */, int* stride, int* firstnode) { +#if !CORENRN_BUILD + auto* const vec_a = nt->node_a_storage(); + auto* const vec_b = nt->node_b_storage(); + auto* const vec_d = nt->node_d_storage(); + auto* const vec_rhs = nt->node_rhs_storage(); +#endif int i = firstnode[icell]; GPU_RHS(icell) /= GPU_D(icell); // the root for (int istride = 0; istride < icellsize; ++istride) { @@ -483,6 +623,12 @@ static void solve_interleaved2_loop_body(NrnThread* nt, int* stridedispl, int* rootbegin, int* nodebegin) { +#if !CORENRN_BUILD + auto* const vec_a = nt->node_a_storage(); + auto* const vec_b = nt->node_b_storage(); + auto* const vec_d = nt->node_d_storage(); + auto* const vec_rhs = nt->node_rhs_storage(); +#endif int iwarp = icore / warpsize; // figure out the >> value int ic = icore & (warpsize - 1); // figure out the & mask int ncycle = ncycles[iwarp]; @@ -564,7 +710,7 @@ static void solve_interleaved2_loop_body(NrnThread* nt, /** * \brief Solve Hines matrices/cells with compartment-based granularity. * - * The node ordering/permuation guarantees cell interleaving (as much coalesced memory access as + * The node ordering/permutation guarantees cell interleaving (as much coalesced memory access as * possible) and balanced warps (through the use of lpt algorithm to define the groups/warps). Every * warp deals with a group of cells, therefore multiple compartments (finer level of parallelism). */ @@ -593,7 +739,12 @@ void solve_interleaved2(int ith) { int nstride = stridedispl[nwarp]; #endif // nvc++/22.3 does not respect an if clause inside nrn_pragma_omp... +#if CORENRN_BUILD if (nt->compute_gpu) { +#else + // bad clang-format + if (0) { // nt->compute_gpu) { +#endif /* If we compare this loop with the one from cellorder.cu (CUDA version), we will * understand that the parallelism here is exposed in steps, while in the CUDA version * all the parallelism is exposed from the very beginning of the loop. In more details, diff --git a/src/coreneuron/permute/cellorder.hpp b/src/coreneuron/permute/cellorder.hpp index fe2f2f84e5..353d9dd077 100644 --- a/src/coreneuron/permute/cellorder.hpp +++ b/src/coreneuron/permute/cellorder.hpp @@ -10,7 +10,13 @@ #include "coreneuron/utils/memory.h" #include +#include + +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif /** * \brief Function that performs the permutation of the cells such that the @@ -23,11 +29,16 @@ namespace coreneuron { * * \return int* order, interleaved order of the cells */ +#if CORENRN_BUILD int* interleave_order(int ith, int ncell, int nnode, int* parent); +#else +std::vector interleave_order(int ith, int ncell, int nnode, int* parent); +#endif void create_interleave_info(); void destroy_interleave_info(); +#if CORENRN_BUILD /** * * \brief Solve the Hines matrices based on the interleave_permute_type (1 or 2). @@ -37,8 +48,10 @@ void destroy_interleave_info(); * is solved by multiple execution threads (with coalesced memory access as well) */ extern void solve_interleaved(int ith); +#endif class InterleaveInfo; // forward declaration +#if CORENRN_BUILD /** * * \brief CUDA branch of the solve_interleaved with interleave_permute_type == 2. @@ -46,6 +59,7 @@ class InterleaveInfo; // forward declaration * This branch is activated in runtime with the --cuda-interface CLI flag */ void solve_interleaved2_launcher(NrnThread* nt, InterleaveInfo* info, int ncore, void* stream); +#endif class InterleaveInfo: public MemoryManaged { public: @@ -95,7 +109,11 @@ class InterleaveInfo: public MemoryManaged { * \param stridedispl * \return int* : a permutation of length nnode */ +#if CORENRN_BUILD int* node_order(int ncell, +#else +std::vector node_order(int ncell, +#endif int nnode, int* parents, int& nwarp, diff --git a/src/coreneuron/permute/cellorder1.cpp b/src/coreneuron/permute/cellorder1.cpp index d016d96666..4c18226f52 100644 --- a/src/coreneuron/permute/cellorder1.cpp +++ b/src/coreneuron/permute/cellorder1.cpp @@ -12,16 +12,34 @@ #include #include +#if CORENRN_BUILD #include "coreneuron/utils/nrn_assert.h" +#else +#include "oc/nrnassrt.h" +#endif + #include "coreneuron/permute/cellorder.hpp" #include "coreneuron/network/tnode.hpp" // just for interleave_permute_type +#if CORENRN_BUILD #include "coreneuron/nrniv/nrniv_decl.h" #include "coreneuron/utils/memory.h" +#else +#include "node_order_optim/node_order_optim.h" +#endif +#if !CORENRN_BUILD && NRN_DEBUG +#undef CORENRN_DEBUG +#define CORENRN_DEBUG NRN_DEBUG +#endif +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif + static size_t groupsize = 32; /** @@ -127,7 +145,7 @@ static void quality(VecTNode& nodevec, size_t max = 32) { size_t ip = nodevec[i]->parent->nodevec_index; // i%max == 0 means that if we start a warp with 8 and then have 32 // the 32 is broken into 24 and 8. (modify if the arrangement during - // gaussian elimination becomes more sophisticated.( + // gaussian elimination becomes more sophisticated.) if (ip == ip_last + 1 && i % max != 0) { // contiguous qcnt += 1; } else { @@ -297,7 +315,11 @@ static void ident_statistic(VecTNode& nodevec, size_t ncell) { } #undef MSS +#if CORENRN_BUILD int* node_order(int ncell, +#else +std::vector node_order(int ncell, +#endif int nnode, int* parent, int& nwarp, @@ -339,7 +361,11 @@ int* node_order(int ncell, quality(nodevec); // the permutation +#if CORENRN_BUILD int* nodeorder = new int[nnode]; +#else + std::vector nodeorder(nnode); +#endif for (int i = 0; i < nnode; ++i) { TNode& nd = *nodevec[i]; nodeorder[nd.nodeindex] = i; diff --git a/src/coreneuron/permute/cellorder2.cpp b/src/coreneuron/permute/cellorder2.cpp index b70958a189..5d696ee628 100644 --- a/src/coreneuron/permute/cellorder2.cpp +++ b/src/coreneuron/permute/cellorder2.cpp @@ -13,15 +13,26 @@ #include #include +#if CORENRN_BUILD #include "coreneuron/utils/nrn_assert.h" +#include "coreneuron/nrniv/nrniv_decl.h" +#else +#include "oc/nrnassrt.h" +#endif + #include "coreneuron/permute/cellorder.hpp" #include "coreneuron/network/tnode.hpp" -#include "coreneuron/nrniv/nrniv_decl.h" + // experiment starting with identical cell ordering // groupindex aleady defined that keeps identical cells together // begin with leaf to root ordering +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif + using VTN = VecTNode; // level of nodes using VVTN = std::vector; // group of levels using VVVTN = std::vector; // groups diff --git a/src/coreneuron/permute/node_permute.cpp b/src/coreneuron/permute/node_permute.cpp index 6ef211b267..a19230b4fc 100644 --- a/src/coreneuron/permute/node_permute.cpp +++ b/src/coreneuron/permute/node_permute.cpp @@ -88,12 +88,37 @@ so pdata_m(k, isz) = inew + data_t #include #include +#if CORENRN_BUILD #include "coreneuron/sim/multicore.hpp" #include "coreneuron/io/nrn_setup.hpp" #include "coreneuron/nrniv/nrniv_decl.h" #include "coreneuron/utils/nrn_assert.h" #include "coreneuron/coreneuron.hpp" +#else +#include "nrnoc/multicore.h" +#include "oc/nrnassrt.h" +#include "node_order_optim/permute_utils.hpp" +#endif + +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif + +#if !CORENRN_BUILD +static int nrn_soa_padded_size(int cnt, int layout) { + assert(layout == 1); + return cnt; +} +static int nrn_i_layout(int icnt, int cnt, int isz, int sz, int layout) { + assert(isz == 0); + assert(sz == 1); + assert(layout == 1); + return icnt; +} +#endif // !CORENRN_BUILD + template void permute(T* data, int cnt, int sz, int layout, int* p) { // data(p[icnt], isz) <- data(icnt, isz) @@ -107,9 +132,11 @@ void permute(T* data, int cnt, int sz, int layout, int* p) { return; } +#if CORENRN_BUILD if (layout == Layout::SoA) { // for SoA, n might be larger due to cnt padding n = nrn_soa_padded_size(cnt, layout) * sz; } +#endif T* data_orig = new T[n]; for (int i = 0; i < n; ++i) { @@ -157,6 +184,7 @@ static void invert_permute(int* p, int n) { // full_search: helper for type_of_ntdata. Return mech type for nt._data[i]. // Update type_hints. +#if CORENRN_BUILD static std::vector type_hints; static int full_search(NrnThread& nt, double* pd) { @@ -208,7 +236,9 @@ int type_of_ntdata(NrnThread& nt, int i, bool reset) { // after the last type_hints return full_search(nt, pd); } +#endif // CORENRN_BUILD +#if CORENRN_BUILD static void update_pdata_values(Memb_list* ml, int type, NrnThread& nt) { // assumes AoS to SoA transformation already made since we are using // nrn_i_layout to determine indices into both ml->pdata and into target data @@ -342,6 +372,18 @@ void node_permute(int* vec, int n, int* permute) { } } +#else // not CORENRN_BUILD + +void update_parent_index(int* vec, int vec_size, const std::vector& permute) { + for (int i = 0; i < vec_size; ++i) { + if (vec[i] >= 0) { + vec[i] = permute[vec[i]]; + } + } +} + +#endif // not CORENRN_BUILD + void permute_ptr(int* vec, int n, int* p) { permute(vec, n, 1, 1, p); } @@ -350,6 +392,7 @@ void permute_data(double* vec, int n, int* p) { permute(vec, n, 1, 1, p); } +#if CORENRN_BUILD void permute_ml(Memb_list* ml, int type, NrnThread& nt) { int sz = corenrn.get_prop_param_size()[type]; int psz = corenrn.get_prop_dparam_size()[type]; @@ -359,26 +402,7 @@ void permute_ml(Memb_list* ml, int type, NrnThread& nt) { update_pdata_values(ml, type, nt); } - -int nrn_index_permute(int ix, int type, Memb_list* ml) { - int* p = ml->_permute; - if (!p) { - return ix; - } - int layout = corenrn.get_mech_data_layout()[type]; - if (layout == Layout::AoS) { - int sz = corenrn.get_prop_param_size()[type]; - int i_cnt = ix / sz; - int i_sz = ix % sz; - return p[i_cnt] * sz + i_sz; - } else { - assert(layout == Layout::SoA); - int padded_cnt = nrn_soa_padded_size(ml->nodecount, layout); - int i_cnt = ix % padded_cnt; - int i_sz = ix / padded_cnt; - return i_sz * padded_cnt + p[i_cnt]; - } -} +#endif // CORENRN_BUILD #if CORENRN_DEBUG static void pr(const char* s, int* x, int n) { @@ -414,20 +438,39 @@ static bool nrn_index_sort_cmp(const std::pair& a, const std::pair nrn_index_sort(int* values, int n) { +#endif std::vector> vi(n); for (int i = 0; i < n; ++i) { vi[i].first = values[i]; vi[i].second = i; } std::sort(vi.begin(), vi.end(), nrn_index_sort_cmp); +#if CORENRN_BUILD int* sort_indices = new int[n]; +#else + std::vector sort_indices(n); +#endif for (int i = 0; i < n; ++i) { sort_indices[i] = vi[i].second; } return sort_indices; } +#if !CORENRN_BUILD +void sort_ml(Memb_list* ml) { + auto isrt = nrn_index_sort(ml->nodeindices, ml->nodecount); + forward_permute(ml->nodeindices, ml->nodecount, isrt); + forward_permute(ml->nodelist, ml->nodecount, isrt); + forward_permute(ml->prop, ml->nodecount, isrt); + forward_permute(ml->pdata, ml->nodecount, isrt); +} +#endif // !CORENRN_BUILD + +#if CORENRN_BUILD void permute_nodeindices(Memb_list* ml, int* p) { // nodeindices values are permuted according to p (that per se does // not affect vec). @@ -444,4 +487,6 @@ void permute_nodeindices(Memb_list* ml, int* p) { invert_permute(ml->_permute, ml->nodecount); permute_ptr(ml->nodeindices, ml->nodecount, ml->_permute); } +#endif // CORENRN_BUILD + } // namespace coreneuron diff --git a/src/coreneuron/permute/node_permute.h b/src/coreneuron/permute/node_permute.h index d453e656ab..fb08c6b000 100644 --- a/src/coreneuron/permute/node_permute.h +++ b/src/coreneuron/permute/node_permute.h @@ -8,21 +8,40 @@ #pragma once +#if CORENRN_BUILD #include "coreneuron/sim/multicore.hpp" +#else +#include "nrnoc/multicore.h" +#endif +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif // determine ml->_permute and permute the ml->nodeindices accordingly void permute_nodeindices(Memb_list* ml, int* permute); +#if CORENRN_BUILD // vec values >= 0 updated according to permutation void node_permute(int* vec, int n, int* permute); +#else // not CORENRN_BUILD +// sort ml fields in increasing node index order +void sort_ml(Memb_list* ml); + +/* + * Update indices vector so that the parent-child relation is kept valid + * after applying a permutation to the vector `vec`. + * This is done by updating vec values >= 0 according to the permutation. + */ +void update_parent_index(int* vec, int vec_size, const std::vector& permute); +#endif // not CORENRN_BUILD // moves values to new location but does not change those values void permute_ptr(int* vec, int n, int* permute); void permute_data(double* vec, int n, int* permute); void permute_ml(Memb_list* ml, int type, NrnThread& nt); -int nrn_index_permute(int, int type, Memb_list* ml); int* inverse_permute(int* p, int n); diff --git a/src/coreneuron/utils/lpt.cpp b/src/coreneuron/utils/lpt.cpp index 78c95b6294..775b634589 100644 --- a/src/coreneuron/utils/lpt.cpp +++ b/src/coreneuron/utils/lpt.cpp @@ -11,9 +11,14 @@ #include #include +#if CORENRN_BUILD #include "coreneuron/nrnconf.h" // for size_t -#include "coreneuron/utils/lpt.hpp" #include "coreneuron/utils/nrn_assert.h" +#else +#include "oc/nrnassrt.h" +#endif + +#include "coreneuron/utils/lpt.hpp" using P = std::pair; diff --git a/src/coreneuron/utils/memory.h b/src/coreneuron/utils/memory.h index 81a4ce3006..e455c8aa87 100644 --- a/src/coreneuron/utils/memory.h +++ b/src/coreneuron/utils/memory.h @@ -13,8 +13,12 @@ #include #include +#if CORENRN_BUILD #include "coreneuron/utils/nrn_assert.h" #include "coreneuron/nrniv/nrniv_decl.h" +#else +#include "oc/nrnassrt.h" +#endif #if !defined(NRN_SOA_BYTE_ALIGN) // for layout 0, every range variable array must be aligned by at least 16 bytes (the size of the @@ -22,7 +26,11 @@ #define NRN_SOA_BYTE_ALIGN (8 * sizeof(double)) #endif +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif /** * @brief Check if GPU support is enabled. * @@ -183,7 +191,14 @@ inline void alloc_memory(void*& pointer, size_t num_bytes, size_t alignment) { size_t multiple = num_bytes / alignment; fill = alignment * (multiple + 1) - num_bytes; } +#ifndef _WIN32 nrn_assert((pointer = std::aligned_alloc(alignment, num_bytes + fill)) != nullptr); +#else // is _WIN32 + // Windows has _aligned_alloc, but that must be paired with + // _aligned_free + fprintf(stderr, "Windows has no std::aligned_alloc\n"); + nrn_assert((pointer = std::malloc(num_bytes)) != nullptr); +#endif // is _WIN32 } else { nrn_assert((pointer = std::malloc(num_bytes)) != nullptr); } @@ -200,21 +215,26 @@ inline void free_memory(void* pointer) { #endif +#if CORENRN_BUILD namespace coreneuron { +#else +namespace neuron { +#endif /** Independent function to compute the needed chunkding, the chunk argument is the number of doubles the chunk is chunkded upon. */ template inline int soa_padded_size(int cnt, int layout) { - int imod = cnt % chunk; - if (layout == Layout::AoS) +#if CORENRN_BUILD + if (layout == Layout::AoS) { return cnt; - if (imod) { - int idiv = cnt / chunk; - return (idiv + 1) * chunk; + } else { + return ((cnt + chunk - 1) / chunk) * chunk; } - return cnt; +#else + return ((cnt + chunk - 1) / chunk) * chunk; +#endif } /** Check for the pointer alignment. diff --git a/src/coreneuron/utils/nrnoc_aux.cpp b/src/coreneuron/utils/nrnoc_aux.cpp index 6d51dbe1f6..d9ba40fd1a 100644 --- a/src/coreneuron/utils/nrnoc_aux.cpp +++ b/src/coreneuron/utils/nrnoc_aux.cpp @@ -21,7 +21,7 @@ int v_structure_change; int diam_changed; #define MAXERRCOUNT 5 int hoc_errno_count; -const char* bbcore_write_version = "1.7"; // NMODLRandom +const char* bbcore_write_version = "1.8"; // Include ArrayDims char* pnt_name(Point_process* pnt) { return corenrn.get_memb_func(pnt->_type).sym; diff --git a/src/gnu/neuron_gnu_std.h b/src/gnu/neuron_gnu_std.h deleted file mode 100755 index 26ffb73449..0000000000 --- a/src/gnu/neuron_gnu_std.h +++ /dev/null @@ -1,29 +0,0 @@ -// This may look like C code, but it is really -*- C++ -*- -/* -Copyright (C) 1988, 1992 Free Software Foundation - written by Doug Lea (dl@rocky.oswego.edu) - -This file is part of the GNU C++ Library. This library is free -software; you can redistribute it and/or modify it under the terms of -the GNU Library General Public License as published by the Free -Software Foundation; either version 2 of the License, or (at your -option) any later version. This library is distributed in the hope -that it will be useful, but WITHOUT ANY WARRANTY; without even the -implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU Library General Public License for more details. -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - - -#pragma once -#include -#include -#include -#ifndef WIN32 -#include -#endif -#include -#include -#include diff --git a/src/ivoc/classreg.cpp b/src/ivoc/classreg.cpp deleted file mode 100644 index d5e8c81776..0000000000 --- a/src/ivoc/classreg.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include <../../nrnconf.h> -// interface c++ class to oc - -#include -#include -#include "classreg.h" -#ifndef OC_CLASSES -#define OC_CLASSES "occlass.h" -#endif - -#define EXTERNS 1 -extern void -#include OC_CLASSES - ; - -#undef EXTERNS -static void (*register_classes[])() = { -#include OC_CLASSES - , - 0}; - -void hoc_class_registration(void) { - for (int i = 0; register_classes[i]; i++) { - (*register_classes[i])(); - } -} - -/*-----------------------------------------------------*/ -#if 0 -//example - -//the class - -/*static*/ class A { -public: - A(); - ~A(); - void f1(char* s); - double f2(double x); -private: - double x_; -}; - -A::A() { printf("A::A\n"); x_=1; } -A::~A() { printf("A::~A\n"); } -void A::f1(char* s) { printf("A::f1(\"%s\")\n", s);} -double A::f2(double x) { double a = x_; x_=x; return a; } - -//the interface - -static void* A_constructor(Object*) { - printf("A::constructor\n"); - return (void*) new A; -} - -static void A_destructor(void* v) { - delete (A*)v; -} - -static double A_f1(void* v) { - ((A*)v)->f1(gargstr(1)); - return(0.); -} - -static double A_f2(void* v) { - double x = ((A*)v)->f2(*getarg(1)); - return(x); -} - -// class registration to oc - -static Member_func A_member_func[] = { - "f1", A_f1, - "f2", A_f2, - 0, 0 -}; - -void A_reg() { - class2oc("A", A_constructor, A_destructor, A_member_func); -} - -#endif diff --git a/src/ivoc/datapath.cpp b/src/ivoc/datapath.cpp deleted file mode 100644 index c4da677e77..0000000000 --- a/src/ivoc/datapath.cpp +++ /dev/null @@ -1,484 +0,0 @@ -#include <../../nrnconf.h> -#include -#include -#include -#include "hoclist.h" -#if HAVE_IV -#include "graph.h" -#endif -#include "datapath.h" -#include "ivocvect.h" - -#include "nrnoc2iv.h" -#include "membfunc.h" - -#include "parse.hpp" -extern Symlist* hoc_built_in_symlist; -extern Symlist* hoc_top_level_symlist; -extern Objectdata* hoc_top_level_data; - -/*static*/ class PathValue { - public: - PathValue(); - ~PathValue() = default; - std::string path{}; - Symbol* sym; - double original; - char* str; -}; -PathValue::PathValue() { - str = NULL; - sym = NULL; -} - -class HocDataPathImpl { - private: - friend class HocDataPaths; - HocDataPathImpl(int, int); - ~HocDataPathImpl(); - - void search(); - void found(double*, const char*, Symbol*); - void found(char**, const char*, Symbol*); - PathValue* found_v(void*, const char*, Symbol*); - - void search(Objectdata*, Symlist*); - void search_vectors(); - void search_pysec(); - void search(Section*); - void search(Node*, double x); - void search(Point_process*, Symbol*); - void search(Prop*, double x); - - private: - std::map table_; - std::vector strlist_; - int size_, count_, found_so_far_; - int pathstyle_; -}; - -#define sentinal 123456789.e15 - -static Symbol *sym_vec, *sym_v, *sym_vext, *sym_rallbranch, *sym_L, *sym_Ra; - -HocDataPaths::HocDataPaths(int size, int pathstyle) { - if (!sym_vec) { - sym_vec = hoc_table_lookup("Vector", hoc_built_in_symlist); - sym_v = hoc_table_lookup("v", hoc_built_in_symlist); - sym_vext = hoc_table_lookup("vext", hoc_built_in_symlist); - sym_rallbranch = hoc_table_lookup("rallbranch", hoc_built_in_symlist); - sym_L = hoc_table_lookup("L", hoc_built_in_symlist); - sym_Ra = hoc_table_lookup("Ra", hoc_built_in_symlist); - } - impl_ = new HocDataPathImpl(size, pathstyle); -} - -HocDataPaths::~HocDataPaths() { - delete impl_; -} - -int HocDataPaths::style() { - return impl_->pathstyle_; -} - -void HocDataPaths::append(double* pd) { - // printf("HocDataPaths::append\n"); - if (pd && impl_->table_.find((void*) pd) == impl_->table_.end()) { - impl_->table_.emplace((void*) pd, new PathValue); - ++impl_->count_; - } -} - -void HocDataPaths::search() { - // printf("HocDataPaths::search\n"); - impl_->search(); - if (impl_->count_ > impl_->found_so_far_) { - // printf("HocDataPaths:: didn't find paths to all the pointers\n"); - // this has proved to be too often a false alarm since most panels are - // in boxes controlled by objects which have no reference but are - // deleted when the window is closed - } -} - -std::string HocDataPaths::retrieve(double* pd) const { - assert(impl_->pathstyle_ != 2); - // printf("HocDataPaths::retrieve\n"); - auto const it = impl_->table_.find(pd); - if (it != impl_->table_.end()) { - return it->second->path; - } - return {}; -} - -Symbol* HocDataPaths::retrieve_sym(double* pd) const { - // printf("HocDataPaths::retrieve\n"); - auto const it = impl_->table_.find(pd); - if (it != impl_->table_.end()) { - return it->second->sym; - } - return nullptr; -} - -void HocDataPaths::append(char** pd) { - // printf("HocDataPaths::append\n"); - if (*pd && impl_->table_.find((void*) pd) == impl_->table_.end()) { - PathValue* pv = new PathValue; - pv->str = *pd; - impl_->table_.emplace((void*) pd, pv); - ++impl_->count_; - } -} - -std::string HocDataPaths::retrieve(char** pd) const { - // printf("HocDataPaths::retrieve\n"); - auto const it = impl_->table_.find(pd); - if (it != impl_->table_.end()) { - return it->second->path; - } - return {}; -} - -/*------------------------------*/ -HocDataPathImpl::HocDataPathImpl(int size, int pathstyle) { - pathstyle_ = pathstyle; - size_ = size; - count_ = 0; - found_so_far_ = 0; -} - -HocDataPathImpl::~HocDataPathImpl() { - for (auto& kv: table_) { - PathValue* pv = kv.second; - delete pv; - } -} - -void HocDataPathImpl::search() { - found_so_far_ = 0; - for (auto& it: table_) { - PathValue* pv = it.second; - if (pv->str) { - char** pstr = (char**) it.first; - *pstr = nullptr; - } else { - double* pd = (double*) it.first; - pv->original = *pd; - *pd = sentinal; - } - } - if (pathstyle_ > 0) { - search(hoc_top_level_data, hoc_built_in_symlist); - search(hoc_top_level_data, hoc_top_level_symlist); - } else { - search(hoc_top_level_data, hoc_top_level_symlist); - search(hoc_top_level_data, hoc_built_in_symlist); - } - if (found_so_far_ < count_) { - search_pysec(); - } - if (found_so_far_ < count_) { - search_vectors(); - } - for (auto& it: table_) { - PathValue* pv = it.second; - if (pv->str) { - char** pstr = (char**) it.first; - *pstr = pv->str; - } else { - double* pd = (double*) it.first; - *pd = pv->original; - } - } -} - -PathValue* HocDataPathImpl::found_v(void* v, const char* buf, Symbol* sym) { - PathValue* pv; - if (pathstyle_ != 2) { - char path[500]; - std::string cs{}; - for (const auto& str: strlist_) { - Sprintf(path, "%s%s.", cs.c_str(), str.c_str()); - cs = path; - } - Sprintf(path, "%s%s", cs.c_str(), buf); - const auto& it = table_.find(v); - if (it == table_.end()) { - hoc_warning("table lookup failed for pointer for-", path); - return nullptr; - } - pv = it->second; - if (pv->path.empty()) { - pv->path = path; - pv->sym = sym; - ++found_so_far_; - } - // printf("HocDataPathImpl::found %s\n", path); - } else { - const auto& it = table_.find(v); - if (it == table_.end()) { - hoc_warning("table lookup failed for pointer for-", sym->name); - return nullptr; - } - pv = it->second; - if (!pv->sym) { - pv->sym = sym; - ++found_so_far_; - } - } - return pv; -} - -void HocDataPathImpl::found(double* pd, const char* buf, Symbol* sym) { - PathValue* pv = found_v((void*) pd, buf, sym); - if (pv) { - *pd = pv->original; - } -} - -void HocDataPathImpl::found(char** pstr, const char* buf, Symbol* sym) { - PathValue* pv = found_v((void*) pstr, buf, sym); - if (pv) { - *pstr = pv->str; - } else { - hoc_assign_str(pstr, "couldn't find"); - } -} - -void HocDataPathImpl::search(Objectdata* od, Symlist* sl) { - Symbol* sym; - int i, total; - char buf[200]; - std::string cs{}; - if (sl) - for (sym = sl->first; sym; sym = sym->next) { - if (sym->cpublic != 2) { - switch (sym->type) { - case VAR: { - double* pd; - if (sym->subtype == NOTUSER) { - pd = object_pval(sym, od); - total = hoc_total_array_data(sym, od); - } else if (sym->subtype == USERDOUBLE) { - pd = sym->u.pval; - total = 1; - } else { - break; - } - for (i = 0; i < total; ++i) { - if (pd[i] == sentinal) { - Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); - cs = buf; - found(pd + i, cs.c_str(), sym); - } - } - } break; - case STRING: { - char** pstr = object_pstr(sym, od); - if (*pstr == NULL) { - Sprintf(buf, "%s", sym->name); - cs = buf; - found(pstr, cs.c_str(), sym); - } - } break; - case OBJECTVAR: { - if (pathstyle_ > 0) { - break; - } - Object** obp = object_pobj(sym, od); - total = hoc_total_array_data(sym, od); - for (i = 0; i < total; ++i) - if (obp[i] && !obp[i]->recurse) { - cTemplate* t = (obp[i])->ctemplate; - if (!t->constructor) { - // not the this pointer - if (obp[i]->u.dataspace != od) { - Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); - cs = buf; - strlist_.push_back(cs); - obp[i]->recurse = 1; - search(obp[i]->u.dataspace, obp[i]->ctemplate->symtable); - obp[i]->recurse = 0; - strlist_.pop_back(); - } - } else { - /* point processes */ - if (t->is_point_) { - Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); - cs = buf; - strlist_.push_back(cs); - search((Point_process*) obp[i]->u.this_pointer, sym); - strlist_.pop_back(); - } - /* seclists, object lists */ - } - } - } break; - case SECTION: { - total = hoc_total_array_data(sym, od); - for (i = 0; i < total; ++i) { - hoc_Item** pitm = object_psecitm(sym, od); - if (pitm[i]) { - Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); - cs = buf; - strlist_.push_back(cs); - search(hocSEC(pitm[i])); - strlist_.pop_back(); - } - } - } break; - case TEMPLATE: { - cTemplate* t = sym->u.ctemplate; - hoc_Item* q; - ITERATE(q, t->olist) { - Object* obj = OBJ(q); - Sprintf(buf, "%s[%d]", sym->name, obj->index); - cs = buf; - strlist_.push_back(cs); - if (!t->constructor) { - search(obj->u.dataspace, t->symtable); - } else { - if (t->is_point_) { - search((Point_process*) obj->u.this_pointer, sym); - } - } - strlist_.pop_back(); - } - } break; - } - } - } -} - -void HocDataPathImpl::search_vectors() { - char buf[200]; - std::string cs{}; - cTemplate* t = sym_vec->u.ctemplate; - hoc_Item* q; - ITERATE(q, t->olist) { - Object* obj = OBJ(q); - Sprintf(buf, "%s[%d]", sym_vec->name, obj->index); - cs = buf; - strlist_.push_back(cs); - Vect* vec = (Vect*) obj->u.this_pointer; - int size = vec->size(); - double* pd = vector_vec(vec); - for (size_t i = 0; i < size; ++i) { - if (pd[i] == sentinal) { - Sprintf(buf, "x[%zu]", i); - found(pd + i, buf, sym_vec); - } - } - strlist_.pop_back(); - } -} - -void HocDataPathImpl::search_pysec() { -#if USE_PYTHON - std::string cs{}; - hoc_Item* qsec; - // ForAllSections(sec) - ITERATE(qsec, section_list) { - Section* sec = hocSEC(qsec); - if (sec->prop && sec->prop->dparam[PROP_PY_INDEX].get()) { - cs = secname(sec); - strlist_.push_back(cs); - search(sec); - strlist_.pop_back(); - } - } -#endif -} - -void HocDataPathImpl::search(Section* sec) { - if (sec->prop->dparam[2].get() == sentinal) { - found(&(sec->prop->dparam[2].literal_value()), "L", sym_L); - } - if (sec->prop->dparam[4].get() == sentinal) { - found(&(sec->prop->dparam[4].literal_value()), "rallbranch", sym_rallbranch); - } - if (sec->prop->dparam[7].get() == sentinal) { - found(&(sec->prop->dparam[7].literal_value()), "Ra", sym_Ra); - } - if (!sec->parentsec && sec->parentnode) { - search(sec->parentnode, sec->prop->dparam[1].get()); - } - for (int i = 0; i < sec->nnode; ++i) { - search(sec->pnode[i], nrn_arc_position(sec, sec->pnode[i])); - } -} -void HocDataPathImpl::search(Node* nd, double x) { - char buf[100]; - if (NODEV(nd) == sentinal) { - Sprintf(buf, "v(%g)", x); - // the conversion below yields a pointer that is potentially invalidated - // by almost any Node operation - found(static_cast(nd->v_handle()), buf, sym_v); - } - -#if EXTRACELLULAR - if (nd->extnode) { - int i; - for (i = 0; i < nlayer; ++i) { - if (nd->extnode->v[i] == sentinal) { - if (i == 0) { - Sprintf(buf, "vext(%g)", x); - } else { - Sprintf(buf, "vext[%d](%g)", i, x); - } - found(&(nd->extnode->v[i]), buf, sym_vext); - } - } - } -#endif - - Prop* p; - for (p = nd->prop; p; p = p->next) { - if (!memb_func[p->_type].is_point) { - search(p, x); - } - } -} - -void HocDataPathImpl::search(Point_process* pp, Symbol*) { - if (pp->prop) { - search(pp->prop, -1); - } -} - -void HocDataPathImpl::search(Prop* prop, double x) { - char buf[200]; - int type = prop->_type; - Symbol* sym = memb_func[type].sym; - Symbol* psym; - double* pd; - int i, imax, k = 0, ir, kmax = sym->s_varn; - - for (k = 0; k < kmax; ++k) { - psym = sym->u.ppsym[k]; - if (psym->subtype == NRNPOINTER) { - continue; - } - ir = psym->u.rng.index; - if (memb_func[type].hoc_mech) { - pd = prop->ob->u.dataspace[ir].pval; - } else { - if (type == EXTRACELL && ir == neuron::extracellular::vext_pseudoindex()) { - // skip as it was handled by caller - continue; - } else { - pd = static_cast(prop->param_handle_legacy(ir)); - } - } - imax = hoc_total_array_data(psym, 0); - for (i = 0; i < imax; ++i) { - if (pd[i] == sentinal) { - if (x < 0) { - Sprintf(buf, "%s%s", psym->name, hoc_araystr(psym, i, 0)); - } else { - Sprintf(buf, "%s%s(%g)", psym->name, hoc_araystr(psym, i, 0), x); - } - found(pd + i, buf, psym); - } - } - } -} diff --git a/src/ivoc/ivocmac.cpp b/src/ivoc/ivocmac.cpp deleted file mode 100644 index 0ebc0345d4..0000000000 --- a/src/ivoc/ivocmac.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include <../../nrnconf.h> - -#include - - -#include -#include "apwindow.h" -#include "ivoc.h" -#include "rubband.h" -#include "symdir.h" -#include "oc2iv.h" -#include "graph.h" -#include -#include -#include -#include -#include - -typedef void (*NrnBBSCallback)(const char*); - - -char* mktemp(char*) { - return NULL; -} - -bool nrnbbs_connect() { - return false; -} -void nrnbbs_disconnect() {} -bool nrnbbs_connected() { - return false; -} - -void nrnbbs_post(const char*) {} -void nrnbbs_post_int(const char*, int) {} -void nrnbbs_post_string(const char*, const char*) {} - -bool nrnbbs_take(const char*) { - return false; -} -bool nrnbbs_take_int(const char*, int*) { - return false; -} -bool nrnbbs_take_string(const char*, char*) { - return false; -} - -bool nrnbbs_look(const char*) { - return false; -} - -void nrnbbs_exec(const char*) {} - -void nrnbbs_notify(const char*, NrnBBSCallback) {} - -void nrnbbs_wait(bool* pflag = (bool*) 0) {} - -bool is_mac_dll(FSSpec*); -bool mac_open_dll(const char*, FSSpec*); - -bool is_mac_dll(FSSpec* fs) { - FInfo finfo; - FSpGetFInfo(fs, &finfo); - return finfo.fdType == 'shlb'; -} - -extern Symlist *hoc_symlist, *hoc_built_in_symlist; -extern OSErr __path2fss(const char* name, FSSpec*); -extern void hoc_nrn_load_dll(); - -static long fsspec2id(FSSpec* fs) { - CInfoPBRec ci; - Str255 name; - int i; - for (i = 0; i < 64; ++i) { - name[i] = fs->name[i]; - } - ci.hFileInfo.ioCompletion = 0; - ci.hFileInfo.ioNamePtr = name; - ci.hFileInfo.ioVRefNum = fs->vRefNum; - ci.hFileInfo.ioDirID = fs->parID; - ci.hFileInfo.ioFDirIndex = 0; - OSErr err = PBGetCatInfo(&ci, false); - return ci.hFileInfo.ioDirID; -} -static int ndll; -static long dllid[10]; - -bool mac_open_dll(const char* name, FSSpec* fs) { - CFragConnectionID id; - Ptr mainaddr; - Str255 sname; - int j; - long fid = fsspec2id(fs); - for (j = 0; j < ndll; ++j) { - if (dllid[j] == fid) { - printf("%s DLL already loaded\n", name); - return false; - } - } - dllid[ndll] = fid; - ndll = (ndll < 10) ? ndll + 1 : 10; - printf("Loading DLL %s\n", name); -#if 1 - OSErr myErr = GetDiskFragment(fs, 0, kCFragGoesToEOF, 0, kLoadCFrag, &id, &mainaddr, sname); - if (myErr) { - sname[sname[0] + 1] = '\0'; - printf("dll load error %d\n%s\n", myErr, sname + 1); - return false; - } - // printf("successfully loaded %s\n", name); - // printf("mainaddr=%p\n", mainaddr); - mainaddr = NULL; - long cnt; - myErr = CountSymbols(id, &cnt); - // printf("symbols exported = %d\n", cnt); - for (long i = 0; i < cnt; ++i) { - Ptr symaddr; - CFragSymbolClass symclass; - myErr = GetIndSymbol(id, i, sname, &symaddr, &symclass); - sname[sname[0] + 1] = '\0'; - if (strcmp((sname + 1), "main") == 0) { - mainaddr = symaddr; - } - // printf("symbol %d name %s\n", i, sname+1); - } -// printf("mainaddr=%p\n", mainaddr); -#if 1 - if (mainaddr) { - Symlist* sav = hoc_symlist; - hoc_symlist = hoc_built_in_symlist; - hoc_built_in_symlist = NULL; - (*(Pfri) mainaddr)(); - hoc_built_in_symlist = hoc_symlist; - hoc_symlist = sav; - return true; - } -#endif -#endif - return false; -} - -bool mac_load_dll(const char* name) { - FSSpec fss; - if ((__path2fss(name, &fss) != fnfErr) && is_mac_dll(&fss)) { - return mac_open_dll(name, &fss); - } - return false; -} - -void hoc_nrn_load_dll() { - int b = mac_load_dll(expand_env_var(gargstr(1))); - ret((double) b); -} - -void pwmimpl_redraw(Window* w) { - w->rep()->MACpaint(); -} - -void ivoc_bring_to_top(Window* w) { - BringToFront(w->rep()->macWindow()); -} - -//--------------------------------------------------------- - - -void Oc::cleanup() { - if (help_cursor_) { - delete help_cursor_; - } -} - -void PrintableWindow::hide() { - unmap(); -} - -void PrintableWindow::xmove(int x, int y) { - WindowPtr theWin = Window::rep()->macWindow(); - MoveWindow(theWin, (x + 1), (y + 17), true); -} -int PrintableWindow::xleft() const { - WindowRep& w = *Window::rep(); - if (w.bound()) { - GrafPtr oldPort; - GetPort(&oldPort); - WindowPtr theWin = w.macWindow(); - w.setport(); - Point upperLeft; - upperLeft.h = theWin->portRect.left; - upperLeft.v = theWin->portRect.top; - LocalToGlobal(&upperLeft); - SetPort(oldPort); - return upperLeft.h - 1; - } else { - return 0; - } -} -int PrintableWindow::xtop() const { - WindowRep& w = *Window::rep(); - if (w.bound()) { - GrafPtr oldPort; - GetPort(&oldPort); - WindowPtr theWin = w.macWindow(); - w.setport(); - Point upperLeft; - upperLeft.h = theWin->portRect.left; - upperLeft.v = theWin->portRect.top; - LocalToGlobal(&upperLeft); - SetPort(oldPort); - return upperLeft.v - 17; - } else { - return 0; - } -} - -void PrintableWindow::xplace(int x, int y) { - WindowRep& wr = *Window::rep(); - // printf("xplace %d %d %d\n", x, y,wr.bound()); - if (wr.bound()) { - xmove(x, y); - } else { - xplace_ = true; - xleft_ = x + 1; - xtop_ = y + 17; - } -} -void PrintableWindow::default_geometry() { - DismissableWindow::default_geometry(); - // the problem is that at this point the canvas size is not necessarly correct. - if (xplace_) { - pplace(xleft_, display()->pheight() - xtop_ - canvas()->pheight()); - } -} - -// following analogy to MACwindow::MACpaint -static CGrafPtr cg_; -static GDHandle gd_; - -void Rubberband::rubber_on(Canvas* c) { - // printf("Rubberband::rubber_on\n"); - // c->front_buffer(); - GetGWorld(&cg_, &gd_); - WindowPtr mw = c->window()->rep()->macWindow(); - SetGWorld((CGrafPort*) mw, GetMainDevice()); -} -void Rubberband::rubber_off(Canvas* c) { - // c->back_buffer(); - SetGWorld(cg_, gd_); - // printf("Rubberband::rubber_off\n"); -} - -IOHandler::IOHandler() {} -IOHandler::~IOHandler() {} -int IOHandler::inputReady(int) { - return 0; -} -int IOHandler::outputReady(int) { - return 0; -} -int IOHandler::exceptionRaised(int) { - return 0; -} -void IOHandler::timerExpired(long, long) {} -void IOHandler::childStatus(pid_t, int) {} diff --git a/src/ivoc/ivocvect.cpp b/src/ivoc/ivocvect.cpp index 866fab6836..cf72e1c69b 100644 --- a/src/ivoc/ivocvect.cpp +++ b/src/ivoc/ivocvect.cpp @@ -1,6 +1,5 @@ #include <../../nrnconf.h> -//#include #include #include #include @@ -24,15 +23,10 @@ #include #include #include -//#include #include #endif -#if defined(SVR4) -extern void exit(int status); -#endif - #include "classreg.h" #if HAVE_IV #include "apwindow.h" @@ -48,24 +42,12 @@ extern void exit(int status); #endif #define PI M_PI #endif -#define BrainDamaged 0 // The Sun CC compiler but it doesn't hurt to leave it in -#if BrainDamaged -#define FWrite(arg1, arg2, arg3, arg4) \ - if (fwrite((arg1), arg2, arg3, arg4) != arg3) { \ - hoc_execerror("fwrite error", 0); \ - } -#define FRead(arg1, arg2, arg3, arg4) \ - if (fread((arg1), arg2, arg3, arg4) != arg3) { \ - hoc_execerror("fread error", 0); \ - } -#else #define FWrite(arg1, arg2, arg3, arg4) \ if (fwrite(arg1, arg2, arg3, arg4) != arg3) { \ } #define FRead(arg1, arg2, arg3, arg4) \ if (fread(arg1, arg2, arg3, arg4) != arg3) { \ } -#endif /** * As all parameters are passed from hoc as double, we need diff --git a/src/ivoc/macmain.cpp b/src/ivoc/macmain.cpp deleted file mode 100644 index f944cf6cf3..0000000000 --- a/src/ivoc/macmain.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma import on -int ivocmain(int, const char**, const char**); -#pragma import off - -int main() { - return ivocmain(0, 0, 0); -} diff --git a/src/ivoc/occlass.h b/src/ivoc/occlass.h deleted file mode 100644 index bd6836ae8d..0000000000 --- a/src/ivoc/occlass.h +++ /dev/null @@ -1,25 +0,0 @@ - -#if EXTERNS -Graph_reg(), HBox_reg(), VBox_reg(), GUIMath_reg(), PWManager_reg(), GrGlyph_reg(), - ValueFieldEditor_reg(), -#if HAVE_IV - TextEditor_reg(), -#endif - OcTimer_reg(), OcDeck_reg(), SymChooser_reg(), StringFunctions_reg(), OcList_reg(), - Vector_reg(), OcPtrVector_reg(), OcFile_reg(), OcPointer_reg(), -#ifdef USEMATRIX - Matrix_reg(), -#endif - Random_reg() -#else -Graph_reg, HBox_reg, VBox_reg, GUIMath_reg, PWManager_reg, GrGlyph_reg, ValueFieldEditor_reg, -#if HAVE_IV - TextEditor_reg, -#endif - OcTimer_reg, OcDeck_reg, SymChooser_reg, StringFunctions_reg, OcList_reg, Vector_reg, - OcPtrVector_reg, OcFile_reg, OcPointer_reg, -#ifdef USEMATRIX - Matrix_reg, -#endif - Random_reg -#endif diff --git a/src/ivoc/ocjump.cpp b/src/ivoc/ocjump.cpp deleted file mode 100644 index 91712f5ec8..0000000000 --- a/src/ivoc/ocjump.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include <../../nrnconf.h> - -#include "nrnfilewrap.h" -#include "nrnoc2iv.h" -#include "ocfunc.h" -#include "ocjump.h" -#if HAVE_IV -#include "ivoc.h" -#endif - -#include - -#include - -extern Objectdata* hoc_top_level_data; -extern Symlist* hoc_top_level_symlist; -extern Symlist* hoc_symlist; -extern Object* hoc_thisobject; -extern int hoc_execerror_messages; - -bool hoc_valid_stmt(const char* stmt, Object* ob) { - std::string s{stmt}; - s.append(1, '\n'); - return OcJump::execute(s.c_str(), ob); -} - -void hoc_execute1() { - Object* ob{}; - int hem{1}; - if (ifarg(2)) { - if (hoc_is_object_arg(2)) { - ob = *hoc_objgetarg(2); - if (ifarg(3)) { - hem = chkarg(3, 0., 1.); - } - } else { - hem = chkarg(2, 0., 1.); - } - } - - auto const hemold = std::exchange(hoc_execerror_messages, hem); - auto const old_mpiabort_flag = std::exchange(nrn_mpiabort_on_error_, 0); - bool const b = hoc_valid_stmt(hoc_gargstr(1), ob); - nrn_mpiabort_on_error_ = old_mpiabort_flag; - hoc_execerror_messages = hemold; - hoc_retpushx(b); -} - -#if HAVE_IV -bool Oc::valid_expr(Symbol* s) { - return OcJump::execute(s->u.u_proc->defn.in); -} - -bool Oc::valid_stmt(const char* stmt, Object* ob) { - return hoc_valid_stmt(stmt, ob); -} -#endif -//------------------------------------------------------------------ -void hoc_execute(Inst*); - -namespace { -struct saved_state { - saved_state() { - // not complete but it is good for expressions and it can be improved - oc_save_hoc_oop(&o1, &o2, &o4, &o5); - oc_save_code(&c1, &c2, c3, &c4, &c5, &c6, &c7, &c8, c9, &c10, &c11, &c12); - oc_save_input_info(&i1, &i2, &i3, &i4); - oc_save_cabcode(&cc1, &cc2); - } - void restore() { - oc_restore_hoc_oop(&o1, &o2, &o4, &o5); - oc_restore_code(&c1, &c2, c3, &c4, &c5, &c6, &c7, &c8, c9, &c10, &c11, &c12); - oc_restore_input_info(i1, i2, i3, i4); - oc_restore_cabcode(&cc1, &cc2); - } - - private: - // hoc_oop - Object* o1{}; - Objectdata* o2{}; - int o4{}; - Symlist* o5{}; - - // code - Inst* c1{}; - Inst* c2{}; - std::size_t c3{}; - nrn::oc::frame* c4{}; - int c5{}; - int c6{}; - Inst* c7{}; - nrn::oc::frame* c8{}; - std::size_t c9{}; - Symlist* c10{}; - Inst* c11{}; - int c12{}; - - // input_info - const char* i1{}; - int i2{}; - int i3{}; - NrnFILEWrap* i4{}; - - // cabcode - int cc1{}; - int cc2{}; -}; -} // namespace - -bool OcJump::execute(Inst* p) { - saved_state before{}; - try_catch_depth_increment tell_children_we_will_catch{}; - try { - hoc_execute(p); - return true; - } catch (...) { - before.restore(); - return false; - } -} - -bool OcJump::execute(const char* stmt, Object* ob) { - saved_state before{}; - try_catch_depth_increment tell_children_we_will_catch{}; - try { - hoc_obj_run(stmt, ob); - return true; - } catch (...) { - before.restore(); - return false; - } -} - -void* OcJump::fpycall(void* (*f)(void*, void*), void* a, void* b) { - saved_state before{}; - try_catch_depth_increment tell_children_we_will_catch{}; - try { - return (*f)(a, b); - } catch (...) { - before.restore(); - throw; - } -} - -ObjectContext::ObjectContext(Object* obj) { - oc_save_hoc_oop(&a1, &a2, &a4, &a5); - hoc_thisobject = obj; - if (obj) { - hoc_objectdata = obj->u.dataspace; - hoc_symlist = obj->ctemplate->symtable; - } else { - hoc_objectdata = hoc_top_level_data; - hoc_symlist = hoc_top_level_symlist; - } -} - -ObjectContext::~ObjectContext() { - oc_restore_hoc_oop(&a1, &a2, &a4, &a5); -} diff --git a/src/ivoc/symdir.cpp b/src/ivoc/symdir.cpp deleted file mode 100644 index 3a2de98cdf..0000000000 --- a/src/ivoc/symdir.cpp +++ /dev/null @@ -1,572 +0,0 @@ -#include <../../nrnconf.h> -#include -#include -#include -#include "ocobserv.h" -#include "utils/enumerate.h" - -#include "nrniv_mf.h" -#include "nrnoc2iv.h" - -#include "membfunc.h" -#include "parse.hpp" -#include "hoclist.h" -extern Symlist* hoc_symlist; -extern Objectdata* hoc_top_level_data; -extern Symlist *hoc_built_in_symlist, *hoc_top_level_symlist; -#include "string.h" -#include "symdir.h" - -#include "nrnsymdiritem.h" - -const char* concat(const char* s1, const char* s2) { - static char* tmp = 0; - int l1 = strlen(s1); - int l2 = strlen(s2); - if (tmp) { - delete[] tmp; - } - tmp = new char[l1 + l2 + 1]; - std::snprintf(tmp, l1 + l2 + 1, "%s%s", s1, s2); - return (const char*) tmp; -} - -class SymDirectoryImpl: public Observer { - public: - void disconnect(Observable*); // watching an object - void update(Observable*); // watching a template - private: - friend class SymDirectory; - Section* sec_; - Object* obj_; - cTemplate* t_; - - std::vector symbol_lists_; - std::string path_; - - void load(int type); - void load(int type, Symlist*); - // void load(Symbol*); - void load_section(); - void load_object(); - void load_aliases(); - void load_template(); - void load_mechanism(const Prop*, int, const char*); - void append(Symbol* sym, Objectdata* od, Object* o = NULL); - void append(Object*); - void un_append(Object*); - void make_pathname(const char*, const char*, const char*, int s = '.'); - void sort(); -}; - -static int compare_entries(const SymbolItem* e1, const SymbolItem* e2) { - int i = strcmp(e1->name().c_str(), e2->name().c_str()); - if (i == 0) { - return e1->array_index() > e2->array_index(); - } - return i > 0; -}; - -void SymDirectoryImpl::sort() { - std::sort(symbol_lists_.begin(), symbol_lists_.end(), compare_entries); -} - -// SymDirectory -SymDirectory::SymDirectory(const std::string& parent_path, - Object* parent_obj, - Symbol* sym, - int array_index, - int) { - impl_ = new SymDirectoryImpl(); - impl_->sec_ = NULL; - impl_->obj_ = NULL; - impl_->t_ = NULL; - Objectdata* obd; - if (parent_obj) { - obd = parent_obj->u.dataspace; - } else { - // obd = hoc_objectdata; - obd = hoc_top_level_data; - } - int suffix = '.'; - if (sym->type == TEMPLATE) { - suffix = '_'; - } - impl_->make_pathname(parent_path.c_str(), - sym->name, - hoc_araystr(sym, array_index, obd), - suffix); - switch (sym->type) { - case SECTION: - if (object_psecitm(sym, obd)[array_index]) { - impl_->sec_ = hocSEC(object_psecitm(sym, obd)[array_index]); - section_ref(impl_->sec_); - impl_->load_section(); - } - break; - case OBJECTVAR: - impl_->obj_ = object_pobj(sym, obd)[array_index]; - if (impl_->obj_) { - ObjObservable::Attach(impl_->obj_, impl_); - impl_->load_object(); - } - break; - case TEMPLATE: - impl_->t_ = sym->u.ctemplate; - ClassObservable::Attach(impl_->t_, impl_); - impl_->load_template(); - break; - case OBJECTALIAS: - impl_->obj_ = sym->u.object_; - if (impl_->obj_) { - ObjObservable::Attach(impl_->obj_, impl_); - impl_->load_object(); - } - break; - default: - hoc_execerror("Don't know how to make a directory out of", path().c_str()); - break; - } - impl_->sort(); -} -SymDirectory::SymDirectory(Object* ob) { - impl_ = new SymDirectoryImpl(); - impl_->sec_ = NULL; - impl_->obj_ = ob; - impl_->t_ = NULL; - int suffix = '.'; - impl_->make_pathname("", hoc_object_name(ob), "", '.'); - ObjObservable::Attach(impl_->obj_, impl_); - impl_->load_object(); - impl_->sort(); -} - -bool SymDirectory::is_pysec(int index) const { - SymbolItem* si = impl_->symbol_lists_.at(index); - return si->pysec_ ? true : false; -} -SymDirectory* SymDirectory::newsymdir(int index) { - SymbolItem* si = impl_->symbol_lists_.at(index); - SymDirectory* d = new SymDirectory(); - if (si->pysec_type_ == PYSECOBJ) { - nrn_symdir_load_pysec(d->impl_->symbol_lists_, si->pysec_); - } else { - d->impl_->sec_ = (Section*) si->pysec_; - section_ref(d->impl_->sec_); - d->impl_->load_section(); - } - d->impl_->path_ = concat(path().c_str(), si->name().c_str()); - d->impl_->path_ = concat(d->impl_->path_.c_str(), "."); - d->impl_->sort(); - return d; -} - -SymDirectory::SymDirectory() { - impl_ = new SymDirectoryImpl(); - impl_->sec_ = NULL; - impl_->obj_ = NULL; - impl_->t_ = NULL; -} - -SymDirectory::SymDirectory(int type) { - ParseTopLevel ptl; - ptl.save(); - impl_ = new SymDirectoryImpl(); - impl_->sec_ = NULL; - impl_->obj_ = NULL; - impl_->t_ = NULL; - impl_->path_ = ""; - impl_->load(type); - impl_->sort(); - ptl.restore(); -} - -SymDirectory::~SymDirectory() { - for (auto& item: impl_->symbol_lists_) { - delete item; - } - impl_->symbol_lists_.clear(); - impl_->symbol_lists_.shrink_to_fit(); - if (impl_->obj_) { - ObjObservable::Detach(impl_->obj_, impl_); - } - if (impl_->t_) { - ClassObservable::Detach(impl_->t_, impl_); - } - if (impl_->sec_) { - section_unref(impl_->sec_); - } - delete impl_; -} -void SymDirectoryImpl::disconnect(Observable*) { - for (auto& item: symbol_lists_) { - delete item; - } - symbol_lists_.clear(); - symbol_lists_.shrink_to_fit(); - obj_ = NULL; -} - -void SymDirectoryImpl::update(Observable* obs) { - if (t_) { // watching a template - ClassObservable* co = (ClassObservable*) obs; - Object* ob = co->object(); - switch (co->message()) { - case ClassObservable::Delete: - un_append(ob); - break; - case ClassObservable::Create: - append(ob); - break; - } - } -} -double* SymDirectory::variable(int index) { - Object* ob = object(); - Symbol* sym = symbol(index); - // printf("::variable index=%d sym=%s ob=%s\n", index, - // sym?sym->name:"SYM0",hoc_object_name(ob)); - if (sym) - switch (sym->type) { - case VAR: - if (ob && ob->ctemplate->constructor) { - extern double* ivoc_vector_ptr(Object*, int); - if (is_obj_type(ob, "Vector")) { - return ivoc_vector_ptr(ob, index); - } else { - return NULL; - } - } else { - Objectdata* od; - if (ob) { - od = ob->u.dataspace; - } else if (sym->subtype == USERDOUBLE) { - return sym->u.pval + array_index(index); - } else { - od = hoc_objectdata; - } - return od[sym->u.oboff].pval + array_index(index); - } - case RANGEVAR: - if (ob && ob->ctemplate->is_point_) { - return static_cast(point_process_pointer( - (Point_process*) ob->u.this_pointer, sym, array_index(index))); - } - break; - } - else { - char buf[256], *cp; - Sprintf(buf, "%s%s", path().c_str(), name(index).c_str()); - if (whole_vector(index)) { // rangevar case for [all] - // replace [all] with [0] - cp = strstr(buf, "[all]"); - assert(cp); - *(++cp) = '0'; - for (++cp; cp[2]; ++cp) { - *cp = cp[2]; - } - *cp = '\0'; - } - return hoc_val_pointer(buf); - } - return NULL; -} - -int SymDirectory::whole_vector(int index) { - return impl_->symbol_lists_.at(index)->whole_vector(); -} - -const std::string& SymDirectory::path() const { - return impl_->path_; -} -int SymDirectory::count() const { - return impl_->symbol_lists_.size(); -} -const std::string& SymDirectory::name(int index) const { - return impl_->symbol_lists_.at(index)->name(); -} -int SymDirectory::array_index(int i) const { - return impl_->symbol_lists_.at(i)->array_index(); -} - -int SymDirectory::index(const std::string& name) const { - for (const auto&& [i, symbol]: enumerate(impl_->symbol_lists_)) { - if (name == symbol->name()) { - return i; - } - } - return -1; -} -void SymDirectory::whole_name(int index, std::string& s) const { - auto s1 = impl_->path_; - auto s2 = name(index); - s = s1 + s2; -} -bool SymDirectory::is_directory(int index) const { - return impl_->symbol_lists_.at(index)->is_directory(); -} -bool SymDirectory::match(const std::string&, const std::string&) { - return true; -} -Symbol* SymDirectory::symbol(int index) const { - return impl_->symbol_lists_.at(index)->symbol(); -} -Object* SymDirectory::object() const { - return impl_->obj_; -} - -Object* SymDirectory::obj(int index) { - return impl_->symbol_lists_.at(index)->object(); -} - -// SymbolItem -SymbolItem::SymbolItem(const char* n, int whole_array) { - symbol_ = NULL; - index_ = 0; - ob_ = NULL; - name_ = n; - whole_array_ = whole_array; - pysec_type_ = 0; - pysec_ = NULL; -} -SymbolItem::SymbolItem(Symbol* sym, Objectdata* od, int index, int whole_array) { - symbol_ = sym; - ob_ = NULL; - whole_array_ = whole_array; - if (ISARRAY(sym)) { - if (whole_array_) { - name_ = concat(sym->name, "[all]"); - } else { - if (od) { - name_ = concat(sym->name, hoc_araystr(sym, index, od)); - } else { - char buf[50]; - Sprintf(buf, "[%d]", index); - name_ = concat(sym->name, buf); - } - } - } else { - name_ = sym->name; - } - index_ = index; - pysec_type_ = 0; - pysec_ = NULL; -} - -int SymbolItem::whole_vector() { - return whole_array_; -} - -SymbolItem::SymbolItem(Object* ob) { - symbol_ = NULL; - index_ = 0; - ob_ = ob; - char buf[10]; - Sprintf(buf, "%d", ob->index); - name_ = buf; - pysec_type_ = 0; - pysec_ = NULL; -} - -void SymbolItem::no_object() { - ob_ = NULL; - name_ = "Deleted"; -} - -SymbolItem::~SymbolItem() {} - -bool SymbolItem::is_directory() const { - if (symbol_) - switch (symbol_->type) { - case SECTION: - case OBJECTVAR: - case TEMPLATE: - case OBJECTALIAS: - // case SECTIONLIST: - // case MECHANISM: - return true; - } - if (ob_) { - return true; - } - if (pysec_) { - return true; - } - return false; -} - -void SymDirectoryImpl::make_pathname(const char* parent, - const char* name, - const char* index, - int suffix) { - char buf[200]; - Sprintf(buf, "%s%s%s%c", parent, name, index, suffix); - path_ = buf; -} - - -void SymDirectoryImpl::load(int type) { - switch (type) { - case TEMPLATE: - load(type, hoc_built_in_symlist); - load(type, hoc_top_level_symlist); - break; - case RANGEVAR: - load(type, hoc_built_in_symlist); - break; - case PYSEC: - path_ = "_pysec."; - nrn_symdir_load_pysec(symbol_lists_, NULL); - break; - default: - load(type, hoc_symlist); - if (hoc_symlist != hoc_built_in_symlist) { - Objectdata* sav = hoc_objectdata; - hoc_objectdata = NULL; - load(type, hoc_built_in_symlist); - hoc_objectdata = sav; - } - if (hoc_symlist != hoc_top_level_symlist) { - load(type, hoc_top_level_symlist); - } - } -} - -void SymDirectoryImpl::load(int type, Symlist* sl) { - for (Symbol* sym = sl->first; sym; sym = sym->next) { - if (type == -1) { - switch (sym->type) { - case SECTION: - case OBJECTVAR: - case VAR: - case TEMPLATE: - append(sym, hoc_objectdata); - } - } else if (sym->type == type) { - append(sym, hoc_objectdata); - } - } -} - -void SymDirectoryImpl::load_object() { - Symlist* sl = obj_->ctemplate->symtable; - Objectdata* od; - if (obj_->ctemplate->constructor) { - od = NULL; - } else { - od = obj_->u.dataspace; - } - if (obj_->aliases) { - load_aliases(); - } - if (sl) - for (Symbol* s = sl->first; s; s = s->next) { - if (s->cpublic) { - append(s, od, obj_); - } - } -} - -void SymDirectoryImpl::load_aliases() { - IvocAliases* a = (IvocAliases*) obj_->aliases; - if (!a) - return; - for (const auto& [_, s]: a->symtab_) { - append(s, nullptr, obj_); - } -} - -void SymDirectoryImpl::load_template() { - hoc_Item* q; - ITERATE(q, t_->olist) { - append(OBJ(q)); - } -} - -void SymDirectoryImpl::load_section() { - char xarg[20]; - char buf[100]; - Section* sec = sec_; - int n = sec->nnode; - - int i = 0; - double x = nrn_arc_position(sec, sec->pnode[0]); - Sprintf(xarg, "( %g )", x); - Sprintf(buf, "v%s", xarg); - symbol_lists_.push_back(new SymbolItem(buf)); - nrn_pushsec(sec); - Node* nd = sec->pnode[i]; - for (const Prop* p = nd->prop; p; p = p->next) { - load_mechanism(p, 0, xarg); - } - nrn_popsec(); -} - -void SymDirectoryImpl::load_mechanism(const Prop* p, int vartype, const char* xarg) { - int type = p->_type; - if (memb_func[type].is_point) { - return; - } - char buf[200]; - Symbol* msym = memb_func[type].sym; - int cnt = msym->s_varn; - for (int i = 0; i < cnt; ++i) { - const Symbol* sym = msym->u.ppsym[i]; - if (nrn_vartype(sym) == vartype || vartype == 0) { - if (ISARRAY(sym)) { - int n = hoc_total_array_data(sym, 0); - if (n > 5) { - Sprintf(buf, "%s[all]%s", sym->name, xarg); - symbol_lists_.push_back(new SymbolItem(buf, n)); - } - Sprintf(buf, "%s[%d]%s", sym->name, 0, xarg); - symbol_lists_.push_back(new SymbolItem(buf)); - Sprintf(buf, "%s[%d]%s", sym->name, n - 1, xarg); - symbol_lists_.push_back(new SymbolItem(buf)); - } else { - Sprintf(buf, "%s%s", sym->name, xarg); - symbol_lists_.push_back(new SymbolItem(buf)); - } - } - } -} - -void SymDirectoryImpl::append(Symbol* sym, Objectdata* od, Object* o) { - if (ISARRAY(sym)) { - int i, n = 1; - if (od) { - n = hoc_total_array_data(sym, od); - } else { // Vector - if (is_obj_type(o, "Vector")) { - extern int ivoc_vector_size(Object*); - n = ivoc_vector_size(o); - } - } - if (n > 5 && sym->type == VAR) { - symbol_lists_.push_back(new SymbolItem(sym, od, 0, n)); - } - for (i = 0; i < n; ++i) { - symbol_lists_.push_back(new SymbolItem(sym, od, i)); - if (i > 5) { - break; - } - } - if (i < n - 1) { - symbol_lists_.push_back(new SymbolItem(sym, od, n - 1)); - } - } else { - symbol_lists_.push_back(new SymbolItem(sym, od, 0)); - } -} - -void SymDirectoryImpl::append(Object* ob) { - symbol_lists_.push_back(new SymbolItem(ob)); -} -void SymDirectoryImpl::un_append(Object* ob) { - for (auto& symbol: symbol_lists_) { - if (symbol->object() == ob) { - symbol->no_object(); - break; - } - } -} diff --git a/src/modlunit/consist.cpp b/src/modlunit/consist.cpp index 6e2623a970..a89b5c63ec 100644 --- a/src/modlunit/consist.cpp +++ b/src/modlunit/consist.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/consist.c,v 1.2 1995/09/05 20:24:01 hines Exp */ /* diff --git a/src/modlunit/declare.cpp b/src/modlunit/declare.cpp index 148ba093a4..29a0fe6d3c 100644 --- a/src/modlunit/declare.cpp +++ b/src/modlunit/declare.cpp @@ -1,11 +1,9 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include +#include #include "model.h" #include "parse1.hpp" #include "symbol.h" -#ifdef HAVE_STRINGS_H -#include -#endif Symbol* indepsym; /* mathematical independent variable */ Item** scop_indep; /* the scop swept information */ @@ -195,15 +193,10 @@ Symbol* basestate(Symbol* s) /* base state symbol for state''' or state0 */ return base; } -#if SYSV || !defined(HAVE_INDEX) || defined(HAVE_STRINGS_H) -#undef index -#define index strchr -#endif - static int nprime(char* s) { char* cp; - cp = index(s, '\''); + cp = strchr(s, '\''); return strlen(s) - (cp - s); } diff --git a/src/modlunit/init.cpp b/src/modlunit/init.cpp index 80250d47ab..7ccea7ca4c 100644 --- a/src/modlunit/init.cpp +++ b/src/modlunit/init.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/init.c,v 1.9 1998/03/25 14:33:55 hines Exp */ #include "model.h" diff --git a/src/modlunit/io.cpp b/src/modlunit/io.cpp index 93b36a6557..28e23759df 100644 --- a/src/modlunit/io.cpp +++ b/src/modlunit/io.cpp @@ -1,8 +1,9 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/io.c,v 1.2 1997/11/24 16:19:09 hines Exp */ /* file.mod input routines */ #include +#include #include "model.h" #include #undef METHOD diff --git a/src/modlunit/kinunit.cpp b/src/modlunit/kinunit.cpp index 003cf4cec0..ad41d5a109 100644 --- a/src/modlunit/kinunit.cpp +++ b/src/modlunit/kinunit.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "model.h" #include "symbol.h" #include "units.h" diff --git a/src/modlunit/lex.lpp b/src/modlunit/lex.lpp index ebe4320d08..f74a58afd6 100755 --- a/src/modlunit/lex.lpp +++ b/src/modlunit/lex.lpp @@ -15,7 +15,7 @@ #undef input #endif -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "model.h" #include "parse1.hpp" diff --git a/src/modlunit/list.cpp b/src/modlunit/list.cpp index 88bea2d255..e6bfac1b06 100644 --- a/src/modlunit/list.cpp +++ b/src/modlunit/list.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/list.c,v 1.3 1997/11/24 16:19:10 hines Exp */ /* The following routines support the concept of a list. @@ -30,6 +30,7 @@ following function calls. */ #include +#include #include "model.h" #include "parse1.hpp" diff --git a/src/modlunit/model.cpp b/src/modlunit/model.cpp index 29005268fc..04ff800ea8 100644 --- a/src/modlunit/model.cpp +++ b/src/modlunit/model.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/model.c,v 1.6 1998/07/12 13:19:02 hines Exp */ /* @@ -24,6 +24,7 @@ * still gives the prefix of the .c and .var files. */ +#include #include "model.h" #include "parse1.hpp" diff --git a/src/modlunit/model.h b/src/modlunit/model.h index 2433436c53..fcc644d6b0 100644 --- a/src/modlunit/model.h +++ b/src/modlunit/model.h @@ -1,13 +1,6 @@ /* /local/src/master/nrn/src/modlunit/model.h,v 1.2 1997/11/24 16:19:13 hines Exp */ #include "wrap_sprintf.h" #include -#if 1 -#if defined(STDC_HEADERS) || defined(SYSV) -#include -#else -#include -#endif -#endif #include #define NRN_BUFSIZE 8192 diff --git a/src/modlunit/nrnunit.cpp b/src/modlunit/nrnunit.cpp index fa71749b68..9ee6a969e7 100644 --- a/src/modlunit/nrnunit.cpp +++ b/src/modlunit/nrnunit.cpp @@ -1,4 +1,5 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> +#include #include "model.h" #include "units.h" #include "parse1.hpp" diff --git a/src/modlunit/parse1.ypp b/src/modlunit/parse1.ypp index 6e0e4778b3..1b63690297 100755 --- a/src/modlunit/parse1.ypp +++ b/src/modlunit/parse1.ypp @@ -1,8 +1,9 @@ %{ /* /local/src/master/nrn/src/modlunit/parse1.y,v 1.11 1999/02/27 21:13:50 hines Exp */ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include +#include #include "model.h" /* Constructs a parse tree. No translation is done, ie. on exit printing diff --git a/src/modlunit/passn.cpp b/src/modlunit/passn.cpp index 7c26668246..1456404f22 100644 --- a/src/modlunit/passn.cpp +++ b/src/modlunit/passn.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/passn.c,v 1.1.1.1 1994/10/12 17:22:50 hines Exp */ /* Returns parse tokens in same order that lexical analyzer did */ diff --git a/src/modlunit/symbol.cpp b/src/modlunit/symbol.cpp index 7af671c526..e59bad1b16 100644 --- a/src/modlunit/symbol.cpp +++ b/src/modlunit/symbol.cpp @@ -1,5 +1,6 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/symbol.c,v 1.1.1.1 1994/10/12 17:22:50 hines Exp */ +#include #include "model.h" #include "parse1.hpp" diff --git a/src/modlunit/units.cpp b/src/modlunit/units.cpp index 5f67ca1d2b..9901f19fae 100644 --- a/src/modlunit/units.cpp +++ b/src/modlunit/units.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/units.c,v 1.5 1997/11/24 16:19:13 hines Exp */ /* Mostly from Berkeley */ #include "model.h" diff --git a/src/modlunit/units1.cpp b/src/modlunit/units1.cpp index 876d4590f5..7db0ba3f08 100644 --- a/src/modlunit/units1.cpp +++ b/src/modlunit/units1.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/units1.c,v 1.1.1.1 1994/10/12 17:22:51 hines Exp */ /* Just a connection to units.c so that file doesn't need to include modl.h */ #include "model.h" diff --git a/src/modlunit/version.cpp b/src/modlunit/version.cpp index 4d4247e766..4f7e71e288 100644 --- a/src/modlunit/version.cpp +++ b/src/modlunit/version.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/modlunit/version.c,v 1.1.1.1 1994/10/12 17:22:51 hines Exp */ const char* RCS_version = "1.1.1.1"; diff --git a/src/mswin/mwprefix.h b/src/mswin/mwprefix.h deleted file mode 100644 index 877b31f9ec..0000000000 --- a/src/mswin/mwprefix.h +++ /dev/null @@ -1,39 +0,0 @@ -/* auto include file for metrowerks codewarrior for all nrn */ -#include -#pragma once off -#ifndef __WIN32__ -#define __WIN32__ 1 -#endif -#ifndef WIN32 -#define WIN32 1 -#endif -#define _WIN32 -#ifndef _Windows -#define _Windows 1 -#endif -struct Section; -struct Object; -struct Symbol; -#define motif_kit -#define sgi_motif_kit -#define printf myprintf -#define vprintf myvprintf -#define gets mygets -#define puts myputs -#define fprintf myfprintf -#undef small -#undef near -#define small mysmall -#define near mynear - -#define stricmp _stricmp -#define putenv _putenv - -//#define system mysystem -#undef DELETE -#undef IGNORE -#define HOC 1 -#define OOP 1 -#define OC_CLASSES "nrnclass.h" -#define USECVODE 1 -#define USEMATRIX 1 diff --git a/src/neuron/container/soa_container.hpp b/src/neuron/container/soa_container.hpp index 7b0f990c4d..c176281f26 100644 --- a/src/neuron/container/soa_container.hpp +++ b/src/neuron/container/soa_container.hpp @@ -5,6 +5,7 @@ #include "neuron/container/generic_data_handle.hpp" #include "neuron/container/soa_identifier.hpp" +#include // std::transform #include #include #include diff --git a/src/nmodl/consist.cpp b/src/nmodl/consist.cpp index 729796f83a..295fdca9b3 100644 --- a/src/nmodl/consist.cpp +++ b/src/nmodl/consist.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* * Check that names do not have conflicting types. This is done after the diff --git a/src/nmodl/deriv.cpp b/src/nmodl/deriv.cpp index 4071ef0e63..07ca2258c3 100644 --- a/src/nmodl/deriv.cpp +++ b/src/nmodl/deriv.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "modl.h" #include "symbol.h" diff --git a/src/nmodl/diffeq.ypp b/src/nmodl/diffeq.ypp index 462eb98510..3d316efe91 100755 --- a/src/nmodl/diffeq.ypp +++ b/src/nmodl/diffeq.ypp @@ -6,7 +6,7 @@ The only thing that makes this less than elegant is dealing with 0.0 */ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include #include "modl.h" #include "difeqdef.h" diff --git a/src/nmodl/discrete.cpp b/src/nmodl/discrete.cpp index 0051132b70..055f9492cc 100644 --- a/src/nmodl/discrete.cpp +++ b/src/nmodl/discrete.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include #include "modl.h" diff --git a/src/nmodl/init.cpp b/src/nmodl/init.cpp index b5c89c3c33..394e3f8b94 100644 --- a/src/nmodl/init.cpp +++ b/src/nmodl/init.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/nmodl/init.c,v 4.5 1998/03/25 14:33:42 hines Exp */ #include "modl.h" diff --git a/src/nmodl/io.cpp b/src/nmodl/io.cpp index 88110a1b36..f6a0982ae6 100644 --- a/src/nmodl/io.cpp +++ b/src/nmodl/io.cpp @@ -1,4 +1,6 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> +#include +namespace fs = std::filesystem; /* file.mod input routines */ #include @@ -385,7 +387,6 @@ static FILE* include_open(char* fname, int err) { } void include_file(Item* q) { - char* pf = NULL; char fname[NRN_BUFSIZE]; Item* qinc; FileStackItem* fsi; @@ -415,13 +416,11 @@ void include_file(Item* q) { qinc = filetxtlist->prev; Sprintf(buf, ":::%s", STR(qinc)); replacstr(qinc, buf); -#if HAVE_REALPATH - pf = realpath(fname, NULL); -#endif - if (pf) { - Sprintf(buf, ":::realpath %s\n", pf); - free(pf); + try { + Sprintf(buf, ":::realpath %s\n", fs::absolute(fname).c_str()); lappendstr(filetxtlist, buf); + } catch (const std::filesystem::filesystem_error&) { + // If we are not able to get an absolute path from fname, simply avoid to write it. } } diff --git a/src/nmodl/kinetic.cpp b/src/nmodl/kinetic.cpp index b3f457355d..763a4b9971 100644 --- a/src/nmodl/kinetic.cpp +++ b/src/nmodl/kinetic.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #define Glass 1 diff --git a/src/nmodl/lex.lpp b/src/nmodl/lex.lpp index d7dc282531..4ec229210f 100755 --- a/src/nmodl/lex.lpp +++ b/src/nmodl/lex.lpp @@ -1,7 +1,7 @@ %{ /* /local/src/master/nrn/src/nmodl/lex.lpp,v 4.2 1997/11/05 17:59:02 hines Exp */ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #undef output #undef unput diff --git a/src/nmodl/list.cpp b/src/nmodl/list.cpp index 807d6547e5..0e373fab5a 100644 --- a/src/nmodl/list.cpp +++ b/src/nmodl/list.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* The following routines support the concept of a list. That is, one can insert at the head of a list or append to the tail of a diff --git a/src/nmodl/modl.cpp b/src/nmodl/modl.cpp index 9523c7a2a1..0c23cb8748 100644 --- a/src/nmodl/modl.cpp +++ b/src/nmodl/modl.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* * int main(int argc, char *argv[]) --- returns 0 if translation is @@ -151,17 +151,14 @@ int main(int argc, char** argv) { #endif if (nmodl_text) { Item* q; - char* pf{nullptr}; -#if HAVE_REALPATH && !defined(NRN_AVOID_ABSOLUTE_PATHS) - pf = realpath(finname, nullptr); -#endif fprintf( fcout, "\n#if NMODL_TEXT\nstatic void register_nmodl_text_and_filename(int mech_type) {\n"); - fprintf(fcout, " const char* nmodl_filename = \"%s\";\n", pf ? pf : finname); - if (pf) { - free(pf); - } +#if !defined(NRN_AVOID_ABSOLUTE_PATHS) + fprintf(fcout, " const char* nmodl_filename = \"%s\";\n", fs::absolute(finname).c_str()); +#else + fprintf(fcout, " const char* nmodl_filename = \"%s\";\n", finname); +#endif fprintf(fcout, " const char* nmodl_file_text = \n"); ITERATE(q, filetxtlist) { char* s = STR(q); diff --git a/src/nmodl/netrec_discon.cpp b/src/nmodl/netrec_discon.cpp index 4666bf0356..f4dd412d70 100644 --- a/src/nmodl/netrec_discon.cpp +++ b/src/nmodl/netrec_discon.cpp @@ -51,7 +51,7 @@ that only dsi/dt that is affected by sj will change si. */ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include #include diff --git a/src/nmodl/noccout.cpp b/src/nmodl/noccout.cpp index f95ed6e315..f57fb52e04 100644 --- a/src/nmodl/noccout.cpp +++ b/src/nmodl/noccout.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* print the .c file from the lists */ #include "modl.h" diff --git a/src/nmodl/nocpout.cpp b/src/nmodl/nocpout.cpp index 48dd954a0e..02c70c5a8e 100644 --- a/src/nmodl/nocpout.cpp +++ b/src/nmodl/nocpout.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/nmodl/nocpout.c,v 4.1 1997/08/30 20:45:28 hines Exp */ @@ -71,6 +71,8 @@ directly by hoc. #ifdef HAVE_UNISTD_H #include #endif +#include +namespace fs = std::filesystem; #define GETWD(buf) getcwd(buf, NRN_BUFSIZE) int vectorize = 1; @@ -184,6 +186,7 @@ static Item* net_send_delivered_; /* location for if flag is 1 then clear the pvarcount indexes pointers to variables such as ena */ static int varcount, parraycount; +static int prop_size; static std::vector> ppvar_data_field_strings; static std::vector data_field_strings; @@ -321,7 +324,7 @@ void parout() { Lappendstr(defs_list, "extern double *hoc_getarg(int);\n"); nrndeclare(); - varcount = parraycount = 0; + varcount = parraycount = prop_size = 0; declare_p(); // iondef defined _nrn_mechanism_cache_range ioncount = iondef(&pointercount); /* first is _nd_area if point process */ @@ -1282,7 +1285,7 @@ extern void _cvode_abstol( Symbol**, double*, int);\n\n\ } register_data_fields.append(");\n"); lappendstr(defs_list, register_data_fields.c_str()); - Sprintf(buf, " hoc_register_prop_size(_mechtype, %d, %d);\n", parraycount, ppvar_cnt); + Sprintf(buf, " hoc_register_prop_size(_mechtype, %d, %d);\n", prop_size, ppvar_cnt); Lappendstr(defs_list, buf); if (watch_seen_) { Lappendstr(defs_list, " hoc_reg_watch_allocate(_mechtype, _watch_alloc);\n"); @@ -1400,14 +1403,14 @@ if (auto* const _extnode = _nrn_mechanism_access_extnode(_nd); _extnode) {\n\ " hoc_register_var(hoc_scdoub, hoc_vdoub, hoc_intfunc);\n"); { char buf1[NRN_BUFSIZE]; - char* pf{}; -#if HAVE_REALPATH && !defined(NRN_AVOID_ABSOLUTE_PATHS) - pf = realpath(finname, NULL); +#if !defined(NRN_AVOID_ABSOLUTE_PATHS) + Sprintf(buf1, + "\tivoc_help(\"help ?1 %s %s\\n\");\n", + mechname, + fs::absolute(finname).c_str()); +#else + Sprintf(buf1, "\tivoc_help(\"help ?1 %s %s\\n\");\n", mechname, finname); #endif - Sprintf(buf1, "\tivoc_help(\"help ?1 %s %s\\n\");\n", mechname, pf ? pf : finname); - if (pf) { - free(pf); - } Lappendstr(defs_list, buf1); } if (suffix[0]) { @@ -1799,6 +1802,9 @@ static void var_count(Symbol* s) { if (s->subtype & ARRAY) { field.append(", "); field.append(std::to_string(s->araydim)); + prop_size += s->araydim; + } else { + prop_size += 1; } // **ATTENTION** in AoS NEURON then parraycount was incremented by s->araydim if the variable // was an array. In SoA NEURON this is not done; the array dimension is communicated separately. diff --git a/src/nmodl/parsact.cpp b/src/nmodl/parsact.cpp index 79e8b96584..e6d656063f 100644 --- a/src/nmodl/parsact.cpp +++ b/src/nmodl/parsact.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* * some parse actions to reduce size of parse.ypp the installation routines can diff --git a/src/nmodl/parse1.ypp b/src/nmodl/parse1.ypp index 03c7108c46..37076da3bd 100755 --- a/src/nmodl/parse1.ypp +++ b/src/nmodl/parse1.ypp @@ -1,7 +1,7 @@ %{ /* /local/src/master/nrn/src/nmodl/parse1.ypp,v 4.11 1999/03/24 18:34:08 hines Exp */ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "modl.h" #include diff --git a/src/nmodl/simultan.cpp b/src/nmodl/simultan.cpp index a946a334cc..ef649b414c 100644 --- a/src/nmodl/simultan.cpp +++ b/src/nmodl/simultan.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "modl.h" #include "parse1.hpp" #include "symbol.h" diff --git a/src/nmodl/solve.cpp b/src/nmodl/solve.cpp index 9051da0f49..9406a74a00 100644 --- a/src/nmodl/solve.cpp +++ b/src/nmodl/solve.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* /local/src/master/nrn/src/nmodl/solve.c,v 4.4 1998/08/20 21:07:34 hines Exp */ #include "modl.h" diff --git a/src/nmodl/symbol.cpp b/src/nmodl/symbol.cpp index 11c0fedf9f..04a970a5e6 100644 --- a/src/nmodl/symbol.cpp +++ b/src/nmodl/symbol.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> #include "modl.h" #include "parse1.hpp" diff --git a/src/nmodl/units.cpp b/src/nmodl/units.cpp index 698fb51f60..a618b6e73b 100755 --- a/src/nmodl/units.cpp +++ b/src/nmodl/units.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* * Automake doesn't deal well with sources that live in other directories. * This is a quick and dirty workaround. diff --git a/src/nmodl/version.cpp b/src/nmodl/version.cpp index 207745ec5d..a40e4a4a2e 100644 --- a/src/nmodl/version.cpp +++ b/src/nmodl/version.cpp @@ -1,4 +1,4 @@ -#include <../../nmodlconf.h> +#include <../../nrnconf.h> /* $Header$ */ const char* RCS_version = "$Revision: 1098 $"; const char* RCS_date = "$Date: 2005-10-04 22:55:37 +0200 (Tue, 04 Oct 2005) $"; diff --git a/src/node_order_optim/README b/src/node_order_optim/README new file mode 100644 index 0000000000..5f20872dad --- /dev/null +++ b/src/node_order_optim/README @@ -0,0 +1,26 @@ +We wish to adopt into NEURON the CoreNEURON algorithms for node ordering. + + +interleave_order + node_order + tree_analysis + check + set_cellindex + set_groupindex + level_from_root + node_interleave_order if permute type 1 else group_order2 + check + quality + admin1 if permute type 1 else admin2 + return the permutation new int[nnode] + if thread 0 + print_quality1 if permute type 1 else print_quality2 + warp_balance + return order + +In CoreNEURON, phase2.cpp + nt._permute = interleave_order + apply the permutation to nodes, mechanisms, point processes + + +std::vector perm = interleave_order(int ith, int ncell, int nnode, int* parent); diff --git a/src/node_order_optim/node_order_optim.h b/src/node_order_optim/node_order_optim.h new file mode 100644 index 0000000000..f8519b9229 --- /dev/null +++ b/src/node_order_optim/node_order_optim.h @@ -0,0 +1,27 @@ +#pragma once + +namespace neuron { + +extern int interleave_permute_type; + +/// @brief Select node ordering for optimum gaussian elimination +/// @param type +/// 0 cell together (Section construction order) +/// 1 Interleave, identical cells warp adjacent +/// 2 Depth order, optimize adjacent nodes to have adjacent parents. +void nrn_optimize_node_order(int type); + +/// @brief Compute and carry out the permutation for interleave_permute_type +void nrn_permute_node_order(); + +/** + * + * \brief Solve the Hines matrices based on the interleave_permute_type (1 or 2). + * + * For interleave_permute_type == 1 : Naive interleaving -> Each execution thread deals with one + * Hines matrix (cell) For interleave_permute_type == 2 : Advanced interleaving -> Each Hines matrix + * is solved by multiple execution threads (with coalesced memory access as well) + */ +extern void solve_interleaved(int ith); + +} // namespace neuron diff --git a/src/node_order_optim/permute_utils.hpp b/src/node_order_optim/permute_utils.hpp new file mode 100644 index 0000000000..68c2fe6af6 --- /dev/null +++ b/src/node_order_optim/permute_utils.hpp @@ -0,0 +1,37 @@ +#include +#include + +template +void forward_permute(std::vector& data, const std::vector& perm) { + // Move data[perm[i]] to data[i] + std::vector new_data; + new_data.reserve(data.size()); + std::transform(perm.begin(), perm.end(), std::back_inserter(new_data), [&data](int ind) { + return data[ind]; + }); + data = std::move(new_data); +} + +template +void forward_permute(T*& data, int data_size, const std::vector& perm) { + // Move data[perm[i]] to data[i] + auto new_data = new T[data_size]; + for (auto i = 0; i < perm.size(); ++i) { + new_data[i] = data[perm[i]]; + } + // cannot std::swap because data may have been allocated by malloc + // std::swap(data, new_data); + for (auto i = 0; i < perm.size(); ++i) { + data[i] = new_data[i]; + } + delete[] new_data; +} + +template +std::vector inverse_permute_vector(const std::vector& p) { + std::vector pinv(p.size()); + for (int i = 0; i < p.size(); ++i) { + pinv[p[i]] = i; + } + return pinv; +} diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index 1573d190e4..f0421a92da 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -5567,7 +5567,7 @@ static int trajec_buffered(NrnThread& nt, // beyond their current size and CoreNEURON will start filling from the // current fill time, h.t, location of the arrays. I.e. starting at CoreNEURON's // start time. (Multiple calls to psolve append to these arrays.) -// n_pr refers the the number of PlayRecord instances in the vpr array. +// n_pr refers to the number of PlayRecord instances in the vpr array. // n_trajec refers to the number of trajectories to be recorded on the // CoreNEURON side and is the size of the types, indices, and varrays. // n_pr is different from n_trajec when one of the GLineRecord instances has diff --git a/src/nrncvode/nrnneosm.h.in b/src/nrncvode/nrnneosm.h.in index ad61a31526..09e38d1c3a 100755 --- a/src/nrncvode/nrnneosm.h.in +++ b/src/nrncvode/nrnneosm.h.in @@ -1,6 +1,6 @@ /* define to 1 if allowing NEOSIM */ -#undef USENEOSIM +#cmakedefine USENEOSIM @USENEOSIM@ /* define to 1 if allowing NCS */ -#undef USENCS +#cmakedefine USENCS @USENCS@ /* define to 1 (default) if Observer is a base class of DiscreteEvent */ -#undef DISCRETE_EVENT_OBSERVER +#cmakedefine DISCRETE_EVENT_OBSERVER @DISCRETE_EVENT_OBSERVER@ diff --git a/src/nrncvode/sptree.hpp b/src/nrncvode/sptree.hpp index bc78cd3180..5ef997e38b 100644 --- a/src/nrncvode/sptree.hpp +++ b/src/nrncvode/sptree.hpp @@ -7,129 +7,141 @@ ** ** assumes that key will be provided by the application, comparable ** with the compare function applied to the addresses of two keys. +** +** Originaly written by Douglas W. Jones in Fortran helped by Srinivas R. Sataluri. +** Translated by David Brower to C circa 1988. +** Some fixes by Mark Moraes . +** +** For some litterature about this Splay tree: https://en.wikipedia.org/wiki/Splay_tree +** The original implementation is based on: +** Self Adjusting Binary Trees +** by D. D. Sleator and R. E. Tarjan, +** Proc. ACM SIGACT Symposium on Theory +** of Computing (Boston, Apr 1983) 235-245. +** +** For more insights, `enqueue` is doing the splay top-down. +** `splay` itself is doing it bottom-up. */ #pragma once -#define STRCMP(a, b) (a - b) - -template -struct SPTREE { - SPBLK* root; /* root node */ - - int enqcmps; /* compares in spenq */ +#include + +template +class SPTree { + public: + SPTree() = default; + + // Is this SPTree empty? + bool empty() const; + + // Return the value of enqcmps. + int get_enqcmps() const; + + // Insert item in the tree. + // + // Put `n` after all other nodes with the same key; when this is + // done, `n` will be the root of the splay tree, all nodes + // with keys less than or equal to that of `n` will be in the + // left subtree, all with greater keys will be in the right subtree; + // the tree is split into these subtrees from the top down, with rotations + // performed along the way to shorten the left branch of the right subtree + // and the right branch of the left subtree + void enqueue(T* n); + + // Return and remove the first node. + // + // Remove and return the first node; this deletes + // (and returns) the leftmost node, replacing it with its right + // subtree (if there is one); on the way to the leftmost node, rotations + // are performed to shorten the left branch of the tree. + T* dequeue(); + + // Return the element in the tree with the lowest key. + // + // Returns a pointer to the item with the lowest key. + // The left branch will be shortened as if the tree had been splayed around the first item. + // This is done by dequeue and enqueuing the first item. + T* first(); + + // Remove node `n` from the tree. + // + // `n` is removed; the resulting splay tree has been splayed + // around its new root, which is the successor of n + void remove(T* n); + + // Find a node with the given key. + // + // Splays the found node to the root. + T* find(double key); + + // Apply the function `f` to each node in ascending order. + // + // `f` is the function that will be applied to nodes. The first argument will be + // a pointer to the current node, the integer value is unused and will always be `0`. + // If n is given, start at that node, otherwise start from the head. + void apply_all(void (*f)(const T*, int), T* n) const; + + private: + // Dequeue the first node in the subtree np + T* dequeue(T** np); + + // Reorganize the tree. + // + // The tree is reorganized so that `n` is the root of the + // splay tree; operation is undefined if `n` is not + // in the tree; the tree is split from `n` up to the old root, with all + // nodes to the left of `n` ending up in the left subtree, and all nodes + // to the right of n ending up in the right subtree; the left branch of + // the right subtree and the right branch of the left subtree are + // shortened in the process + // + // This code assumes that `n` is not `nullptr` and is in the tree; it can sometimes + // detect that `n` is not in the tree and throw an exception. + void splay(T* n); + + // Return the first element in the tree witout splaying. + // + // Returns a reference to the head event. + // Avoids splaying but just searches for and returns a pointer to + // the bottom of the left branch. + T* fast_first() const; + + // Fast return next higher item in the tree, or nullptr + // + // Return the successor of `n`, represented as a splay tree. + // This is a fast (on average) version that does not splay. + T* fast_next(T* n) const; + + // The node at the top of the tree + T* root{}; + + // Number of comparison in enqueue + int enqcmps{}; }; -#define spinit sptq_spinit -#define spempty sptq_spempty -#define spenq sptq_spenq -#define spdeq sptq_spdeq -#define splay sptq_splay -#define sphead sptq_sphead -#define spdelete sptq_spdelete -#define splookup sptq_splookup -#define spscan sptq_spscan -#define spfhead sptq_spfhead -#define spfnext sptq_spfnext - -/* Original file: sptree.cpp - * - * sptree.cpp: The following code implements the basic operations on - * an event-set or priority-queue implemented using splay trees: - * -Hines changed to void spinit(SPTREE**) for use with TQueue. - * SPTREE *spinit( compare ) Make a new tree - * int spempty(); Is tree empty? - * SPBLK *spenq( n, q ) Insert n in q after all equal keys. - * SPBLK *spdeq( np ) Return first key under *np, removing it. - * void splay( n, q ) n (already in q) becomes the root. - * - * In the above, n points to an SPBLK type, while q points to an - * SPTREE. - * - * The implementation used here is based on the implementation - * which was used in the tests of splay trees reported in: - * - * An Empirical Comparison of Priority-Queue and Event-Set Implementations, - * by Douglas W. Jones, Comm. ACM 29, 4 (Apr. 1986) 300-311. - * - * The changes made include the addition of the enqprior - * operation and the addition of up-links to allow for the splay - * operation. The basic splay tree algorithms were originally - * presented in: - * - * Self Adjusting Binary Trees, - * by D. D. Sleator and R. E. Tarjan, - * Proc. ACM SIGACT Symposium on Theory - * of Computing (Boston, Apr 1983) 235-245. - * - * The enq and enqprior routines use variations on the - * top-down splay operation, while the splay routine is bottom-up. - * All are coded for speed. - * - * Written by: - * Douglas W. Jones - * - * Translated to C by: - * David Brower, daveb@rtech.uucp - * - * Thu Oct 6 12:11:33 PDT 1988 (daveb) Fixed spdeq, which was broken - * handling one-node trees. I botched the pascal translation of - * a VAR parameter. - */ - - -/* USER SUPPLIED! */ - -/*extern char *emalloc();*/ - - -/*---------------- - * - * spinit() -- initialize an empty splay tree - * - */ -template -void spinit(SPTREE* q) { - q->enqcmps = 0; - q->root = nullptr; +template +bool SPTree::empty() const { + return root == nullptr; } -/*---------------- - * - * spempty() -- is an event-set represented as a splay tree empty? - */ -template -bool spempty(SPTREE* q) { - return (q == nullptr || q->root == nullptr); +template +int SPTree::get_enqcmps() const { + return enqcmps; } - -/*---------------- - * - * spenq() -- insert item in a tree. - * - * put n in q after all other nodes with the same key; when this is - * done, n will be the root of the splay tree representing q, all nodes - * in q with keys less than or equal to that of n will be in the - * left subtree, all with greater keys will be in the right subtree; - * the tree is split into these subtrees from the top down, with rotations - * performed along the way to shorten the left branch of the right subtree - * and the right branch of the left subtree - */ -template -SPBLK* spenq(SPBLK* n, SPTREE* q) { - SPBLK* left; /* the rightmost node in the left tree */ - SPBLK* right; /* the leftmost node in the right tree */ - SPBLK* next; /* the root of the unsplit part */ - SPBLK* temp; +template +void SPTree::enqueue(T* n) { + T* left; /* the rightmost node in the left tree */ + T* right; /* the leftmost node in the right tree */ + T* next; /* the root of the unsplit part */ + T* temp; double key; - int Sct; /* Strcmp value */ n->uplink = nullptr; - next = q->root; - q->root = n; + next = root; + root = n; if (next == nullptr) /* trivial enq */ { n->leftlink = nullptr; @@ -144,12 +156,12 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { splayed trees resulting from splitting on n->key; note that the children will be reversed! */ - q->enqcmps++; - if (STRCMP(next->key, key) > 0) + enqcmps++; + if (next->key - key > 0) { goto two; + } - one: /* assert next->key <= key */ - + one: /* assert next->key <= key */ do /* walk to the right in the left tree */ { temp = next->rightlink; @@ -160,8 +172,8 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto done; /* job done, entire tree split */ } - q->enqcmps++; - if (STRCMP(temp->key, key) > 0) { + enqcmps++; + if (temp->key - key > 0) { left->rightlink = next; next->uplink = left; left = next; @@ -170,8 +182,9 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { } next->rightlink = temp->leftlink; - if (temp->leftlink != nullptr) + if (temp->leftlink != nullptr) { temp->leftlink->uplink = next; + } left->rightlink = temp; temp->uplink = left; temp->leftlink = next; @@ -183,12 +196,11 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto done; /* job done, entire tree split */ } - q->enqcmps++; - - } while (STRCMP(next->key, key) <= 0); /* change sides */ + enqcmps++; - two: /* assert next->key > key */ + } while (next->key - key <= 0); /* change sides */ + two: /* assert next->key > key */ do /* walk to the left in the right tree */ { temp = next->leftlink; @@ -199,8 +211,8 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto done; /* job done, entire tree split */ } - q->enqcmps++; - if (STRCMP(temp->key, key) <= 0) { + enqcmps++; + if (temp->key - key <= 0) { right->leftlink = next; next->uplink = right; right = next; @@ -208,8 +220,9 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto one; /* change sides */ } next->leftlink = temp->rightlink; - if (temp->rightlink != nullptr) + if (temp->rightlink != nullptr) { temp->rightlink->uplink = next; + } right->leftlink = temp; temp->uplink = right; temp->rightlink = next; @@ -221,42 +234,32 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto done; /* job done, entire tree split */ } - q->enqcmps++; + enqcmps++; - } while (STRCMP(next->key, key) > 0); /* change sides */ + } while (next->key - key > 0); /* change sides */ goto one; - done: /* split is done, branches of n need reversal */ - + done: /* split is done, branches of `n` need reversal */ temp = n->leftlink; n->leftlink = n->rightlink; n->rightlink = temp; } +} - return (n); - -} /* spenq */ - - -/*---------------- - * - * spdeq() -- return and remove head node from a subtree. - * - * remove and return the head node from the node set; this deletes - * (and returns) the leftmost node from q, replacing it with its right - * subtree (if there is one); on the way to the leftmost node, rotations - * are performed to shorten the left branch of the tree - */ -template -SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ +template +T* SPTree::dequeue() { + return dequeue(&root); +} +template +T* SPTree::dequeue(T** np) /* pointer to a node pointer */ { - SPBLK* deq; /* one to return */ - SPBLK* next; /* the next thing to deal with */ - SPBLK* left; /* the left child of next */ - SPBLK* farleft; /* the left child of left */ - SPBLK* farfarleft; /* the left child of farleft */ + T* deq; /* one to return */ + T* next; /* the next thing to deal with */ + T* left; /* the left child of next */ + T* farleft; /* the left child of left */ + T* farfarleft; /* the left child of farleft */ if (np == nullptr || *np == nullptr) { deq = nullptr; @@ -267,10 +270,10 @@ SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ deq = next; *np = next->rightlink; - if (*np != nullptr) + if (*np != nullptr) { (*np)->uplink = nullptr; - - } else + } + } else { for (;;) /* left is not null */ { /* next is not it, left is not nullptr, might be it */ @@ -304,37 +307,20 @@ SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ next = farleft; left = farfarleft; } + } } - return (deq); - -} /* spdeq */ - - -/*---------------- - * - * splay() -- reorganize the tree. - * - * the tree is reorganized so that n is the root of the - * splay tree representing q; results are unpredictable if n is not - * in q to start with; q is split from n up to the old root, with all - * nodes to the left of n ending up in the left subtree, and all nodes - * to the right of n ending up in the right subtree; the left branch of - * the right subtree and the right branch of the left subtree are - * shortened in the process - * - * this code assumes that n is not nullptr and is in q; it can sometimes - * detect n not in q and complain - */ - -template -void splay(SPBLK* n, SPTREE* q) { - SPBLK* up; /* points to the node being dealt with */ - SPBLK* prev; /* a descendent of up, already dealt with */ - SPBLK* upup; /* the parent of up */ - SPBLK* upupup; /* the grandparent of up */ - SPBLK* left; /* the top of left subtree being built */ - SPBLK* right; /* the top of right subtree being built */ + return deq; +} + +template +void SPTree::splay(T* n) { + T* up; /* points to the node being dealt with */ + T* prev; /* a descendent of up, already dealt with */ + T* upup; /* the parent of up */ + T* upupup; /* the grandparent of up */ + T* left; /* the top of left subtree being built */ + T* right; /* the top of right subtree being built */ left = n->leftlink; @@ -344,8 +330,7 @@ void splay(SPBLK* n, SPTREE* q) { while (up != nullptr) { /* walk up the tree towards the root, splaying all to the left of - n into the left subtree, all to right into the right subtree */ - + `n` into the left subtree, all to right into the right subtree */ upup = up->uplink; if (up->leftlink == prev) /* up is to the right of n */ { @@ -353,46 +338,52 @@ void splay(SPBLK* n, SPTREE* q) { { upupup = upup->uplink; upup->leftlink = up->rightlink; - if (upup->leftlink != nullptr) + if (upup->leftlink != nullptr) { upup->leftlink->uplink = upup; + } up->rightlink = upup; upup->uplink = up; - if (upupup == nullptr) - q->root = up; - else if (upupup->leftlink == upup) + if (upupup == nullptr) { + root = up; + } else if (upupup->leftlink == upup) { upupup->leftlink = up; - else + } else { upupup->rightlink = up; + } up->uplink = upupup; upup = upupup; } up->leftlink = right; - if (right != nullptr) + if (right != nullptr) { right->uplink = up; + } right = up; - } else /* up is to the left of n */ + } else /* up is to the left of `n` */ { if (upup != nullptr && upup->rightlink == up) /* rotate */ { upupup = upup->uplink; upup->rightlink = up->leftlink; - if (upup->rightlink != nullptr) + if (upup->rightlink != nullptr) { upup->rightlink->uplink = upup; + } up->leftlink = upup; upup->uplink = up; - if (upupup == nullptr) - q->root = up; - else if (upupup->rightlink == upup) + if (upupup == nullptr) { + root = up; + } else if (upupup->rightlink == upup) { upupup->rightlink = up; - else + } else { upupup->leftlink = up; + } up->uplink = upupup; upup = upupup; } up->rightlink = left; - if (left != nullptr) + if (left != nullptr) { left->uplink = up; + } left = up; } prev = up; @@ -400,278 +391,129 @@ void splay(SPBLK* n, SPTREE* q) { } #ifdef DEBUG - if (q->root != prev) { - /* fprintf(stderr, " *** bug in splay: n not in q *** " ); */ + if (root != prev) { + /*fprintf(stderr, " *** bug in splay: n not in q *** " ); */ abort(); } #endif n->leftlink = left; n->rightlink = right; - if (left != nullptr) + if (left != nullptr) { left->uplink = n; - if (right != nullptr) + } + if (right != nullptr) { right->uplink = n; - q->root = n; + } + root = n; n->uplink = nullptr; +} -} /* splay */ - - -/* Original file: spaux.cpp - - spaux.cpp: This code implements the following operations on an event-set - or priority-queue implemented using splay trees: - - n = sphead( q ) n is the head item in q (not removed). - spdelete( n, q ) n is removed from q. - - In the above, n and np are pointers to single items (type - SPBLK *); q is an event-set (type SPTREE *), - The type definitions for these are taken - from file sptree.h. All of these operations rest on basic - splay tree operations from file sptree.cpp. - - The basic splay tree algorithms were originally presented in: - - Self Adjusting Binary Trees, - by D. D. Sleator and R. E. Tarjan, - Proc. ACM SIGACT Symposium on Theory - of Computing (Boston, Apr 1983) 235-245. - - The operations in this package supplement the operations from - file splay.h to provide support for operations typically needed - on the pending event set in discrete event simulation. See, for - example, - - Introduction to Simula 67, - by Gunther Lamprecht, Vieweg & Sohn, Braucschweig, Wiesbaden, 1981. - (Chapter 14 contains the relevant discussion.) - - Simula Begin, - by Graham M. Birtwistle, et al, Studentlitteratur, Lund, 1979. - (Chapter 9 contains the relevant discussion.) - - Many of the routines in this package use the splay procedure, - for bottom-up splaying of the queue. Consequently, item n in - delete and item np in all operations listed above must be in the - event-set prior to the call or the results will be - unpredictable (eg: chaos will ensue). - - Note that, in all cases, these operations can be replaced with - the corresponding operations formulated for a conventional - lexicographically ordered tree. The versions here all use the - splay operation to ensure the amortized bounds; this usually - leads to a very compact formulation of the operations - themselves, but it may slow the average performance. - - Alternative versions based on simple binary tree operations are - provided (commented out) for head, next, and prev, since these - are frequently used to traverse the entire data structure, and - the cost of traversal is independent of the shape of the - structure, so the extra time taken by splay in this context is - wasted. - - This code was written by: - Douglas W. Jones with assistance from Srinivas R. Sataluri - - Translated to C by David Brower, daveb@rtech.uucp - - Thu Oct 6 12:11:33 PDT 1988 (daveb) Fixed spdeq, which was broken - handling one-node trees. I botched the pascal translation of - a VAR parameter. Changed interface, so callers must also be - corrected to pass the node by address rather than value. - Mon Apr 3 15:18:32 PDT 1989 (daveb) - Apply fix supplied by Mark Moraes to - spdelete(), which dropped core when taking out the last element - in a subtree -- that is, when the right subtree was empty and - the leftlink was also null, it tried to take out the leftlink's - uplink anyway. - */ - -/*---------------- - * - * sphead() -- return the "lowest" element in the tree. - * - * returns a reference to the head event in the event-set q, - * represented as a splay tree; q->root ends up pointing to the head - * event, and the old left branch of q is shortened, as if q had - * been splayed about the head element; this is done by dequeueing - * the head and then making the resulting queue the right son of - * the head returned by spdeq; an alternative is provided which - * avoids splaying but just searches for and returns a pointer to - * the bottom of the left branch - */ -template -SPBLK* sphead(SPTREE* q) { - SPBLK* x; - - /* splay version, good amortized bound */ - x = spdeq(&q->root); - if (x != nullptr) { - x->rightlink = q->root; - x->leftlink = nullptr; - x->uplink = nullptr; - if (q->root != nullptr) - q->root->uplink = x; +template +T* SPTree::first() { + if (empty()) { + return nullptr; } - q->root = x; - - /* alternative version, bad amortized bound, - but faster on the average */ - - return (x); - -} /* sphead */ + T* x = dequeue(); + x->rightlink = root; + x->leftlink = nullptr; + x->uplink = nullptr; + if (root != nullptr) { + root->uplink = x; + } + root = x; -/*---------------- - * - * spdelete() -- Delete node from a tree. - * - * n is deleted from q; the resulting splay tree has been splayed - * around its new root, which is the successor of n - * - */ -template -void spdelete(SPBLK* n, SPTREE* q) { - SPBLK* x; + return x; +} - splay(n, q); - x = spdeq(&q->root->rightlink); +template +void SPTree::remove(T* n) { + splay(n); + T* x = dequeue(&root->rightlink); if (x == nullptr) /* empty right subtree */ { - q->root = q->root->leftlink; - if (q->root) - q->root->uplink = nullptr; + root = root->leftlink; + if (root) { + root->uplink = nullptr; + } } else /* non-empty right subtree */ { x->uplink = nullptr; - x->leftlink = q->root->leftlink; - x->rightlink = q->root->rightlink; - if (x->leftlink != nullptr) + x->leftlink = root->leftlink; + x->rightlink = root->rightlink; + if (x->leftlink != nullptr) { x->leftlink->uplink = x; - if (x->rightlink != nullptr) + } + if (x->rightlink != nullptr) { x->rightlink->uplink = x; - q->root = x; + } + root = x; } +} -} /* spdelete */ - - -/* Original file: spdaveb.cpp - * - * spdaveb.cpp -- daveb's new splay tree functions. - * - * The functions in this file provide an interface that is nearly - * the same as the hash library I swiped from mkmf, allowing - * replacement of one by the other. Hey, it worked for me! - * - * splookup() -- given a key, find a node in a tree. - * spfhead() -- fast (non-splay) find the first node in a tree. - * spscan() -- forward scan tree from the head. - * spfnext() -- non-splaying next. - * - * Written by David Brower, daveb@rtech.uucp 1/88. - */ - - -/*---------------- - * - * splookup() -- given key, find a node in a tree. - * - * Splays the found node to the root. - */ -template -SPBLK* splookup(double key, SPTREE* q) { - SPBLK* n = q->root; +template +T* SPTree::find(double key) { + T* n = root; while (n && key != n->key) { n = key < n->key ? n->leftlink : n->rightlink; } /* reorganize tree around this node */ - if (n != nullptr) - splay(n, q); + if (n != nullptr) { + splay(n); + } - return (n); + return n; } +template +void SPTree::apply_all(void (*f)(const T*, int), T* n) const { + for (T* x = n != nullptr ? n : fast_first(); x != nullptr; x = fast_next(x)) { + std::invoke(f, x, 0); + } +} -/*---------------- - * - * spfhead() -- return the "lowest" element in the tree. - * - * returns a reference to the head event in the event-set q. - * avoids splaying but just searches for and returns a pointer to - * the bottom of the left branch. - */ -template -SPBLK* spfhead(SPTREE* q) { - SPBLK* x; - - if (nullptr != (x = q->root)) - while (x->leftlink != nullptr) - x = x->leftlink; - - return (x); - -} /* spfhead */ - - -/*---------------- - * - * spscan() -- apply a function to nodes in ascending order. - * - * if n is given, start at that node, otherwise start from - * the head. - */ -template -void spscan(void (*f)(const SPBLK*, int), SPBLK* n, SPTREE* q) { - SPBLK* x; +template +T* SPTree::fast_first() const { + if (empty()) { + return nullptr; + } - for (x = n != nullptr ? n : spfhead(q); x != nullptr; x = spfnext(x)) - (*f)(x, 0); + T* x = root; + while (x->leftlink != nullptr) { + x = x->leftlink; + } + return x; } -/*---------------- - * - * spfnext() -- fast return next higer item in the tree, or nullptr. - * - * return the successor of n in q, represented as a splay tree. - * This is a fast (on average) version that does not splay. - */ -template -SPBLK* spfnext(SPBLK* n) { - SPBLK* next; - SPBLK* x; - - /* a long version, avoids splaying for fast average, - * poor amortized bound - */ - - if (n == nullptr) - return (n); - - x = n->rightlink; - if (x != nullptr) { - while (x->leftlink != nullptr) +template +T* SPTree::fast_next(T* n) const { + if (n == nullptr) { + return n; + } + + // If there is a right element, the next element is the smallest item on the most left of this + // right element + if (T* x = n->rightlink; x != nullptr) { + while (x->leftlink != nullptr) { x = x->leftlink; - next = x; - } else /* x == nullptr */ - { - x = n->uplink; - next = nullptr; - while (x != nullptr) { - if (x->leftlink == n) { - next = x; - x = nullptr; - } else { - n = x; - x = n->uplink; - } } + return x; } - return (next); + // Otherwise we have to go up and left + T* next = nullptr; + T* x = n->uplink; + while (x != nullptr) { + if (x->leftlink == n) { + next = x; + x = nullptr; + } else { + n = x; + x = n->uplink; + } + } -} /* spfnext */ + return next; +} diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 8e51035838..9010ae6c43 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -55,8 +55,7 @@ TQueue::TQueue(TQItemPool* tp, int mkmut) { MUTCONSTRUCT(mkmut) tpool_ = tp; nshift_ = 0; - sptree_ = new SPTREE; - spinit(sptree_); + sptree_ = new SPTree(); binq_ = new BinQ; least_ = 0; @@ -68,7 +67,7 @@ TQueue::TQueue(TQItemPool* tp, int mkmut) { TQueue::~TQueue() { TQItem *q, *q2; - while ((q = spdeq(&sptree_->root)) != nullptr) { + while ((q = sptree_->dequeue()) != nullptr) { deleteitem(q); } delete sptree_; @@ -89,7 +88,7 @@ void TQueue::print() { if (least_) { prnt(least_, 0); } - spscan(prnt, static_cast(nullptr), sptree_); + sptree_->apply_all(prnt, nullptr); for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { prnt(q, 0); } @@ -101,7 +100,7 @@ void TQueue::forall_callback(void (*f)(const TQItem*, int)) { if (least_) { f(least_, 0); } - spscan(f, static_cast(nullptr), sptree_); + sptree_->apply_all(f, nullptr); for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { f(q, 0); } @@ -114,11 +113,11 @@ void TQueue::check(const char* mes) {} // Assume not using bin queue. TQItem* TQueue::second_least(double t) { assert(least_); - TQItem* b = sphead(sptree_); + TQItem* b = sptree_->first(); if (b && b->t_ == t) { return b; } - return 0; + return nullptr; } void TQueue::move_least(double tnew) { @@ -131,11 +130,11 @@ void TQueue::move_least_nolock(double tnew) { TQItem* b = least(); if (b) { b->t_ = tnew; - TQItem* nl = sphead(sptree_); + TQItem* nl = sptree_->first(); if (nl) { if (tnew > nl->t_) { - least_ = spdeq(&sptree_->root); - spenq(b, sptree_); + least_ = sptree_->dequeue(); + sptree_->enqueue(b); } } } @@ -147,14 +146,14 @@ void TQueue::move(TQItem* i, double tnew) { if (i == least_) { move_least_nolock(tnew); } else if (tnew < least_->t_) { - spdelete(i, sptree_); + sptree_->remove(i); i->t_ = tnew; - spenq(least_, sptree_); + sptree_->enqueue(least_); least_ = i; } else { - spdelete(i, sptree_); + sptree_->remove(i); i->t_ = tnew; - spenq(i, sptree_); + sptree_->enqueue(i); } MUTUNLOCK } @@ -167,7 +166,7 @@ void TQueue::statistics() { nrem, nleast); Printf("calls to find=%lu\n", nfind); - Printf("comparisons=%d\n", sptree_->enqcmps); + Printf("comparisons=%d\n", sptree_->get_enqcmps()); #else Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.hpp\n"); #endif @@ -192,11 +191,11 @@ TQItem* TQueue::insert(double t, void* d) { i->cnt_ = -1; if (t < least_t_nolock()) { if (least()) { - spenq(least(), sptree_); + sptree_->enqueue(least()); } least_ = i; } else { - spenq(i, sptree_); + sptree_->enqueue(i); } MUTUNLOCK return i; @@ -223,15 +222,15 @@ void TQueue::remove(TQItem* q) { STAT(nrem); if (q) { if (q == least_) { - if (!spempty(sptree_)) { - least_ = spdeq(&sptree_->root); + if (!sptree_->empty()) { + least_ = sptree_->dequeue(); } else { least_ = nullptr; } } else if (q->cnt_ >= 0) { binq_->remove(q); } else { - spdelete(q, sptree_); + sptree_->remove(q); } tpool_->hpfree(q); } @@ -244,8 +243,8 @@ TQItem* TQueue::atomic_dq(double tt) { if (least_ && least_->t_ <= tt) { q = least_; STAT(nrem); - if (!spempty(sptree_)) { - least_ = spdeq(&sptree_->root); + if (!sptree_->empty()) { + least_ = sptree_->dequeue(); } else { least_ = nullptr; } @@ -262,7 +261,7 @@ TQItem* TQueue::find(double t) { if (t == least_t_nolock()) { q = least(); } else { - q = splookup(t, sptree_); + q = sptree_->find(t); } MUTUNLOCK return (q); diff --git a/src/nrncvode/tqueue.hpp b/src/nrncvode/tqueue.hpp index 4ee6cf23cc..adf45e2464 100644 --- a/src/nrncvode/tqueue.hpp +++ b/src/nrncvode/tqueue.hpp @@ -23,7 +23,7 @@ using TQItemPool = MutexPool; #define COLLECT_TQueue_STATISTICS 1 template -struct SPTREE; +struct SPTree; // helper class for the TQueue (SplayTBinQueue). class BinQ { @@ -133,7 +133,7 @@ class TQueue { } } void move_least_nolock(double tnew); - SPTREE* sptree_; + SPTree* sptree_; BinQ* binq_; TQItem* least_; TQItemPool* tpool_; diff --git a/src/nrniv/CMakeLists.txt b/src/nrniv/CMakeLists.txt index 48f1c16531..c9331147c3 100644 --- a/src/nrniv/CMakeLists.txt +++ b/src/nrniv/CMakeLists.txt @@ -149,6 +149,7 @@ set(NRN_OC_GENERATED_SOURCES # ============================================================================= set(NRN_NRNIV_LIB_SRC_FILES ${NRN_IVOC_SRC_FILES} + ${NRN_NODEORDEROPTIM_SRC_FILES} ${NRN_NRNCVODE_SRC_FILES} ${NRN_NRNIV_SRC_FILES} ${NRN_NRNOC_SRC_FILES} @@ -581,14 +582,24 @@ endif() # ============================================================================= # For testneuron CTest -cpp_cc_build_time_copy(INPUT ${PROJECT_SOURCE_DIR}/src/ivoc/nrnmain.cpp - OUTPUT ${CMAKE_BINARY_DIR}/share/nrn/nrnmain.cpp) +cpp_cc_build_time_copy( + INPUT ${PROJECT_SOURCE_DIR}/src/ivoc/nrnmain.cpp OUTPUT ${CMAKE_BINARY_DIR}/share/nrn/nrnmain.cpp + TARGET nrnivmodl_nrnmain_cpp) + cpp_cc_build_time_copy(INPUT ${PROJECT_BINARY_DIR}/src/oc/nrnmpiuse.h - OUTPUT ${CMAKE_BINARY_DIR}/include/nrnmpiuse.h) -cpp_cc_build_time_copy(INPUT ${PROJECT_BINARY_DIR}/src/nrncvode/nrnneosm.h - OUTPUT ${CMAKE_BINARY_DIR}/include/nrncvode/nrnneosm.h) + OUTPUT ${CMAKE_BINARY_DIR}/include/nrnmpiuse.h TARGET nrnivmodl_nrnmpiuse_h) + +cpp_cc_build_time_copy( + INPUT ${PROJECT_BINARY_DIR}/src/nrncvode/nrnneosm.h + OUTPUT ${CMAKE_BINARY_DIR}/include/nrncvode/nrnneosm.h TARGET nrnivmodl_nrnneosm_h) + cpp_cc_build_time_copy(INPUT ${PROJECT_BINARY_DIR}/nrnconf.h - OUTPUT ${CMAKE_BINARY_DIR}/include/nrnconf.h) + OUTPUT ${CMAKE_BINARY_DIR}/include/nrnconf.h TARGET nrnivmodl_nrnconf_h) + +add_custom_target( + nrnivmodl_dependency ALL + DEPENDS ${CMAKE_BINARY_DIR}/share/nrn/nrnmain.cpp ${CMAKE_BINARY_DIR}/include/nrnmpiuse.h + ${CMAKE_BINARY_DIR}/include/nrncvode/nrnneosm.h ${CMAKE_BINARY_DIR}/include/nrnconf.h) # For the installation install(FILES ${PROJECT_SOURCE_DIR}/src/ivoc/nrnmain.cpp DESTINATION share/nrn) diff --git a/src/nrniv/classreg.cpp b/src/nrniv/classreg.cpp old mode 100755 new mode 100644 index cbc150a159..06e27dd5ce --- a/src/nrniv/classreg.cpp +++ b/src/nrniv/classreg.cpp @@ -1,7 +1,90 @@ -/* - * Automake doesn't deal well with source files in a separate directory, so - * this is just a stub to trick it into working right. - */ +#include <../../nrnconf.h> -#define OC_CLASSES "nrnclass.h" -#include "../ivoc/classreg.cpp" +extern void Graph_reg(), HBox_reg(), VBox_reg(), GUIMath_reg(), PWManager_reg(), GrGlyph_reg(), + ValueFieldEditor_reg(), +#if HAVE_IV + TextEditor_reg(), +#endif + OcTimer_reg(), OcDeck_reg(), SymChooser_reg(), StringFunctions_reg(), OcList_reg(), + Vector_reg(), OcPtrVector_reg(), OcFile_reg(), OcPointer_reg(), +#ifdef USEMATRIX + Matrix_reg(), +#endif + Random_reg(), Shape_reg(), PlotShape_reg(), PPShape_reg(), RangeVarPlot_reg(), + SectionBrowser_reg(), MechanismStandard_reg(), MechanismType_reg(), NetCon_reg(), + LinearMechanism_reg(), KSChan_reg(), Impedance_reg(), SaveState_reg(), BBSaveState_reg(), + FInitializeHandler_reg(), StateTransitionEvent_reg(), nrnpython_reg(), +#if USEDASPK + Daspk_reg(), +#endif +#if USECVODE + Cvode_reg(), +#endif +#if USEDSP + DSP_reg(), +#endif +#if USEBBS + ParallelContext_reg(), +#endif + NMODLRandom_reg(); + +void hoc_class_registration(void) { + void (*register_classes[])() = + { Graph_reg, + HBox_reg, + VBox_reg, + GUIMath_reg, + PWManager_reg, + GrGlyph_reg, + ValueFieldEditor_reg, +#if HAVE_IV + TextEditor_reg, +#endif + OcTimer_reg, + OcDeck_reg, + SymChooser_reg, + StringFunctions_reg, + OcList_reg, + Vector_reg, + OcPtrVector_reg, + OcFile_reg, + OcPointer_reg, +#ifdef USEMATRIX + Matrix_reg, +#endif + Random_reg, + Shape_reg, + PlotShape_reg, + PPShape_reg, + RangeVarPlot_reg, + SectionBrowser_reg, + MechanismStandard_reg, + MechanismType_reg, + NetCon_reg, + LinearMechanism_reg, + KSChan_reg, + Impedance_reg, + SaveState_reg, + BBSaveState_reg, + FInitializeHandler_reg, + StateTransitionEvent_reg, + nrnpython_reg, + NMODLRandom_reg, +#if USEDASPK + Daspk_reg, +#endif +#if USECVODE + Cvode_reg, +#endif +#if USEDSP + DSP_reg, +#endif +#if USEBBS + ParallelContext_reg, +#endif + nullptr }; + + for (int i = 0; register_classes[i]; i++) { + (*register_classes[i])(); + } +} diff --git a/src/nrniv/datapath.cpp b/src/nrniv/datapath.cpp old mode 100755 new mode 100644 index 016cc9b124..c4da677e77 --- a/src/nrniv/datapath.cpp +++ b/src/nrniv/datapath.cpp @@ -1,5 +1,484 @@ -/* - * Automake doesn't deal well with source files in a separate directory, so - * this is just a stub to trick it into working right. - */ -#include "../ivoc/datapath.cpp" +#include <../../nrnconf.h> +#include +#include +#include +#include "hoclist.h" +#if HAVE_IV +#include "graph.h" +#endif +#include "datapath.h" +#include "ivocvect.h" + +#include "nrnoc2iv.h" +#include "membfunc.h" + +#include "parse.hpp" +extern Symlist* hoc_built_in_symlist; +extern Symlist* hoc_top_level_symlist; +extern Objectdata* hoc_top_level_data; + +/*static*/ class PathValue { + public: + PathValue(); + ~PathValue() = default; + std::string path{}; + Symbol* sym; + double original; + char* str; +}; +PathValue::PathValue() { + str = NULL; + sym = NULL; +} + +class HocDataPathImpl { + private: + friend class HocDataPaths; + HocDataPathImpl(int, int); + ~HocDataPathImpl(); + + void search(); + void found(double*, const char*, Symbol*); + void found(char**, const char*, Symbol*); + PathValue* found_v(void*, const char*, Symbol*); + + void search(Objectdata*, Symlist*); + void search_vectors(); + void search_pysec(); + void search(Section*); + void search(Node*, double x); + void search(Point_process*, Symbol*); + void search(Prop*, double x); + + private: + std::map table_; + std::vector strlist_; + int size_, count_, found_so_far_; + int pathstyle_; +}; + +#define sentinal 123456789.e15 + +static Symbol *sym_vec, *sym_v, *sym_vext, *sym_rallbranch, *sym_L, *sym_Ra; + +HocDataPaths::HocDataPaths(int size, int pathstyle) { + if (!sym_vec) { + sym_vec = hoc_table_lookup("Vector", hoc_built_in_symlist); + sym_v = hoc_table_lookup("v", hoc_built_in_symlist); + sym_vext = hoc_table_lookup("vext", hoc_built_in_symlist); + sym_rallbranch = hoc_table_lookup("rallbranch", hoc_built_in_symlist); + sym_L = hoc_table_lookup("L", hoc_built_in_symlist); + sym_Ra = hoc_table_lookup("Ra", hoc_built_in_symlist); + } + impl_ = new HocDataPathImpl(size, pathstyle); +} + +HocDataPaths::~HocDataPaths() { + delete impl_; +} + +int HocDataPaths::style() { + return impl_->pathstyle_; +} + +void HocDataPaths::append(double* pd) { + // printf("HocDataPaths::append\n"); + if (pd && impl_->table_.find((void*) pd) == impl_->table_.end()) { + impl_->table_.emplace((void*) pd, new PathValue); + ++impl_->count_; + } +} + +void HocDataPaths::search() { + // printf("HocDataPaths::search\n"); + impl_->search(); + if (impl_->count_ > impl_->found_so_far_) { + // printf("HocDataPaths:: didn't find paths to all the pointers\n"); + // this has proved to be too often a false alarm since most panels are + // in boxes controlled by objects which have no reference but are + // deleted when the window is closed + } +} + +std::string HocDataPaths::retrieve(double* pd) const { + assert(impl_->pathstyle_ != 2); + // printf("HocDataPaths::retrieve\n"); + auto const it = impl_->table_.find(pd); + if (it != impl_->table_.end()) { + return it->second->path; + } + return {}; +} + +Symbol* HocDataPaths::retrieve_sym(double* pd) const { + // printf("HocDataPaths::retrieve\n"); + auto const it = impl_->table_.find(pd); + if (it != impl_->table_.end()) { + return it->second->sym; + } + return nullptr; +} + +void HocDataPaths::append(char** pd) { + // printf("HocDataPaths::append\n"); + if (*pd && impl_->table_.find((void*) pd) == impl_->table_.end()) { + PathValue* pv = new PathValue; + pv->str = *pd; + impl_->table_.emplace((void*) pd, pv); + ++impl_->count_; + } +} + +std::string HocDataPaths::retrieve(char** pd) const { + // printf("HocDataPaths::retrieve\n"); + auto const it = impl_->table_.find(pd); + if (it != impl_->table_.end()) { + return it->second->path; + } + return {}; +} + +/*------------------------------*/ +HocDataPathImpl::HocDataPathImpl(int size, int pathstyle) { + pathstyle_ = pathstyle; + size_ = size; + count_ = 0; + found_so_far_ = 0; +} + +HocDataPathImpl::~HocDataPathImpl() { + for (auto& kv: table_) { + PathValue* pv = kv.second; + delete pv; + } +} + +void HocDataPathImpl::search() { + found_so_far_ = 0; + for (auto& it: table_) { + PathValue* pv = it.second; + if (pv->str) { + char** pstr = (char**) it.first; + *pstr = nullptr; + } else { + double* pd = (double*) it.first; + pv->original = *pd; + *pd = sentinal; + } + } + if (pathstyle_ > 0) { + search(hoc_top_level_data, hoc_built_in_symlist); + search(hoc_top_level_data, hoc_top_level_symlist); + } else { + search(hoc_top_level_data, hoc_top_level_symlist); + search(hoc_top_level_data, hoc_built_in_symlist); + } + if (found_so_far_ < count_) { + search_pysec(); + } + if (found_so_far_ < count_) { + search_vectors(); + } + for (auto& it: table_) { + PathValue* pv = it.second; + if (pv->str) { + char** pstr = (char**) it.first; + *pstr = pv->str; + } else { + double* pd = (double*) it.first; + *pd = pv->original; + } + } +} + +PathValue* HocDataPathImpl::found_v(void* v, const char* buf, Symbol* sym) { + PathValue* pv; + if (pathstyle_ != 2) { + char path[500]; + std::string cs{}; + for (const auto& str: strlist_) { + Sprintf(path, "%s%s.", cs.c_str(), str.c_str()); + cs = path; + } + Sprintf(path, "%s%s", cs.c_str(), buf); + const auto& it = table_.find(v); + if (it == table_.end()) { + hoc_warning("table lookup failed for pointer for-", path); + return nullptr; + } + pv = it->second; + if (pv->path.empty()) { + pv->path = path; + pv->sym = sym; + ++found_so_far_; + } + // printf("HocDataPathImpl::found %s\n", path); + } else { + const auto& it = table_.find(v); + if (it == table_.end()) { + hoc_warning("table lookup failed for pointer for-", sym->name); + return nullptr; + } + pv = it->second; + if (!pv->sym) { + pv->sym = sym; + ++found_so_far_; + } + } + return pv; +} + +void HocDataPathImpl::found(double* pd, const char* buf, Symbol* sym) { + PathValue* pv = found_v((void*) pd, buf, sym); + if (pv) { + *pd = pv->original; + } +} + +void HocDataPathImpl::found(char** pstr, const char* buf, Symbol* sym) { + PathValue* pv = found_v((void*) pstr, buf, sym); + if (pv) { + *pstr = pv->str; + } else { + hoc_assign_str(pstr, "couldn't find"); + } +} + +void HocDataPathImpl::search(Objectdata* od, Symlist* sl) { + Symbol* sym; + int i, total; + char buf[200]; + std::string cs{}; + if (sl) + for (sym = sl->first; sym; sym = sym->next) { + if (sym->cpublic != 2) { + switch (sym->type) { + case VAR: { + double* pd; + if (sym->subtype == NOTUSER) { + pd = object_pval(sym, od); + total = hoc_total_array_data(sym, od); + } else if (sym->subtype == USERDOUBLE) { + pd = sym->u.pval; + total = 1; + } else { + break; + } + for (i = 0; i < total; ++i) { + if (pd[i] == sentinal) { + Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); + cs = buf; + found(pd + i, cs.c_str(), sym); + } + } + } break; + case STRING: { + char** pstr = object_pstr(sym, od); + if (*pstr == NULL) { + Sprintf(buf, "%s", sym->name); + cs = buf; + found(pstr, cs.c_str(), sym); + } + } break; + case OBJECTVAR: { + if (pathstyle_ > 0) { + break; + } + Object** obp = object_pobj(sym, od); + total = hoc_total_array_data(sym, od); + for (i = 0; i < total; ++i) + if (obp[i] && !obp[i]->recurse) { + cTemplate* t = (obp[i])->ctemplate; + if (!t->constructor) { + // not the this pointer + if (obp[i]->u.dataspace != od) { + Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); + cs = buf; + strlist_.push_back(cs); + obp[i]->recurse = 1; + search(obp[i]->u.dataspace, obp[i]->ctemplate->symtable); + obp[i]->recurse = 0; + strlist_.pop_back(); + } + } else { + /* point processes */ + if (t->is_point_) { + Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); + cs = buf; + strlist_.push_back(cs); + search((Point_process*) obp[i]->u.this_pointer, sym); + strlist_.pop_back(); + } + /* seclists, object lists */ + } + } + } break; + case SECTION: { + total = hoc_total_array_data(sym, od); + for (i = 0; i < total; ++i) { + hoc_Item** pitm = object_psecitm(sym, od); + if (pitm[i]) { + Sprintf(buf, "%s%s", sym->name, hoc_araystr(sym, i, od)); + cs = buf; + strlist_.push_back(cs); + search(hocSEC(pitm[i])); + strlist_.pop_back(); + } + } + } break; + case TEMPLATE: { + cTemplate* t = sym->u.ctemplate; + hoc_Item* q; + ITERATE(q, t->olist) { + Object* obj = OBJ(q); + Sprintf(buf, "%s[%d]", sym->name, obj->index); + cs = buf; + strlist_.push_back(cs); + if (!t->constructor) { + search(obj->u.dataspace, t->symtable); + } else { + if (t->is_point_) { + search((Point_process*) obj->u.this_pointer, sym); + } + } + strlist_.pop_back(); + } + } break; + } + } + } +} + +void HocDataPathImpl::search_vectors() { + char buf[200]; + std::string cs{}; + cTemplate* t = sym_vec->u.ctemplate; + hoc_Item* q; + ITERATE(q, t->olist) { + Object* obj = OBJ(q); + Sprintf(buf, "%s[%d]", sym_vec->name, obj->index); + cs = buf; + strlist_.push_back(cs); + Vect* vec = (Vect*) obj->u.this_pointer; + int size = vec->size(); + double* pd = vector_vec(vec); + for (size_t i = 0; i < size; ++i) { + if (pd[i] == sentinal) { + Sprintf(buf, "x[%zu]", i); + found(pd + i, buf, sym_vec); + } + } + strlist_.pop_back(); + } +} + +void HocDataPathImpl::search_pysec() { +#if USE_PYTHON + std::string cs{}; + hoc_Item* qsec; + // ForAllSections(sec) + ITERATE(qsec, section_list) { + Section* sec = hocSEC(qsec); + if (sec->prop && sec->prop->dparam[PROP_PY_INDEX].get()) { + cs = secname(sec); + strlist_.push_back(cs); + search(sec); + strlist_.pop_back(); + } + } +#endif +} + +void HocDataPathImpl::search(Section* sec) { + if (sec->prop->dparam[2].get() == sentinal) { + found(&(sec->prop->dparam[2].literal_value()), "L", sym_L); + } + if (sec->prop->dparam[4].get() == sentinal) { + found(&(sec->prop->dparam[4].literal_value()), "rallbranch", sym_rallbranch); + } + if (sec->prop->dparam[7].get() == sentinal) { + found(&(sec->prop->dparam[7].literal_value()), "Ra", sym_Ra); + } + if (!sec->parentsec && sec->parentnode) { + search(sec->parentnode, sec->prop->dparam[1].get()); + } + for (int i = 0; i < sec->nnode; ++i) { + search(sec->pnode[i], nrn_arc_position(sec, sec->pnode[i])); + } +} +void HocDataPathImpl::search(Node* nd, double x) { + char buf[100]; + if (NODEV(nd) == sentinal) { + Sprintf(buf, "v(%g)", x); + // the conversion below yields a pointer that is potentially invalidated + // by almost any Node operation + found(static_cast(nd->v_handle()), buf, sym_v); + } + +#if EXTRACELLULAR + if (nd->extnode) { + int i; + for (i = 0; i < nlayer; ++i) { + if (nd->extnode->v[i] == sentinal) { + if (i == 0) { + Sprintf(buf, "vext(%g)", x); + } else { + Sprintf(buf, "vext[%d](%g)", i, x); + } + found(&(nd->extnode->v[i]), buf, sym_vext); + } + } + } +#endif + + Prop* p; + for (p = nd->prop; p; p = p->next) { + if (!memb_func[p->_type].is_point) { + search(p, x); + } + } +} + +void HocDataPathImpl::search(Point_process* pp, Symbol*) { + if (pp->prop) { + search(pp->prop, -1); + } +} + +void HocDataPathImpl::search(Prop* prop, double x) { + char buf[200]; + int type = prop->_type; + Symbol* sym = memb_func[type].sym; + Symbol* psym; + double* pd; + int i, imax, k = 0, ir, kmax = sym->s_varn; + + for (k = 0; k < kmax; ++k) { + psym = sym->u.ppsym[k]; + if (psym->subtype == NRNPOINTER) { + continue; + } + ir = psym->u.rng.index; + if (memb_func[type].hoc_mech) { + pd = prop->ob->u.dataspace[ir].pval; + } else { + if (type == EXTRACELL && ir == neuron::extracellular::vext_pseudoindex()) { + // skip as it was handled by caller + continue; + } else { + pd = static_cast(prop->param_handle_legacy(ir)); + } + } + imax = hoc_total_array_data(psym, 0); + for (i = 0; i < imax; ++i) { + if (pd[i] == sentinal) { + if (x < 0) { + Sprintf(buf, "%s%s", psym->name, hoc_araystr(psym, i, 0)); + } else { + Sprintf(buf, "%s%s(%g)", psym->name, hoc_araystr(psym, i, 0), x); + } + found(pd + i, buf, psym); + } + } + } +} diff --git a/src/ivoc/datapath.h b/src/nrniv/datapath.h similarity index 100% rename from src/ivoc/datapath.h rename to src/nrniv/datapath.h diff --git a/src/nrniv/nrnclass.h b/src/nrniv/nrnclass.h deleted file mode 100644 index dcc42b1120..0000000000 --- a/src/nrniv/nrnclass.h +++ /dev/null @@ -1,46 +0,0 @@ -#include "occlass.h" -#if EXTERNS -, Shape_reg(), PlotShape_reg(), PPShape_reg(), RangeVarPlot_reg(), SectionBrowser_reg(), - MechanismStandard_reg(), MechanismType_reg(), NetCon_reg(), LinearMechanism_reg(), KSChan_reg(), - Impedance_reg(), SaveState_reg(), BBSaveState_reg(), FInitializeHandler_reg(), - StateTransitionEvent_reg(), nrnpython_reg(), NMODLRandom_reg() -#if USEDASPK - , - Daspk_reg() -#endif -#if USECVODE - , - Cvode_reg() -#endif -#if USEDSP - , - DSP_reg() -#endif -#if USEBBS - , - ParallelContext_reg() -#endif - -#else // EXTERNS - -, Shape_reg, PlotShape_reg, PPShape_reg, RangeVarPlot_reg, SectionBrowser_reg, - MechanismStandard_reg, MechanismType_reg, NetCon_reg, LinearMechanism_reg, KSChan_reg, - Impedance_reg, SaveState_reg, BBSaveState_reg, FInitializeHandler_reg, StateTransitionEvent_reg, - nrnpython_reg, NMODLRandom_reg -#if USEDASPK - , - Daspk_reg -#endif -#if USECVODE - , - Cvode_reg -#endif -#if USEDSP - , - DSP_reg -#endif -#if USEBBS - , - ParallelContext_reg -#endif -#endif // !EXTERNS diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp index 9770e24cbd..dfdfa77307 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp @@ -58,10 +58,18 @@ void write_memb_mech_types_direct(std::ostream& s) { for (int type = 2; type < n_memb_func; ++type) { const char* w = " "; Memb_func& mf = memb_func[type]; + Memb_list& ml = memb_list[type]; s << mf.sym->name << w << type << w << int(pnt_map[type]) << w // the pointtype, 0 means not a POINT_PROCESS << nrn_is_artificial_[type] << w << nrn_is_ion(type) << w << nrn_prop_param_size_[type] - << w << bbcore_dparam_size[type] << std::endl; + << w << bbcore_dparam_size[type] << w; + + int n_vars = ml.get_num_variables(); + s << n_vars; + for (size_t i = 0; i < n_vars; ++i) { + s << w << ml.get_array_dims(i); + } + s << std::endl; if (nrn_is_ion(type)) { s << nrn_ion_charge(mf.sym) << std::endl; @@ -140,7 +148,7 @@ void nrnthreads_all_weights_return(std::vector& weights) { /** @brief Return location for CoreNEURON to copy data into. * The type is mechanism type or special negative type for voltage, - * i_membrane_, or time. See coreneuron/io/nrn_setup.cpp:stdindex2ptr. + * i_membrane_, or time. See coreneuron/io/nrn_setup.cpp:legacy_index2pointer. * We allow coreneuron to copy to NEURON's AoS data as CoreNEURON knows * how its data is arranged (SoA and possibly permuted). * This function figures out the size (just for sanity check) @@ -168,16 +176,19 @@ size_t nrnthreads_type_return(int type, int tid, double*& data, std::vector 0 && type < n_memb_func) { + auto set_mdata = [type, tid, &mdata](Memb_list* ml) -> size_t { + mdata = ml->data(); + return ml->nodecount; + }; + Memb_list* ml = nt._ml_list[type]; if (ml) { - mdata = ml->data(); - n = ml->nodecount; + n = set_mdata(ml); } else { // The single thread case is easy if (nrn_nthread == 1) { ml = &memb_list[type]; - mdata = ml->data(); - n = ml->nodecount; + n = set_mdata(ml); } else { // mk_tml_with_art() created a cgs[id].mlwithart which appended // artificial cells to the end. Turns out that @@ -185,9 +196,8 @@ size_t nrnthreads_type_return(int type, int tid, double*& data, std::vectornodecount); - mdata = ml->data(); + Memb_list* ml = CellGroup::deferred_type2artml_[tid][type]; + n = set_mdata(ml); } } } @@ -384,6 +394,7 @@ int nrnthread_dat2_mech(int tid, int vdata_offset = cg.ml_vdata_offset[i]; int isart = nrn_is_artificial_[type]; int n = ml->nodecount; + int n_vars = ml->get_num_variables(); int sz = nrn_prop_param_size_[type]; // As the NEURON data is now transposed then for now always create a new @@ -393,8 +404,11 @@ int nrnthread_dat2_mech(int tid, data = new double[n * sz]; } for (auto instance = 0, k = 0; instance < n; ++instance) { - for (auto variable = 0; variable < sz; ++variable) { - data[k++] = ml->data(instance, variable); + for (int variable = 0; variable < n_vars; ++variable) { + auto array_dim = ml->get_array_dims(variable); + for (int array_index = 0; array_index < array_dim; ++array_index) { + data[k++] = ml->data(instance, variable, array_index); + } } } @@ -632,16 +646,15 @@ int* datum2int(int type, int ioff = i * sz; for (int j = 0; j < sz; ++j) { int jj = ioff + j; - int etype = di.ion_type[jj]; - int eindex = di.ion_index[jj]; + int etype = di.datum_type[jj]; + int eindex = di.datum_index[jj]; const int seman = semantics[j]; // Would probably be more clear if use seman for as many as // possible of the cases // below and within each case deal with etype appropriately. - // ion_type and ion_index have become misnomers as they no longer - // refer to ions specificially but the mechanism type where the + // datum_type and datum_index refer to mechanism type where the // range variable lives (and otherwise is generally the same as - // seman). And ion_index refers to the index of the range variable + // seman). And datum_index refers to the index of the range variable // within the mechanism (or voltage, area, etc.) if (seman == -5) { // POINTER to range variable (e.g. voltage) pdata[jj] = eindex; diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h index 8bc9941a21..bde4366977 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h @@ -16,7 +16,7 @@ typedef struct core2nrn_callback_t { CNB f; } core2nrn_callback_t; -// Mechanism type to be used from stdindex2ptr (in CoreNeuron) and nrn_dblpntr2nrncore. +// Mechanism type to be used from legacy_index2pointer (in CoreNeuron) and nrn_dblpntr2nrncore. // Values of the mechanism types should be negative numbers to avoid any conflict with // mechanism types of Memb_list(>0) or time(0) passed to CoreNeuron enum mech_type { voltage = -1, i_membrane_ = -2 }; diff --git a/src/nrniv/nrncore_write/data/cell_group.cpp b/src/nrniv/nrncore_write/data/cell_group.cpp index fe25bc1793..02a277f2ce 100644 --- a/src/nrniv/nrncore_write/data/cell_group.cpp +++ b/src/nrniv/nrncore_write/data/cell_group.cpp @@ -227,8 +227,8 @@ void CellGroup::datumtransform(CellGroup* cgs) { DatumIndices& di = cg.datumindices[i++]; di.type = type; int n = ml->nodecount * sz; - di.ion_type = new int[n]; - di.ion_index = new int[n]; + di.datum_type = new int[n]; + di.datum_index = new int[n]; // fill the indices. // had tointroduce a memb_func[i].dparam_semantics registered by each mod file. datumindex_fill(ith, cg, di, ml); @@ -361,8 +361,8 @@ void CellGroup::datumindex_fill(int ith, CellGroup& cg, DatumIndices& di, Memb_l Sprintf(errmes, "Unknown semantics type %d for dparam item %d of", dmap[j], j); hoc_execerror(errmes, memb_func[di.type].sym->name); } - di.ion_type[offset + j] = etype; - di.ion_index[offset + j] = eindex; + di.datum_type[offset + j] = etype; + di.datum_index[offset + j] = eindex; } } } diff --git a/src/nrniv/nrncore_write/data/datum_indices.cpp b/src/nrniv/nrncore_write/data/datum_indices.cpp index 93f57dd9f4..ea4027c913 100644 --- a/src/nrniv/nrncore_write/data/datum_indices.cpp +++ b/src/nrniv/nrncore_write/data/datum_indices.cpp @@ -1,13 +1,6 @@ #include "datum_indices.h" -DatumIndices::DatumIndices() { - type = -1; - ion_type = ion_index = 0; -} - DatumIndices::~DatumIndices() { - if (ion_type) - delete[] ion_type; - if (ion_index) - delete[] ion_index; + delete[] datum_type; + delete[] datum_index; } diff --git a/src/nrniv/nrncore_write/data/datum_indices.h b/src/nrniv/nrncore_write/data/datum_indices.h index 46df3ba67c..ec369ee19e 100644 --- a/src/nrniv/nrncore_write/data/datum_indices.h +++ b/src/nrniv/nrncore_write/data/datum_indices.h @@ -6,10 +6,16 @@ // NrnThread.NrnThreadMembList.Memb_List.data and pdata etc. class DatumIndices { public: - DatumIndices(); + DatumIndices() = default; virtual ~DatumIndices(); - int type; + + // These are the datum of mechanism `type`. + int type = -1; + + // `datum_index[i]` is the index of datum `i` inside the mechanism + // `datum_type[i]`. + // // ordering as though pdata[i][j] was pdata[0][i*sz+j] - int* ion_type; // negative codes semantics, positive codes mechanism type - int* ion_index; // index of range variable relative to beginning of that type + int* datum_type = nullptr; // negative codes semantics, positive codes mechanism type + int* datum_index = nullptr; // index of range variable relative to beginning of that type }; diff --git a/src/nrniv/nrncore_write/io/nrncore_io.cpp b/src/nrniv/nrncore_write/io/nrncore_io.cpp index 46e0913ce5..9336221559 100644 --- a/src/nrniv/nrncore_write/io/nrncore_io.cpp +++ b/src/nrniv/nrncore_write/io/nrncore_io.cpp @@ -22,7 +22,7 @@ extern NetCvode* net_cvode_instance; extern void (*nrnthread_v_transfer_)(NrnThread*); int chkpnt; -const char* bbcore_write_version = "1.7"; // NMODLRandom +const char* bbcore_write_version = "1.8"; // Include ArrayDims /// create directory with given path void create_dir_path(const std::string& path) { @@ -549,7 +549,7 @@ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { /** number of gids in NrnThread */ int count; nrnthread_dat3_cell_count(count); - fprintf(f, "%zd\n", count); + fprintf(f, "%d\n", count); /** all cells mapping information in NrnThread */ for (size_t i = 0; i < count; i++) { @@ -559,7 +559,7 @@ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { int n_seclist; nrnthread_dat3_cellmapping(i, cgid, t_sec, t_seg, n_seclist); /** gid, #section, #compartments, #sectionlists */ - fprintf(f, "%d %d %d %zd\n", cgid, t_sec, t_seg, n_seclist); + fprintf(f, "%d %d %d %d\n", cgid, t_sec, t_seg, n_seclist); for (size_t j = 0; j < n_seclist; j++) { std::string sclname; @@ -582,7 +582,7 @@ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { data_lfp); /** section list name, number of sections, number of segments */ fprintf(f, - "%s %d %zd %zd %d\n", + "%s %d %d %zd %d\n", sclname.c_str(), nsec, nseg, diff --git a/src/nrniv/nrncore_write/utils/nrncore_utils.cpp b/src/nrniv/nrncore_write/utils/nrncore_utils.cpp index 9267bd0c96..8e4e2d4082 100644 --- a/src/nrniv/nrncore_write/utils/nrncore_utils.cpp +++ b/src/nrniv/nrncore_write/utils/nrncore_utils.cpp @@ -122,9 +122,10 @@ void nrnbbcore_register_mapping() { mapinfo.add_sec_mapping(gid, smap); } -// This function is related to stdindex2ptr in CoreNeuron to determine which values should -// be transferred from CoreNeuron. Types correspond to the value to be transferred based on -// mech_type enum or non-artificial cell mechanisms. +// This function is related to legacy_index2pointer in CoreNeuron to determine +// which values should be transferred from CoreNeuron. Types correspond to the +// value to be transferred based on mech_type enum or non-artificial cell +// mechanisms. // Limited to pointers to voltage, nt.node_sav_rhs_storage() (fast_imem value) or // data of non-artificial cell mechanisms. // Input double* and NrnThread. Output type and index. diff --git a/src/nrniv/nrnmenu.cpp b/src/nrniv/nrnmenu.cpp index f771e9fb44..22f3ccbc1a 100644 --- a/src/nrniv/nrnmenu.cpp +++ b/src/nrniv/nrnmenu.cpp @@ -79,10 +79,6 @@ void nrnsecmenu() { hoc_retpushx(1.); } -#ifdef ultrix -char* strstr(const char*, const char*); -#endif - static bool has_globals(const char* name) { Symbol* sp; char suffix[100]; @@ -485,10 +481,6 @@ static void point_menu(Object* ob, int make_label) { } if (vartype == nrnocCONST) { deflt = true; - -#if defined(MikeNeubig) - deflt = false; -#endif // end of hack } else { deflt = false; } diff --git a/src/nrniv/ocjump.cpp b/src/nrniv/ocjump.cpp old mode 100755 new mode 100644 index 262bc50604..91712f5ec8 --- a/src/nrniv/ocjump.cpp +++ b/src/nrniv/ocjump.cpp @@ -1,5 +1,159 @@ -/* - * Automake doesn't deal well with source files in a separate directory, so - * this is just a stub to trick it into working right. - */ -#include "../ivoc/ocjump.cpp" +#include <../../nrnconf.h> + +#include "nrnfilewrap.h" +#include "nrnoc2iv.h" +#include "ocfunc.h" +#include "ocjump.h" +#if HAVE_IV +#include "ivoc.h" +#endif + +#include + +#include + +extern Objectdata* hoc_top_level_data; +extern Symlist* hoc_top_level_symlist; +extern Symlist* hoc_symlist; +extern Object* hoc_thisobject; +extern int hoc_execerror_messages; + +bool hoc_valid_stmt(const char* stmt, Object* ob) { + std::string s{stmt}; + s.append(1, '\n'); + return OcJump::execute(s.c_str(), ob); +} + +void hoc_execute1() { + Object* ob{}; + int hem{1}; + if (ifarg(2)) { + if (hoc_is_object_arg(2)) { + ob = *hoc_objgetarg(2); + if (ifarg(3)) { + hem = chkarg(3, 0., 1.); + } + } else { + hem = chkarg(2, 0., 1.); + } + } + + auto const hemold = std::exchange(hoc_execerror_messages, hem); + auto const old_mpiabort_flag = std::exchange(nrn_mpiabort_on_error_, 0); + bool const b = hoc_valid_stmt(hoc_gargstr(1), ob); + nrn_mpiabort_on_error_ = old_mpiabort_flag; + hoc_execerror_messages = hemold; + hoc_retpushx(b); +} + +#if HAVE_IV +bool Oc::valid_expr(Symbol* s) { + return OcJump::execute(s->u.u_proc->defn.in); +} + +bool Oc::valid_stmt(const char* stmt, Object* ob) { + return hoc_valid_stmt(stmt, ob); +} +#endif +//------------------------------------------------------------------ +void hoc_execute(Inst*); + +namespace { +struct saved_state { + saved_state() { + // not complete but it is good for expressions and it can be improved + oc_save_hoc_oop(&o1, &o2, &o4, &o5); + oc_save_code(&c1, &c2, c3, &c4, &c5, &c6, &c7, &c8, c9, &c10, &c11, &c12); + oc_save_input_info(&i1, &i2, &i3, &i4); + oc_save_cabcode(&cc1, &cc2); + } + void restore() { + oc_restore_hoc_oop(&o1, &o2, &o4, &o5); + oc_restore_code(&c1, &c2, c3, &c4, &c5, &c6, &c7, &c8, c9, &c10, &c11, &c12); + oc_restore_input_info(i1, i2, i3, i4); + oc_restore_cabcode(&cc1, &cc2); + } + + private: + // hoc_oop + Object* o1{}; + Objectdata* o2{}; + int o4{}; + Symlist* o5{}; + + // code + Inst* c1{}; + Inst* c2{}; + std::size_t c3{}; + nrn::oc::frame* c4{}; + int c5{}; + int c6{}; + Inst* c7{}; + nrn::oc::frame* c8{}; + std::size_t c9{}; + Symlist* c10{}; + Inst* c11{}; + int c12{}; + + // input_info + const char* i1{}; + int i2{}; + int i3{}; + NrnFILEWrap* i4{}; + + // cabcode + int cc1{}; + int cc2{}; +}; +} // namespace + +bool OcJump::execute(Inst* p) { + saved_state before{}; + try_catch_depth_increment tell_children_we_will_catch{}; + try { + hoc_execute(p); + return true; + } catch (...) { + before.restore(); + return false; + } +} + +bool OcJump::execute(const char* stmt, Object* ob) { + saved_state before{}; + try_catch_depth_increment tell_children_we_will_catch{}; + try { + hoc_obj_run(stmt, ob); + return true; + } catch (...) { + before.restore(); + return false; + } +} + +void* OcJump::fpycall(void* (*f)(void*, void*), void* a, void* b) { + saved_state before{}; + try_catch_depth_increment tell_children_we_will_catch{}; + try { + return (*f)(a, b); + } catch (...) { + before.restore(); + throw; + } +} + +ObjectContext::ObjectContext(Object* obj) { + oc_save_hoc_oop(&a1, &a2, &a4, &a5); + hoc_thisobject = obj; + if (obj) { + hoc_objectdata = obj->u.dataspace; + hoc_symlist = obj->ctemplate->symtable; + } else { + hoc_objectdata = hoc_top_level_data; + hoc_symlist = hoc_top_level_symlist; + } +} + +ObjectContext::~ObjectContext() { + oc_restore_hoc_oop(&a1, &a2, &a4, &a5); +} diff --git a/src/ivoc/ocjump.h b/src/nrniv/ocjump.h similarity index 100% rename from src/ivoc/ocjump.h rename to src/nrniv/ocjump.h diff --git a/src/nrniv/partrans.cpp b/src/nrniv/partrans.cpp index a6779bad9e..53d138887e 100644 --- a/src/nrniv/partrans.cpp +++ b/src/nrniv/partrans.cpp @@ -945,7 +945,7 @@ extern size_t nrnbbcore_gap_write(const char* path, int* group_ids); ntar // number of targets in this thread (vpre) nsrc // number of sources in this thread (v) - Note: type, index is sufficient for CoreNEURON stdindex2ptr to determine + Note: type, index is sufficient for CoreNEURON legacy_index2pointer to determine double* in its NrnThread.data array. src_sid // nsrc of these diff --git a/src/nrniv/symdir.cpp b/src/nrniv/symdir.cpp old mode 100755 new mode 100644 index eee5dfcad0..3a2de98cdf --- a/src/nrniv/symdir.cpp +++ b/src/nrniv/symdir.cpp @@ -1,5 +1,572 @@ -/* - * Automake doesn't deal well with source files in a separate directory, so - * this is just a stub to trick it into working right. - */ -#include "../ivoc/symdir.cpp" +#include <../../nrnconf.h> +#include +#include +#include +#include "ocobserv.h" +#include "utils/enumerate.h" + +#include "nrniv_mf.h" +#include "nrnoc2iv.h" + +#include "membfunc.h" +#include "parse.hpp" +#include "hoclist.h" +extern Symlist* hoc_symlist; +extern Objectdata* hoc_top_level_data; +extern Symlist *hoc_built_in_symlist, *hoc_top_level_symlist; +#include "string.h" +#include "symdir.h" + +#include "nrnsymdiritem.h" + +const char* concat(const char* s1, const char* s2) { + static char* tmp = 0; + int l1 = strlen(s1); + int l2 = strlen(s2); + if (tmp) { + delete[] tmp; + } + tmp = new char[l1 + l2 + 1]; + std::snprintf(tmp, l1 + l2 + 1, "%s%s", s1, s2); + return (const char*) tmp; +} + +class SymDirectoryImpl: public Observer { + public: + void disconnect(Observable*); // watching an object + void update(Observable*); // watching a template + private: + friend class SymDirectory; + Section* sec_; + Object* obj_; + cTemplate* t_; + + std::vector symbol_lists_; + std::string path_; + + void load(int type); + void load(int type, Symlist*); + // void load(Symbol*); + void load_section(); + void load_object(); + void load_aliases(); + void load_template(); + void load_mechanism(const Prop*, int, const char*); + void append(Symbol* sym, Objectdata* od, Object* o = NULL); + void append(Object*); + void un_append(Object*); + void make_pathname(const char*, const char*, const char*, int s = '.'); + void sort(); +}; + +static int compare_entries(const SymbolItem* e1, const SymbolItem* e2) { + int i = strcmp(e1->name().c_str(), e2->name().c_str()); + if (i == 0) { + return e1->array_index() > e2->array_index(); + } + return i > 0; +}; + +void SymDirectoryImpl::sort() { + std::sort(symbol_lists_.begin(), symbol_lists_.end(), compare_entries); +} + +// SymDirectory +SymDirectory::SymDirectory(const std::string& parent_path, + Object* parent_obj, + Symbol* sym, + int array_index, + int) { + impl_ = new SymDirectoryImpl(); + impl_->sec_ = NULL; + impl_->obj_ = NULL; + impl_->t_ = NULL; + Objectdata* obd; + if (parent_obj) { + obd = parent_obj->u.dataspace; + } else { + // obd = hoc_objectdata; + obd = hoc_top_level_data; + } + int suffix = '.'; + if (sym->type == TEMPLATE) { + suffix = '_'; + } + impl_->make_pathname(parent_path.c_str(), + sym->name, + hoc_araystr(sym, array_index, obd), + suffix); + switch (sym->type) { + case SECTION: + if (object_psecitm(sym, obd)[array_index]) { + impl_->sec_ = hocSEC(object_psecitm(sym, obd)[array_index]); + section_ref(impl_->sec_); + impl_->load_section(); + } + break; + case OBJECTVAR: + impl_->obj_ = object_pobj(sym, obd)[array_index]; + if (impl_->obj_) { + ObjObservable::Attach(impl_->obj_, impl_); + impl_->load_object(); + } + break; + case TEMPLATE: + impl_->t_ = sym->u.ctemplate; + ClassObservable::Attach(impl_->t_, impl_); + impl_->load_template(); + break; + case OBJECTALIAS: + impl_->obj_ = sym->u.object_; + if (impl_->obj_) { + ObjObservable::Attach(impl_->obj_, impl_); + impl_->load_object(); + } + break; + default: + hoc_execerror("Don't know how to make a directory out of", path().c_str()); + break; + } + impl_->sort(); +} +SymDirectory::SymDirectory(Object* ob) { + impl_ = new SymDirectoryImpl(); + impl_->sec_ = NULL; + impl_->obj_ = ob; + impl_->t_ = NULL; + int suffix = '.'; + impl_->make_pathname("", hoc_object_name(ob), "", '.'); + ObjObservable::Attach(impl_->obj_, impl_); + impl_->load_object(); + impl_->sort(); +} + +bool SymDirectory::is_pysec(int index) const { + SymbolItem* si = impl_->symbol_lists_.at(index); + return si->pysec_ ? true : false; +} +SymDirectory* SymDirectory::newsymdir(int index) { + SymbolItem* si = impl_->symbol_lists_.at(index); + SymDirectory* d = new SymDirectory(); + if (si->pysec_type_ == PYSECOBJ) { + nrn_symdir_load_pysec(d->impl_->symbol_lists_, si->pysec_); + } else { + d->impl_->sec_ = (Section*) si->pysec_; + section_ref(d->impl_->sec_); + d->impl_->load_section(); + } + d->impl_->path_ = concat(path().c_str(), si->name().c_str()); + d->impl_->path_ = concat(d->impl_->path_.c_str(), "."); + d->impl_->sort(); + return d; +} + +SymDirectory::SymDirectory() { + impl_ = new SymDirectoryImpl(); + impl_->sec_ = NULL; + impl_->obj_ = NULL; + impl_->t_ = NULL; +} + +SymDirectory::SymDirectory(int type) { + ParseTopLevel ptl; + ptl.save(); + impl_ = new SymDirectoryImpl(); + impl_->sec_ = NULL; + impl_->obj_ = NULL; + impl_->t_ = NULL; + impl_->path_ = ""; + impl_->load(type); + impl_->sort(); + ptl.restore(); +} + +SymDirectory::~SymDirectory() { + for (auto& item: impl_->symbol_lists_) { + delete item; + } + impl_->symbol_lists_.clear(); + impl_->symbol_lists_.shrink_to_fit(); + if (impl_->obj_) { + ObjObservable::Detach(impl_->obj_, impl_); + } + if (impl_->t_) { + ClassObservable::Detach(impl_->t_, impl_); + } + if (impl_->sec_) { + section_unref(impl_->sec_); + } + delete impl_; +} +void SymDirectoryImpl::disconnect(Observable*) { + for (auto& item: symbol_lists_) { + delete item; + } + symbol_lists_.clear(); + symbol_lists_.shrink_to_fit(); + obj_ = NULL; +} + +void SymDirectoryImpl::update(Observable* obs) { + if (t_) { // watching a template + ClassObservable* co = (ClassObservable*) obs; + Object* ob = co->object(); + switch (co->message()) { + case ClassObservable::Delete: + un_append(ob); + break; + case ClassObservable::Create: + append(ob); + break; + } + } +} +double* SymDirectory::variable(int index) { + Object* ob = object(); + Symbol* sym = symbol(index); + // printf("::variable index=%d sym=%s ob=%s\n", index, + // sym?sym->name:"SYM0",hoc_object_name(ob)); + if (sym) + switch (sym->type) { + case VAR: + if (ob && ob->ctemplate->constructor) { + extern double* ivoc_vector_ptr(Object*, int); + if (is_obj_type(ob, "Vector")) { + return ivoc_vector_ptr(ob, index); + } else { + return NULL; + } + } else { + Objectdata* od; + if (ob) { + od = ob->u.dataspace; + } else if (sym->subtype == USERDOUBLE) { + return sym->u.pval + array_index(index); + } else { + od = hoc_objectdata; + } + return od[sym->u.oboff].pval + array_index(index); + } + case RANGEVAR: + if (ob && ob->ctemplate->is_point_) { + return static_cast(point_process_pointer( + (Point_process*) ob->u.this_pointer, sym, array_index(index))); + } + break; + } + else { + char buf[256], *cp; + Sprintf(buf, "%s%s", path().c_str(), name(index).c_str()); + if (whole_vector(index)) { // rangevar case for [all] + // replace [all] with [0] + cp = strstr(buf, "[all]"); + assert(cp); + *(++cp) = '0'; + for (++cp; cp[2]; ++cp) { + *cp = cp[2]; + } + *cp = '\0'; + } + return hoc_val_pointer(buf); + } + return NULL; +} + +int SymDirectory::whole_vector(int index) { + return impl_->symbol_lists_.at(index)->whole_vector(); +} + +const std::string& SymDirectory::path() const { + return impl_->path_; +} +int SymDirectory::count() const { + return impl_->symbol_lists_.size(); +} +const std::string& SymDirectory::name(int index) const { + return impl_->symbol_lists_.at(index)->name(); +} +int SymDirectory::array_index(int i) const { + return impl_->symbol_lists_.at(i)->array_index(); +} + +int SymDirectory::index(const std::string& name) const { + for (const auto&& [i, symbol]: enumerate(impl_->symbol_lists_)) { + if (name == symbol->name()) { + return i; + } + } + return -1; +} +void SymDirectory::whole_name(int index, std::string& s) const { + auto s1 = impl_->path_; + auto s2 = name(index); + s = s1 + s2; +} +bool SymDirectory::is_directory(int index) const { + return impl_->symbol_lists_.at(index)->is_directory(); +} +bool SymDirectory::match(const std::string&, const std::string&) { + return true; +} +Symbol* SymDirectory::symbol(int index) const { + return impl_->symbol_lists_.at(index)->symbol(); +} +Object* SymDirectory::object() const { + return impl_->obj_; +} + +Object* SymDirectory::obj(int index) { + return impl_->symbol_lists_.at(index)->object(); +} + +// SymbolItem +SymbolItem::SymbolItem(const char* n, int whole_array) { + symbol_ = NULL; + index_ = 0; + ob_ = NULL; + name_ = n; + whole_array_ = whole_array; + pysec_type_ = 0; + pysec_ = NULL; +} +SymbolItem::SymbolItem(Symbol* sym, Objectdata* od, int index, int whole_array) { + symbol_ = sym; + ob_ = NULL; + whole_array_ = whole_array; + if (ISARRAY(sym)) { + if (whole_array_) { + name_ = concat(sym->name, "[all]"); + } else { + if (od) { + name_ = concat(sym->name, hoc_araystr(sym, index, od)); + } else { + char buf[50]; + Sprintf(buf, "[%d]", index); + name_ = concat(sym->name, buf); + } + } + } else { + name_ = sym->name; + } + index_ = index; + pysec_type_ = 0; + pysec_ = NULL; +} + +int SymbolItem::whole_vector() { + return whole_array_; +} + +SymbolItem::SymbolItem(Object* ob) { + symbol_ = NULL; + index_ = 0; + ob_ = ob; + char buf[10]; + Sprintf(buf, "%d", ob->index); + name_ = buf; + pysec_type_ = 0; + pysec_ = NULL; +} + +void SymbolItem::no_object() { + ob_ = NULL; + name_ = "Deleted"; +} + +SymbolItem::~SymbolItem() {} + +bool SymbolItem::is_directory() const { + if (symbol_) + switch (symbol_->type) { + case SECTION: + case OBJECTVAR: + case TEMPLATE: + case OBJECTALIAS: + // case SECTIONLIST: + // case MECHANISM: + return true; + } + if (ob_) { + return true; + } + if (pysec_) { + return true; + } + return false; +} + +void SymDirectoryImpl::make_pathname(const char* parent, + const char* name, + const char* index, + int suffix) { + char buf[200]; + Sprintf(buf, "%s%s%s%c", parent, name, index, suffix); + path_ = buf; +} + + +void SymDirectoryImpl::load(int type) { + switch (type) { + case TEMPLATE: + load(type, hoc_built_in_symlist); + load(type, hoc_top_level_symlist); + break; + case RANGEVAR: + load(type, hoc_built_in_symlist); + break; + case PYSEC: + path_ = "_pysec."; + nrn_symdir_load_pysec(symbol_lists_, NULL); + break; + default: + load(type, hoc_symlist); + if (hoc_symlist != hoc_built_in_symlist) { + Objectdata* sav = hoc_objectdata; + hoc_objectdata = NULL; + load(type, hoc_built_in_symlist); + hoc_objectdata = sav; + } + if (hoc_symlist != hoc_top_level_symlist) { + load(type, hoc_top_level_symlist); + } + } +} + +void SymDirectoryImpl::load(int type, Symlist* sl) { + for (Symbol* sym = sl->first; sym; sym = sym->next) { + if (type == -1) { + switch (sym->type) { + case SECTION: + case OBJECTVAR: + case VAR: + case TEMPLATE: + append(sym, hoc_objectdata); + } + } else if (sym->type == type) { + append(sym, hoc_objectdata); + } + } +} + +void SymDirectoryImpl::load_object() { + Symlist* sl = obj_->ctemplate->symtable; + Objectdata* od; + if (obj_->ctemplate->constructor) { + od = NULL; + } else { + od = obj_->u.dataspace; + } + if (obj_->aliases) { + load_aliases(); + } + if (sl) + for (Symbol* s = sl->first; s; s = s->next) { + if (s->cpublic) { + append(s, od, obj_); + } + } +} + +void SymDirectoryImpl::load_aliases() { + IvocAliases* a = (IvocAliases*) obj_->aliases; + if (!a) + return; + for (const auto& [_, s]: a->symtab_) { + append(s, nullptr, obj_); + } +} + +void SymDirectoryImpl::load_template() { + hoc_Item* q; + ITERATE(q, t_->olist) { + append(OBJ(q)); + } +} + +void SymDirectoryImpl::load_section() { + char xarg[20]; + char buf[100]; + Section* sec = sec_; + int n = sec->nnode; + + int i = 0; + double x = nrn_arc_position(sec, sec->pnode[0]); + Sprintf(xarg, "( %g )", x); + Sprintf(buf, "v%s", xarg); + symbol_lists_.push_back(new SymbolItem(buf)); + nrn_pushsec(sec); + Node* nd = sec->pnode[i]; + for (const Prop* p = nd->prop; p; p = p->next) { + load_mechanism(p, 0, xarg); + } + nrn_popsec(); +} + +void SymDirectoryImpl::load_mechanism(const Prop* p, int vartype, const char* xarg) { + int type = p->_type; + if (memb_func[type].is_point) { + return; + } + char buf[200]; + Symbol* msym = memb_func[type].sym; + int cnt = msym->s_varn; + for (int i = 0; i < cnt; ++i) { + const Symbol* sym = msym->u.ppsym[i]; + if (nrn_vartype(sym) == vartype || vartype == 0) { + if (ISARRAY(sym)) { + int n = hoc_total_array_data(sym, 0); + if (n > 5) { + Sprintf(buf, "%s[all]%s", sym->name, xarg); + symbol_lists_.push_back(new SymbolItem(buf, n)); + } + Sprintf(buf, "%s[%d]%s", sym->name, 0, xarg); + symbol_lists_.push_back(new SymbolItem(buf)); + Sprintf(buf, "%s[%d]%s", sym->name, n - 1, xarg); + symbol_lists_.push_back(new SymbolItem(buf)); + } else { + Sprintf(buf, "%s%s", sym->name, xarg); + symbol_lists_.push_back(new SymbolItem(buf)); + } + } + } +} + +void SymDirectoryImpl::append(Symbol* sym, Objectdata* od, Object* o) { + if (ISARRAY(sym)) { + int i, n = 1; + if (od) { + n = hoc_total_array_data(sym, od); + } else { // Vector + if (is_obj_type(o, "Vector")) { + extern int ivoc_vector_size(Object*); + n = ivoc_vector_size(o); + } + } + if (n > 5 && sym->type == VAR) { + symbol_lists_.push_back(new SymbolItem(sym, od, 0, n)); + } + for (i = 0; i < n; ++i) { + symbol_lists_.push_back(new SymbolItem(sym, od, i)); + if (i > 5) { + break; + } + } + if (i < n - 1) { + symbol_lists_.push_back(new SymbolItem(sym, od, n - 1)); + } + } else { + symbol_lists_.push_back(new SymbolItem(sym, od, 0)); + } +} + +void SymDirectoryImpl::append(Object* ob) { + symbol_lists_.push_back(new SymbolItem(ob)); +} +void SymDirectoryImpl::un_append(Object* ob) { + for (auto& symbol: symbol_lists_) { + if (symbol->object() == ob) { + symbol->no_object(); + break; + } + } +} diff --git a/src/ivoc/symdir.h b/src/nrniv/symdir.h similarity index 100% rename from src/ivoc/symdir.h rename to src/nrniv/symdir.h diff --git a/src/nrnoc/extcelln.cpp b/src/nrnoc/extcelln.cpp index 61eb0900a7..b6facf2b76 100644 --- a/src/nrnoc/extcelln.cpp +++ b/src/nrnoc/extcelln.cpp @@ -86,7 +86,8 @@ static void update_parmsize() { #endif ); // clang-format on - hoc_register_prop_size(EXTRACELL, nparm, 0); + int prop_size = nparm + 3 * (nrn_nlayer_extracellular - 1); + hoc_register_prop_size(EXTRACELL, prop_size, 0); } static std::vector param_default{ diff --git a/src/nrnoc/fadvance.cpp b/src/nrnoc/fadvance.cpp index 11dc9b89df..a8a06f314e 100644 --- a/src/nrnoc/fadvance.cpp +++ b/src/nrnoc/fadvance.cpp @@ -8,6 +8,7 @@ #include "nrn_ansi.h" #include "nrniv_mf.h" #include "multisplit.h" +#include "node_order_optim/node_order_optim.h" #define nrnoc_fadvance_c #include "utils/profile/profiler_interface.h" #include "nonvintblock.h" @@ -474,7 +475,11 @@ static void nrn_fixed_step_thread(neuron::model_sorted_token const& cache_token, setup_tree_matrix(cache_token, nt); { nrn::Instrumentor::phase p("matrix-solver"); - nrn_solve(nth); + if (neuron::interleave_permute_type) { + neuron::solve_interleaved(nt.id); + } else { + nrn_solve(nth); + } } { nrn::Instrumentor::phase p("second-order-cur"); diff --git a/src/nrnoc/init.cpp b/src/nrnoc/init.cpp index ce86426495..7b47891358 100644 --- a/src/nrnoc/init.cpp +++ b/src/nrnoc/init.cpp @@ -22,6 +22,8 @@ #include #include +#include +namespace fs = std::filesystem; /* change this to correspond to the ../nmodl/nocpout nmodl_version_ string*/ static char nmodl_version_[] = "7.7.0"; @@ -225,40 +227,23 @@ int nrn_is_cable(void) { } void* nrn_realpath_dlopen(const char* relpath, int flags) { - char* abspath = NULL; - void* handle = NULL; - - /* use realpath or _fullpath even if is already a full path */ - -#if defined(HAVE_REALPATH) - abspath = realpath(relpath, NULL); -#else /* not HAVE_REALPATH */ -#if defined(__MINGW32__) - abspath = _fullpath(NULL, relpath, 0); -#else /* not __MINGW32__ */ - abspath = strdup(relpath); -#endif /* not __MINGW32__ */ -#endif /* not HAVE_REALPATH */ - if (abspath) { - handle = dlopen(abspath, flags); + void* handle = nullptr; + try { // Try with an absolute path otherwise relpath + auto abspath = fs::absolute(relpath); + handle = dlopen(abspath.string().c_str(), flags); #if DARWIN if (!handle) { - nrn_possible_mismatched_arch(abspath); + nrn_possible_mismatched_arch(abspath.c_str()); } -#endif // DARWIN - free(abspath); - } else { - int patherr = errno; +#endif + } catch (const std::filesystem::filesystem_error& e) { handle = dlopen(relpath, flags); if (!handle) { - Fprintf(stderr, - "realpath failed errno=%d (%s) and dlopen failed with %s\n", - patherr, - strerror(patherr), - relpath); + std::cerr << "std::filesystem::absolute failed (" << e.what() + << ") and dlopen failed with '" << relpath << "'" << std::endl; #if DARWIN - nrn_possible_mismatched_arch(abspath); -#endif // DARWIN + nrn_possible_mismatched_arch(relpath); +#endif } } return handle; @@ -874,10 +859,23 @@ void register_data_fields(int mechtype, register_data_fields(mechtype, params, dparams); } + +// Count the number of floating point variables. +// +// An array variable with N elements counts as N floating point variables. +static int count_prop_param_size(const std::vector>& param_info) { + int float_variables = 0; + for (const auto& [i, n]: param_info) { + float_variables += n; + } + + return float_variables; +} + void register_data_fields(int mechtype, std::vector> const& param_info, std::vector> const& dparam_info) { - nrn_prop_param_size_[mechtype] = param_info.size(); + nrn_prop_param_size_[mechtype] = count_prop_param_size(param_info); nrn_prop_dparam_size_[mechtype] = dparam_info.size(); delete[] std::exchange(memb_func[mechtype].dparam_semantics, nullptr); if (!dparam_info.empty()) { diff --git a/src/nrnoc/memblist.cpp b/src/nrnoc/memblist.cpp index 6e83477441..f8fe6677d1 100644 --- a/src/nrnoc/memblist.cpp +++ b/src/nrnoc/memblist.cpp @@ -53,6 +53,32 @@ neuron::container::data_handle Memb_list::data_handle( } +[[nodiscard]] int Memb_list::get_num_variables() const { + using Tag = neuron::container::Mechanism::field::FloatingPoint; + return m_storage->get_num_variables(); +} + +[[nodiscard]] int Memb_list::get_array_dims(int variable) const { + using Tag = neuron::container::Mechanism::field::FloatingPoint; + return m_storage->get_array_dims()[variable]; +} + +[[nodiscard]] int const* Memb_list::get_array_dims() const { + using Tag = neuron::container::Mechanism::field::FloatingPoint; + return m_storage->get_array_dims(); +} + +[[nodiscard]] int Memb_list::get_array_prefix_sums(int variable) const { + using Tag = neuron::container::Mechanism::field::FloatingPoint; + return m_storage->get_array_dim_prefix_sums()[variable]; +} + +[[nodiscard]] int const* Memb_list::get_array_prefix_sums() const { + using Tag = neuron::container::Mechanism::field::FloatingPoint; + return m_storage->get_array_dim_prefix_sums(); +} + + [[nodiscard]] std::ptrdiff_t Memb_list::legacy_index(double const* ptr) const { assert(m_storage_offset != neuron::container::invalid_row); // For a mechanism with (in order) range variables: a, b[2], c the mechanism data are diff --git a/src/nrnoc/nrnoc_ml.h b/src/nrnoc/nrnoc_ml.h index 6cf6d4397a..34e4134745 100644 --- a/src/nrnoc/nrnoc_ml.h +++ b/src/nrnoc/nrnoc_ml.h @@ -112,6 +112,33 @@ struct Memb_list { int variable, int array_index = 0) const; + + /** + * @brief Get the number of fields/variables of this mechanism. + */ + [[nodiscard]] int get_num_variables() const; + + /** + * @brief Get the array_dims of field `variable`. + */ + [[nodiscard]] int get_array_dims(int variable) const; + + /** + * @brief Get the array_dims of field `variable`. + */ + [[nodiscard]] int const* get_array_dims() const; + + /** + * @brief Get the array_dims of field `variable`. + */ + [[nodiscard]] int get_array_prefix_sums(int variable) const; + + /** + * @brief Get the array_dims of field `variable`. + */ + [[nodiscard]] int const* get_array_prefix_sums() const; + + /** * @brief Calculate a legacy index of the given pointer in this mechanism data. * diff --git a/src/nrnoc/treeset.cpp b/src/nrnoc/treeset.cpp index 16642afaa7..86e1e6992d 100644 --- a/src/nrnoc/treeset.cpp +++ b/src/nrnoc/treeset.cpp @@ -9,6 +9,7 @@ #include "neuron/cache/mechanism_range.hpp" #include "neuron/cache/model_data.hpp" #include "neuron/container/soa_container.hpp" +#include "node_order_optim/node_order_optim.h" #include "nonvintblock.h" #include "nrndae_c.h" #include "nrniv_mf.h" @@ -1757,12 +1758,43 @@ void v_setup_vectors(void) { } } neuron::model().node_data().mark_as_unsorted(); + // The assumption here is that one can arbitrarily permute the + // NrnThread node order (within the constraint that + // parent index < node index). A NrnThread node order permutation + // involves modifying NrnThread fields: _v_node, _v_parent, v_parent_index, + // and Node.v_node_index. + // Additionally, there appears to be a Memb_list node_indices ordering + // presumption embodied in nrn_sort_mech_data. I.e. Preexisting Memb_list node + // order is the order that results by iterating over Nrnthread nodes + // asserted by ml->nodelist[nt_mech_count] == nd. + // We this by monotonically ordering the Memb_list with + // sort_ml(Memb_list*) but there is a question whether the sort preserves + // the order when there are many POINT_PROCESS instances in the same node. + neuron::nrn_permute_node_order(); + v_structure_change = 0; nrn_update_ps2nt(); ++structure_change_cnt; long_difus_solve(nrn_ensure_model_data_are_sorted(), 3, *nrn_threads); // !!! nrn_nonvint_block_setup(); diam_changed = 1; + +#if 0 + for (int tid = 0; tid < nrn_nthread; ++tid) { + printf("nrnthread %d node info\n", tid); + auto& nt = nrn_threads[tid]; + for (int i = 0; i < nt.end; ++i) { + printf( + " _v_node[%2d]->v_node_index=%2d" + " _v_parent[%2d]->v_node_index=%2d v=%g\n", + i, + nt._v_node[i]->v_node_index, + i, + nt._v_parent[i] ? nt._v_parent[i]->v_node_index : -1, + (*nt._v_node[i]).v()); + } + } +#endif // 0 } diff --git a/src/nrnpython/nrnpython.cpp b/src/nrnpython/nrnpython.cpp index 2097630f63..2cd3e7979c 100644 --- a/src/nrnpython/nrnpython.cpp +++ b/src/nrnpython/nrnpython.cpp @@ -16,6 +16,7 @@ #include #include #include +#include extern HocStr* hoc_cbufstr; extern int nrnpy_nositeflag; extern std::string nrnpy_pyexe; @@ -119,16 +120,59 @@ static void nrnpython_set_path(std::string_view fname) { * @return 0 on failure, 1 on success. */ int nrnpy_pyrun(const char* fname) { - nrnpython_set_path(fname); - auto* const fp = fopen(fname, "r"); + auto* fp = fopen(fname, "r"); + if (fp) { + nrnpython_set_path(fname); + } else { + std::cerr << "Could not open " << fname << std::endl; + return 0; + } + fclose(fp); +#if !defined(MINGW) + fp = fopen(fname, "r"); if (fp) { int const code = PyRun_AnyFile(fp, fname); fclose(fp); return !code; - } else { - std::cerr << "Could not open " << fname << std::endl; + } + return 0; +#else // MINGW + // MINGW and Python have incompatible FILE* so try to accomplish + // with pure Python + std::string exec{"with open('"}; + exec += fname; + exec += + "', 'rb') as nrnmingw_file:" + " exec(nrnmingw_file.read(), globals())\n"; + int const code = PyRun_SimpleString(exec.c_str()); + if (code) { + PyErr_Print(); return 0; } + PyRun_SimpleString("del nrnmingw_file\n"); + return 1; +#endif // MINGW +} + +/** + * @brief Like a PyRun_InteractiveLoop that does not need a FILE* + * Use InteractiveConsole to work around the issue of mingw FILE* + * not being compatible with Python via the CAPI on windows11. + * @return 0 on success, nonzero on failure. + */ +static int nrnmingw_pyrun_interactiveloop() { + int code{}; + std::string lines[3]{ + "import code as nrnmingw_code\n", + "nrnmingw_interpreter = nrnmingw_code.InteractiveConsole(locals=globals())\n", + "nrnmingw_interpreter.interact(\"\")\n"}; + for (const auto& line: lines) { + if (PyRun_SimpleString(line.c_str())) { + PyErr_Print(); + return -1; + } + } + return 0; } extern PyObject* nrnpy_hoc(); @@ -265,10 +309,15 @@ static int nrnpython_start(int b) { // There used to be a call to PySys_SetArgv here, which dates back to // e48d933e03b5c25a454e294deea55e399f8ba1b1 and a comment about sys.argv not being set with // nrniv -python. Today, it seems like this is not needed any more. -#if !defined(MINGW) - // cannot get this to avoid crashing with MINGW + + // Used to crash with MINGW when assocated with a python gui thread e.g + // from neuron import h, gui + // g = h.Graph() + // del g + // Also, NEURONMainMenu/File/Quit did not work. The solution to both + // seems to be to just avoid gui threads if MINGW and launched nrniv PyOS_ReadlineFunctionPointer = nrnpython_getline; -#endif + // Is there a -c "command" or file.py arg. bool python_error_encountered{false}, have_reset_sys_path{false}; for (int i = 1; i < nrn_global_argc; ++i) { @@ -299,7 +348,15 @@ static int nrnpython_start(int b) { // it. reset_sys_path(""); } +#if !defined(MINGW) PyRun_InteractiveLoop(hoc_fin, "stdin"); +#else + // mingw FILE incompatible with windows11 Python FILE. + int ret = nrnmingw_pyrun_interactiveloop(); + if (ret) { + python_error_encountered = ret; + } +#endif } return python_error_encountered; } diff --git a/src/oc/audit.cpp b/src/oc/audit.cpp index 33fedaceef..3b26128078 100644 --- a/src/oc/audit.cpp +++ b/src/oc/audit.cpp @@ -221,13 +221,7 @@ static void xopen_audit(void) { #endif } -#ifdef NeXT -int hoc_retrieve_audit(int id) /* I have no idea why... CMC */ -#else -int hoc_retrieve_audit(int id) - -#endif -{ +int hoc_retrieve_audit(int id) { #if !OCSMALL RetrieveAudit save; char buf[200]; diff --git a/src/oc/classreg.h b/src/oc/classreg.h index fbb4beb706..4a1893c4af 100644 --- a/src/oc/classreg.h +++ b/src/oc/classreg.h @@ -1,10 +1,8 @@ #pragma once -#include #include #include - extern void class2oc(const char*, void* (*cons)(Object*), void (*destruct)(void*), diff --git a/src/oc/debug.cpp b/src/oc/debug.cpp index 415f5af9fd..a8b511eb6d 100644 --- a/src/oc/debug.cpp +++ b/src/oc/debug.cpp @@ -6,11 +6,7 @@ #include int zzdebug; -#if DOS -#define prcod(c1, c2) else if (p->pf == c1) Printf("%p %p %s", p, p->pf, c2) -#else #define prcod(c1, c2) else if (p->pf == c1) Printf("%p %p %s", p, p->pf, c2) -#endif void debug(void) /* print the machine */ { diff --git a/src/oc/hoc.cpp b/src/oc/hoc.cpp index 878f29b963..322522c507 100644 --- a/src/oc/hoc.cpp +++ b/src/oc/hoc.cpp @@ -188,7 +188,7 @@ static int backslash(int c); #endif #if HAS_SIGPIPE /*ARGSUSED*/ -static RETSIGTYPE sigpipe_handler(int sig) { +static void sigpipe_handler(int sig) { fprintf(stderr, "writing to a broken pipe\n"); signal(SIGPIPE, sigpipe_handler); } @@ -653,7 +653,7 @@ void hoc_execerror(const char* s, const char* t) /* recover from run-time error hoc_execerror_mes(s, t, hoc_execerror_messages); } -RETSIGTYPE onintr(int sig) /* catch interrupt */ +void onintr(int /* sig */) /* catch interrupt */ { /*ARGSUSED*/ stoprun = 1; @@ -728,7 +728,7 @@ void print_bt() { #endif } -RETSIGTYPE fpecatch(int sig) /* catch floating point exceptions */ +void fpecatch(int /* sig */) /* catch floating point exceptions */ { /*ARGSUSED*/ #if DOS @@ -746,7 +746,8 @@ RETSIGTYPE fpecatch(int sig) /* catch floating point exceptions */ execerror("Floating point exception.", (char*) 0); } -RETSIGTYPE sigsegvcatch(int sig) /* segmentation violation probably due to arg type error */ +__attribute__((noreturn)) void sigsegvcatch(int /* sig */) /* segmentation violation probably due to + arg type error */ { Fprintf(stderr, "Segmentation violation\n"); print_bt(); @@ -758,7 +759,7 @@ RETSIGTYPE sigsegvcatch(int sig) /* segmentation violation probably due to arg t } #if HAVE_SIGBUS -RETSIGTYPE sigbuscatch(int sig) { +__attribute__((noreturn)) void sigbuscatch(int /* sig */) { Fprintf(stderr, "Bus error\n"); print_bt(); /*ARGSUSED*/ @@ -984,7 +985,9 @@ void hoc_final_exit(void) { std::string cmd{neuron_home}; cmd += "/lib/cleanup "; cmd += std::to_string(hoc_pid()); - system(cmd.c_str()); + if (system(cmd.c_str())) { // fix warning: ignoring return value + return; + } #endif } @@ -1168,9 +1171,8 @@ int hoc_moreinput() { return 1; } -typedef RETSIGTYPE (*SignalType)(int); - -static SignalType signals[4]; +using SignalType = void(int); +static SignalType* signals[4]; static void set_signals(void) { signals[0] = signal(SIGINT, onintr); diff --git a/src/oc/macprt.cpp b/src/oc/macprt.cpp deleted file mode 100644 index 683bb36a6b..0000000000 --- a/src/oc/macprt.cpp +++ /dev/null @@ -1,183 +0,0 @@ - -#include -#include -#include -#include -#include -#include "hoc.h" - -#include "gui-redirect.h" - -extern void debugfile(const char*, ...); -extern int oc_print_from_dll(char*); -extern void single_event_run(); - -extern char* neuron_home; - -int oc_print_from_dll(char* buf) { /* interchange \n and \r !*/ - char* cp; - for (cp = buf; *cp != '\0'; ++cp) { /* safe because buf is already a temp buffer */ - if (*cp == '\n') { - *cp = '\r'; - } else if (*cp == '\r') { - *cp = '\n'; - } - } - return printf("%s", buf); -} - -/* jijun 4/22/97, 4/23/97 */ -extern void setneuronhome(const char* p) { - CInfoPBRec myPB; - short vRefNum; - long dirID; - Str255 dirName; - char prePath[256]; - static char fullPath[256]; - - OSErr err = HGetVol((0), &vRefNum, &dirID); - if (err == noErr) { - myPB.dirInfo.ioNamePtr = dirName; - myPB.dirInfo.ioVRefNum = vRefNum; - myPB.dirInfo.ioDrParID = dirID; - myPB.dirInfo.ioFDirIndex = -1; - do { - myPB.dirInfo.ioDrDirID = myPB.dirInfo.ioDrParID; - err = PBGetCatInfoSync(&myPB); - if (err == noErr) { - dirName[dirName[0] + 1] = '\0'; - strcpy(prePath, &dirName[1]); - strcat(prePath, ":"); - strcat(prePath, fullPath); - strcpy(fullPath, prePath); - } - } while (myPB.dirInfo.ioDrDirID > 2); - } - - neuron_home = fullPath; - // get rid of last ':' - neuron_home[strlen(neuron_home) - 1] = '\0'; - // debugfile("neuron_home = %s\n", neuron_home); -} - -char* getenv(const char* s) { - static char buf[200]; - if (strcmp(s, "NEURONHOME") == 0) { - return neuron_home; - } - if (strcmp(s, "NRNDEMO") == 0) { - strcpy(buf, neuron_home); - strcat(buf, ":demo:"); - return buf; - } - printf("getenv: don't know |%s|\n", s); - return 0; -} - -int hoc_copyfile(const char* src, const char* dest) { - return 0; -} - -void hoc_check_intupt(int intupt) { -#if 1 - extern void set_intset(); - EventRecord e; - if (EventAvail(keyDownMask, &e)) { - // debugfile("%d\n", e.what); - if (e.what == keyDown) { - char c = e.message & charCodeMask; - if (c == 0x03) { - set_intset(); - } - } - // single_event_run(); - } -#endif -} - -FILE* popen(char* s1, char* s2) { - printf("no popen\n"); - return 0; -} - -pclose(FILE* p) { - printf("no pclose\n"); -} - -hoc_win_normal_cursor() {} - -hoc_win_wait_cursor() {} - -void plprint(const char* s) { - printf("%s", s); -} -int hoc_plttext; -/* -int getpid() { - return 1; -} -*/ -hoc_close_plot() {} -hoc_Graphmode() { - TRY_GUI_REDIRECT_DOUBLE("graphmode", NULL); - ret(); - pushx(0.); -} -hoc_Graph() { - TRY_GUI_REDIRECT_DOUBLE("graph", NULL); - ret(); - pushx(0.); -} -hoc_regraph() { - TRY_GUI_REDIRECT_DOUBLE("regraph", NULL); - ret(); - pushx(0.); -} -hoc_plotx() { - TRY_GUI_REDIRECT_DOUBLE("plotx", NULL); - ret(); - pushx(0.); -} -hoc_ploty() { - TRY_GUI_REDIRECT_DOUBLE("ploty", NULL); - ret(); - pushx(0.); -} -hoc_Plt() { - TRY_GUI_REDIRECT_DOUBLE("plt", NULL); - ret(); - pushx(0.); -} -hoc_Setcolor() { - TRY_GUI_REDIRECT_DOUBLE("setcolor", NULL); - ret(); - pushx(0.); -} -hoc_Lw() { - ret(); - pushx(0.); -} -hoc_settext() { - TRY_GUI_REDIRECT_DOUBLE("settext", NULL); - ret(); - pushx(0.); -} -hoc_Plot() { - TRY_GUI_REDIRECT_DOUBLE("plot", NULL); - ret(); - pushx(0.); -} -hoc_axis() { - TRY_GUI_REDIRECT_DOUBLE("axis", NULL); - ret(); - pushx(0.); -} - - -// int gethostname() {printf("no gethostname\n");} - - -plt(int mode, double x, double y) {} - - -initplot() {} diff --git a/src/oc/mk_hocusr_h.py b/src/oc/mk_hocusr_h.py index 8d67a4d24a..596634c51a 100644 --- a/src/oc/mk_hocusr_h.py +++ b/src/oc/mk_hocusr_h.py @@ -25,8 +25,7 @@ def processvar(a, names): def remove_multiline_comments(string): # remove all occurance comments (/*COMMENT */) from string - string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string) - return string + return re.sub(r"/\*.*?\*/", "", string, 0, re.DOTALL) types = {} diff --git a/src/oc/nrnmpiuse.h.in b/src/oc/nrnmpiuse.h.in index 4db1c75155..80cf11785b 100755 --- a/src/oc/nrnmpiuse.h.in +++ b/src/oc/nrnmpiuse.h.in @@ -1,19 +1,16 @@ -#ifndef usenrnmpi_h -#define usenrnmpi_h +#pragma once /* define to 1 if you want MPI specific features activated */ -#undef NRNMPI +#cmakedefine NRNMPI @NRNMPI@ /* define to 1 if you want mpi dynamically loaded instead of linked normally */ -#undef NRNMPI_DYNAMICLOAD +#cmakedefine NRNMPI_DYNAMICLOAD @NRNMPI_DYNAMICLOAD@ /* define to 1 if you want the MUSIC - MUlti SImulation Coordinator */ -#undef NRN_MUSIC +#cmakedefine NRN_MUSIC @NRN_MUSIC@ /* define to the dll path if you want to load automatically */ -#undef DLL_DEFAULT_FNAME +#cmakedefine DLL_DEFAULT_FNAME @DLL_DEFAULT_FNAME@ /* define if needed */ -#undef ALWAYS_CALL_MPI_INIT - -#endif +#cmakedefine ALWAYS_CALL_MPI_INIT @ALWAYS_CALL_MPI_INIT@ diff --git a/src/oc/parse.ypp b/src/oc/parse.ypp index 70cccbbce0..07f9240e37 100755 --- a/src/oc/parse.ypp +++ b/src/oc/parse.ypp @@ -5,10 +5,6 @@ /* changes as of 2-jan-89 */ /* version 7.2.1 2-jan-89 short form of the for statement */ -#if AIX -#pragma alloca -#endif - #include "hoc.h" #include "hocdec.h" #include "ocmisc.h" diff --git a/src/oc/x.cpp b/src/oc/x.cpp index 1f01a6d8f2..33c1cd8f90 100644 --- a/src/oc/x.cpp +++ b/src/oc/x.cpp @@ -7,10 +7,6 @@ #endif #endif -#if defined(__alpha) -#undef USG -#endif - #if NRNOC_X11 #if defined(IVX11_DYNAM) diff --git a/src/parallel/bbs.cpp b/src/parallel/bbs.cpp index 95962ccbe2..2a51b20c55 100644 --- a/src/parallel/bbs.cpp +++ b/src/parallel/bbs.cpp @@ -11,16 +11,9 @@ #include "bbsrcli.h" #endif -#if defined(HAVE_TMS) && !NRNMPI -#include -#include -#include -static struct tms tmsbuf, tms_start_; -static clock_t starttime; -#endif - extern int nrn_global_argc; extern char** nrn_global_argv; +extern double nrn_timeus(); bool BBSImpl::is_master_ = false; bool BBSImpl::started_ = false; @@ -132,11 +125,7 @@ double BBSImpl::time() { #if NRNMPI return nrnmpi_wtime(); #else -#ifdef HAVE_TMS - return double(times(&tmsbuf)) / 100.; -#else - return 0.; -#endif + return nrn_timeus(); #endif } @@ -493,18 +482,6 @@ void BBSImpl::done() { return; } done_ = true; -#ifdef HAVE_TMS - clock_t elapsed = times(&tmsbuf) - starttime; - printf("%d tasks in %g seconds. %g seconds waiting for tasks\n", - etaskcnt, - total_exec_time, - worker_take_time); - printf("user=%g sys=%g elapsed=%g %g%%\n", - (double) (tmsbuf.tms_utime - tms_start_.tms_utime) / 100, - (double) (tmsbuf.tms_stime - tms_start_.tms_stime) / 100, - (double) (elapsed) / 100, - 100. * (double) (tmsbuf.tms_utime - tms_start_.tms_utime) / (double) elapsed); -#endif } void BBSImpl::start() { @@ -512,7 +489,4 @@ void BBSImpl::start() { return; } started_ = 1; -#ifdef HAVE_TMS - starttime = times(&tms_start_); -#endif } diff --git a/src/parallel/bbsclimpi.cpp b/src/parallel/bbsclimpi.cpp index 4e95b5b336..101949cd83 100644 --- a/src/parallel/bbsclimpi.cpp +++ b/src/parallel/bbsclimpi.cpp @@ -20,13 +20,7 @@ extern void nrnmpi_int_broadcast(int*, int, int); #include -struct ltint { - bool operator()(int i, int j) const { - return i < j; - } -}; - -class KeepArgs: public std::map {}; +class KeepArgs: public std::map {}; int BBSClient::sid_; @@ -257,7 +251,7 @@ int BBSClient::look_take_result(int pid) { void BBSClient::save_args(int userid) { nrnmpi_ref(sendbuf_); - keepargs_->insert(std::pair(userid, sendbuf_)); + keepargs_->emplace(userid, sendbuf_); post_todo(working_id_); } diff --git a/src/parallel/bbsdirect.h b/src/parallel/bbsdirect.h index b5679dbceb..9c595b710d 100644 --- a/src/parallel/bbsdirect.h +++ b/src/parallel/bbsdirect.h @@ -15,43 +15,43 @@ struct bbsmpibuf; class BBSDirect: public BBSImpl { public: BBSDirect(); - virtual ~BBSDirect(); + ~BBSDirect() override; - virtual bool look(const char*); + bool look(const char*) override; - virtual void take(const char*); /* blocks til something to take */ - virtual bool look_take(const char*); /* returns false if nothing to take */ + void take(const char*) override; /* blocks til something to take */ + bool look_take(const char*) override; /* returns false if nothing to take */ // after taking use these - virtual int upkint(); - virtual double upkdouble(); - virtual void upkvec(int, double*); - virtual char* upkstr(); // delete [] char* when finished - virtual char* upkpickle(size_t*); // delete [] char* when finished + int upkint() override; + double upkdouble() override; + void upkvec(int, double*) override; + char* upkstr() override; // delete [] char* when finished + char* upkpickle(size_t*) override; // delete [] char* when finished // before posting use these - virtual void pkbegin(); - virtual void pkint(int); - virtual void pkdouble(double); - virtual void pkvec(int, double*); - virtual void pkstr(const char*); - virtual void pkpickle(const char*, size_t); - virtual void post(const char*); - - virtual void post_todo(int parentid); - virtual void post_result(int id); - virtual int look_take_result(int pid); // returns id, or 0 if nothing - virtual int master_take_result(int pid); // returns id - virtual int look_take_todo(); // returns id, or 0 if nothing - virtual int take_todo(); // returns id - virtual void save_args(int); - virtual void return_args(int); - - virtual void context(); - - virtual void start(); - virtual void done(); - - virtual void perror(const char*); + void pkbegin() override; + void pkint(int) override; + void pkdouble(double) override; + void pkvec(int, double*) override; + void pkstr(const char*) override; + void pkpickle(const char*, size_t) override; + void post(const char*) override; + + void post_todo(int parentid) override; + void post_result(int id) override; + int look_take_result(int pid) override; // returns id, or 0 if nothing + int master_take_result(int pid) override; // returns id + int look_take_todo() override; // returns id, or 0 if nothing + int take_todo() override; // returns id + void save_args(int) override; + void return_args(int) override; + + void context() override; + + void start() override; + void done() override; + + void perror(const char*) override; private: KeepArgs* keepargs_; diff --git a/src/parallel/bbsdirectmpi.cpp b/src/parallel/bbsdirectmpi.cpp index 4e62dbe0bf..ca68f86dfc 100644 --- a/src/parallel/bbsdirectmpi.cpp +++ b/src/parallel/bbsdirectmpi.cpp @@ -19,13 +19,7 @@ extern void nrnmpi_int_broadcast(int*, int, int); #define debug 0 -struct ltint { - bool operator()(int i, int j) const { - return i < j; - } -}; - -class KeepArgs: public std::map {}; +class KeepArgs: public std::map {}; BBSDirect::BBSDirect() { if (!BBSDirectServer::server_) { @@ -252,7 +246,7 @@ int BBSDirect::master_take_result(int pid) { void BBSDirect::save_args(int userid) { nrnmpi_ref(sendbuf_); - keepargs_->insert(std::pair(userid, sendbuf_)); + keepargs_->emplace(userid, sendbuf_); post_todo(working_id_); } diff --git a/src/parallel/bbslocal.cpp b/src/parallel/bbslocal.cpp index 15ae4b8ee1..32549658a0 100644 --- a/src/parallel/bbslocal.cpp +++ b/src/parallel/bbslocal.cpp @@ -2,20 +2,14 @@ #include #include "oc2iv.h" #include "bbslocal.h" -#include "bbslsrv.h" +#include "bbslsrv.hpp" #include #include #include #include -struct ltint { - bool operator()(int i, int j) const { - return i < j; - } -}; - -class KeepArgs: public std::map {}; +class KeepArgs: public std::map {}; static MessageValue* posting_; static MessageValue* taking_; @@ -205,7 +199,7 @@ int BBSLocal::take_todo() { void BBSLocal::save_args(int userid) { server_->post_todo(working_id_, posting_); - keepargs_->insert(std::pair(userid, posting_)); + keepargs_->emplace(userid, posting_); posting_ = nullptr; } @@ -213,7 +207,7 @@ void BBSLocal::return_args(int userid) { KeepArgs::iterator i = keepargs_->find(userid); assert(i != keepargs_->end()); Resource::unref(taking_); - taking_ = (MessageValue*) ((*i).second); + taking_ = const_cast((*i).second); keepargs_->erase(i); taking_->init_unpack(); BBSImpl::return_args(userid); diff --git a/src/parallel/bbslocal.h b/src/parallel/bbslocal.h index 920ea557f3..dba6051a23 100644 --- a/src/parallel/bbslocal.h +++ b/src/parallel/bbslocal.h @@ -7,42 +7,42 @@ class KeepArgs; class BBSLocal: public BBSImpl { public: BBSLocal(); - virtual ~BBSLocal(); + ~BBSLocal() override; - virtual bool look(const char*); + bool look(const char*) override; - virtual void take(const char*); /* blocks til something to take */ - virtual bool look_take(const char*); /* returns false if nothing to take */ + void take(const char*) override; /* blocks til something to take */ + bool look_take(const char*) override; /* returns false if nothing to take */ // after taking use these - virtual int upkint(); - virtual double upkdouble(); - virtual void upkvec(int, double*); - virtual char* upkstr(); // delete [] char* when finished - virtual char* upkpickle(size_t* size); // delete [] char* when finished + int upkint() override; + double upkdouble() override; + void upkvec(int, double*) override; + char* upkstr() override; // delete [] char* when finished + char* upkpickle(size_t* size) override; // delete [] char* when finished // before posting use these - virtual void pkbegin(); - virtual void pkint(int); - virtual void pkdouble(double); - virtual void pkvec(int, double*); - virtual void pkstr(const char*); - virtual void pkpickle(const char*, size_t); - virtual void post(const char*); - - virtual void post_todo(int parentid); - virtual void post_result(int id); - virtual int look_take_result(int pid); // returns id, or 0 if nothing - virtual int look_take_todo(); // returns id, or 0 if nothing - virtual int take_todo(); // returns id - virtual void save_args(int); - virtual void return_args(int); - - virtual void context(); - - virtual void start(); - virtual void done(); - - virtual void perror(const char*); + void pkbegin() override; + void pkint(int) override; + void pkdouble(double) override; + void pkvec(int, double*) override; + void pkstr(const char*) override; + void pkpickle(const char*, size_t) override; + void post(const char*) override; + + void post_todo(int parentid) override; + void post_result(int id) override; + int look_take_result(int pid) override; // returns id, or 0 if nothing + int look_take_todo() override; // returns id, or 0 if nothing + int take_todo() override; // returns id + void save_args(int) override; + void return_args(int) override; + + void context() override; + + void start() override; + void done() override; + + void perror(const char*) override; private: KeepArgs* keepargs_; diff --git a/src/parallel/bbslsrv.cpp b/src/parallel/bbslsrv.cpp index b98dd212d1..5d0a1c4f33 100644 --- a/src/parallel/bbslsrv.cpp +++ b/src/parallel/bbslsrv.cpp @@ -1,7 +1,7 @@ #include <../../nrnconf.h> #include #include -#include "bbslsrv.h" +#include "bbslsrv.hpp" #include "oc_ansi.h" #define INT 1 @@ -33,12 +33,6 @@ struct ltstr { } }; -struct ltint { - bool operator()(int i, int j) const { - return i < j; - } -}; - struct ltWorkItem { bool operator()(const WorkItem* w1, const WorkItem* w2) const { return w1->todo_less_than(w2); @@ -86,148 +80,97 @@ bool WorkItem::todo_less_than(const WorkItem* w) const { } class MessageList: public std::multimap {}; -class WorkList: public std::map {}; +class WorkList: public std::map {}; class ReadyList: public std::set {}; -class ResultList: public std::multimap {}; - -MessageItem::MessageItem() { - next_ = nullptr; - type_ = 0; -} - -MessageItem::~MessageItem() { - switch (type_) { - case STRING: - delete[] u.s; - break; - case VECTOR: - delete[] u.pd; - break; - case PICKLE: - delete[] u.s; - break; - } -} - -MessageValue::MessageValue() { - first_ = nullptr; - last_ = nullptr; - unpack_ = nullptr; -} - -MessageValue::~MessageValue() { - MessageItem *mi, *next; - for (mi = first_; mi; mi = next) { - next = mi->next_; - delete mi; - } -} - -MessageItem* MessageValue::link() { - MessageItem* mi = new MessageItem(); - if (last_) { - last_->next_ = mi; - } else { - first_ = mi; - unpack_ = mi; - } - last_ = mi; - return mi; -} +class ResultList: public std::multimap {}; void MessageValue::init_unpack() { - unpack_ = first_; + index_ = 0; } int MessageValue::pkint(int i) { - MessageItem* m = link(); - m->type_ = INT; - m->u.i = i; + args_.emplace_back(i); return 0; } int MessageValue::pkdouble(double x) { - MessageItem* m = link(); - m->type_ = DOUBLE; - m->u.d = x; + args_.emplace_back(x); return 0; } int MessageValue::pkvec(int n, double* x) { - int i; - MessageItem* m = link(); - m->type_ = VECTOR; - m->u.pd = new double[n]; - for (i = 0; i < n; ++i) { - m->u.pd[i] = x[i]; - } + args_.emplace_back(std::vector(x, x + n)); return 0; } int MessageValue::pkstr(const char* str) { - MessageItem* m = link(); - m->type_ = STRING; - m->u.s = new char[strlen(str) + 1]; - strcpy(m->u.s, str); + args_.emplace_back(std::string(str)); return 0; } int MessageValue::pkpickle(const char* bytes, size_t n) { - MessageItem* m = link(); - m->type_ = PICKLE; - m->u.s = new char[n]; - m->size_ = n; - memcpy(m->u.s, bytes, n); + args_.emplace_back(std::vector(bytes, bytes + n)); return 0; } int MessageValue::upkint(int* i) { - if (!unpack_ || unpack_->type_ != INT) { + if (index_ > args_.size()) { return -1; } - *i = unpack_->u.i; - unpack_ = unpack_->next_; - return 0; + if (const auto* val = std::get_if(args_.data() + index_)) { + *i = *val; + ++index_; + return 0; + } + return -1; } int MessageValue::upkdouble(double* d) { - if (!unpack_ || unpack_->type_ != DOUBLE) { - return -1; + const auto& mi = args_.front(); + if (const auto* val = std::get_if(args_.data() + index_)) { + *d = *val; + ++index_; + return 0; } - *d = unpack_->u.d; - unpack_ = unpack_->next_; - return 0; + return -1; } int MessageValue::upkvec(int n, double* d) { - int i; - if (!unpack_ || unpack_->type_ != VECTOR) { - return -1; - } - for (i = 0; i < n; ++i) { - d[i] = unpack_->u.pd[i]; + const auto& mi = args_.front(); + if (const auto* val = std::get_if>(args_.data() + index_)) { + for (std::size_t i = 0; i < n; ++i) { + d[i] = val->at(i); + } + ++index_; + return 0; } - unpack_ = unpack_->next_; - return 0; + return -1; } int MessageValue::upkstr(char* s) { - if (!unpack_ || unpack_->type_ != STRING) { - return -1; + const auto& mi = args_.front(); + if (const auto* val = std::get_if(args_.data() + index_)) { + for (std::size_t i = 0; i < val->size(); ++i) { + s[i] = val->at(i); + } + s[val->size()] = '\0'; + ++index_; + return 0; } - strcpy(s, unpack_->u.s); - unpack_ = unpack_->next_; - return 0; + return -1; } int MessageValue::upkpickle(char* s, size_t* n) { - if (!unpack_ || unpack_->type_ != PICKLE) { - return -1; + const auto& mi = args_.front(); + if (const auto* val = std::get_if>(args_.data() + index_)) { + *n = val->size(); + for (std::size_t i = 0; i < *n; ++i) { + s[i] = val->at(i); + } + ++index_; + return 0; } - *n = unpack_->size_; - memcpy(s, unpack_->u.s, *n); - unpack_ = unpack_->next_; - return 0; + return -1; } BBSLocalServer::BBSLocalServer() { @@ -251,7 +194,7 @@ BBSLocalServer::~BBSLocalServer() { bool BBSLocalServer::look_take(const char* key, MessageValue** val) { MessageList::iterator m = messages_->find(key); if (m != messages_->end()) { - *val = (MessageValue*) ((*m).second); + *val = const_cast((*m).second); char* s = (char*) ((*m).first); messages_->erase(m); delete[] s; @@ -269,7 +212,7 @@ bool BBSLocalServer::look_take(const char* key, MessageValue** val) { bool BBSLocalServer::look(const char* key, MessageValue** val) { MessageList::iterator m = messages_->find(key); if (m != messages_->end()) { - *val = (MessageValue*) ((*m).second); + *val = const_cast((*m).second); Resource::ref(*val); #if debug printf("srvr_look true |%s|\n", key); @@ -285,8 +228,7 @@ bool BBSLocalServer::look(const char* key, MessageValue** val) { } void BBSLocalServer::post(const char* key, MessageValue* val) { - MessageList::iterator m = messages_->insert( - std::pair(newstr(key), val)); + MessageList::iterator m = messages_->emplace(newstr(key), val); Resource::ref(val); #if debug printf("srvr_post |%s|\n", key); @@ -299,8 +241,8 @@ void BBSLocalServer::post_todo(int parentid, MessageValue* val) { if (p != work_->end()) { w->parent_ = (WorkItem*) ((*p).second); } - work_->insert(std::pair(w->id_, w)); - todo_->insert(w); + work_->emplace(w->id_, w); + todo_->emplace(w); #if debug printf("srvr_post_todo id=%d pid=%d\n", w->id_, parentid); #endif @@ -312,7 +254,7 @@ void BBSLocalServer::post_result(int id, MessageValue* val) { val->ref(); w->val_->unref(); w->val_ = val; - results_->insert(std::pair(w->parent_ ? w->parent_->id_ : 0, w)); + results_->emplace(w->parent_ ? w->parent_->id_ : 0, w); #if debug printf("srvr_post_done id=%d pid=%d\n", id, w->parent_ ? w->parent_->id_ : 0); #endif diff --git a/src/parallel/bbslsrv.h b/src/parallel/bbslsrv.hpp similarity index 72% rename from src/parallel/bbslsrv.h rename to src/parallel/bbslsrv.hpp index 0e7a866a5b..ed6f87e68a 100644 --- a/src/parallel/bbslsrv.h +++ b/src/parallel/bbslsrv.hpp @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #include class MessageList; @@ -7,25 +11,10 @@ class WorkList; class ReadyList; class ResultList; -class MessageItem { - public: - MessageItem(); - virtual ~MessageItem(); - MessageItem* next_; - int type_; - size_t size_; // for pickle type - union { - int i; - double d; - double* pd; - char* s; - } u; -}; +using MessageItem = std::variant, std::vector, std::string>; class MessageValue: public Resource { public: - MessageValue(); - virtual ~MessageValue(); void init_unpack(); // following return 0 if success, -1 if failure int upkint(int*); @@ -41,12 +30,8 @@ class MessageValue: public Resource { int pkpickle(const char*, size_t); private: - MessageItem* link(); - - private: - MessageItem* first_; - MessageItem* last_; - MessageItem* unpack_; + std::vector args_{}; + std::size_t index_{}; }; class BBSLocalServer { diff --git a/src/parallel/bbsrcli.h b/src/parallel/bbsrcli.h index 70bcfa3790..72ef12a9ab 100644 --- a/src/parallel/bbsrcli.h +++ b/src/parallel/bbsrcli.h @@ -8,40 +8,40 @@ struct bbsmpibuf; class BBSClient: public BBSImpl { // implemented as PVM Client public: BBSClient(); - virtual ~BBSClient(); + ~BBSClient() override; - virtual bool look(const char*); + bool look(const char*) override; - virtual void take(const char*); /* blocks til something to take */ - virtual bool look_take(const char*); /* returns false if nothing to take */ + void take(const char*) override; /* blocks til something to take */ + bool look_take(const char*) override; /* returns false if nothing to take */ // after taking use these - virtual int upkint(); - virtual double upkdouble(); - virtual void upkvec(int, double*); - virtual char* upkstr(); // delete [] char* when finished - virtual char* upkpickle(size_t*); // delete [] char* when finished + int upkint() override; + double upkdouble() override; + void upkvec(int, double*) override; + char* upkstr() override; // delete [] char* when finished + char* upkpickle(size_t*) override; // delete [] char* when finished // before posting use these - virtual void pkbegin(); - virtual void pkint(int); - virtual void pkdouble(double); - virtual void pkvec(int, double*); - virtual void pkstr(const char*); - virtual void pkpickle(const char*, size_t); - virtual void post(const char*); - - virtual void post_todo(int parentid); - virtual void post_result(int id); - virtual int look_take_result(int pid); // returns id, or 0 if nothing - virtual int look_take_todo(); // returns id, or 0 if nothing - virtual int take_todo(); // returns id - virtual void save_args(int); - virtual void return_args(int); - - virtual void start(); - virtual void done(); - - virtual void perror(const char*); + void pkbegin() override; + void pkint(int) override; + void pkdouble(double) override; + void pkvec(int, double*) override; + void pkstr(const char*) override; + void pkpickle(const char*, size_t) override; + void post(const char*) override; + + void post_todo(int parentid) override; + void post_result(int id) override; + int look_take_result(int pid) override; // returns id, or 0 if nothing + int look_take_todo() override; // returns id, or 0 if nothing + int take_todo() override; // returns id + void save_args(int) override; + void return_args(int) override; + + void start() override; + void done() override; + + void perror(const char*) override; private: int get(const char* key, int type); // return type diff --git a/src/parallel/bbssrv2mpi.cpp b/src/parallel/bbssrv2mpi.cpp index 0a2776445e..3da195fac7 100644 --- a/src/parallel/bbssrv2mpi.cpp +++ b/src/parallel/bbssrv2mpi.cpp @@ -43,12 +43,6 @@ struct ltstr { } }; -struct ltint { - bool operator()(int i, int j) const { - return i < j; - } -}; - struct ltWorkItem { bool operator()(const WorkItem* w1, const WorkItem* w2) const { return w1->todo_less_than(w2); @@ -95,10 +89,10 @@ bool WorkItem::todo_less_than(const WorkItem* w) const { class MessageList: public std::multimap {}; class PendingList: public std::multimap {}; -class WorkList: public std::map {}; -class LookingToDoList: public std::set {}; +class WorkList: public std::map {}; +class LookingToDoList: public std::set {}; class ReadyList: public std::set {}; -class ResultList: public std::multimap {}; +class ResultList: public std::multimap {}; BBSDirectServer::BBSDirectServer() { messages_ = new MessageList(); @@ -173,7 +167,7 @@ void BBSDirectServer::put_pending(const char* key, int cid) { printf("put_pending |%s| %d\n", key, cid); #endif char* s = newstr(key); - pending_->insert(std::pair(s, cid)); + pending_->emplace(s, cid); } bool BBSDirectServer::take_pending(const char* key, int* cid) { @@ -200,14 +194,13 @@ void BBSDirectServer::post(const char* key, bbsmpibuf* send) { if (take_pending(key, &cid)) { nrnmpi_bbssend(cid, TAKE, send); } else { - MessageList::iterator m = messages_->insert( - std::pair(newstr(key), send)); + MessageList::iterator m = messages_->emplace(newstr(key), send); nrnmpi_ref(send); } } void BBSDirectServer::add_looking_todo(int cid) { - looking_todo_->insert(cid); + looking_todo_->emplace(cid); } void BBSDirectServer::post_todo(int pid, int cid, bbsmpibuf* send) { @@ -220,7 +213,7 @@ void BBSDirectServer::post_todo(int pid, int cid, bbsmpibuf* send) { if (p != work_->end()) { w->parent_ = (WorkItem*) ((*p).second); } - work_->insert(std::pair(w->id_, w)); + work_->emplace(w->id_, w); #if debug printf("work insert %d\n", w->id_); #endif @@ -234,7 +227,7 @@ void BBSDirectServer::post_todo(int pid, int cid, bbsmpibuf* send) { #if debug printf("todo insert\n"); #endif - todo_->insert(w); + todo_->emplace(w); } } @@ -258,7 +251,7 @@ void BBSDirectServer::context(bbsmpibuf* send) { } remaining_context_cnt_ = nrnmpi_numprocs_bbs - 1; for (j = 1; j < nrnmpi_numprocs_bbs; ++j) { - send_context_->insert(j); + send_context_->emplace(j); } LookingToDoList::iterator i = looking_todo_->begin(); while (i != looking_todo_->end()) { @@ -320,7 +313,7 @@ void BBSDirectServer::post_result(int id, bbsmpibuf* send) { nrnmpi_ref(send); nrnmpi_unref(w->buf_); w->buf_ = send; - results_->insert(std::pair(w->parent_ ? w->parent_->id_ : 0, w)); + results_->emplace(w->parent_ ? w->parent_->id_ : 0, w); } int BBSDirectServer::look_take_todo(bbsmpibuf** recv) { diff --git a/src/parallel/ocbbs.cpp b/src/parallel/ocbbs.cpp index 0df1804fdf..7462d149b3 100644 --- a/src/parallel/ocbbs.cpp +++ b/src/parallel/ocbbs.cpp @@ -1,4 +1,4 @@ -#include <../../nrnconf.h> +#include #include #include "classreg.h" #include "oc2iv.h" @@ -13,6 +13,7 @@ #include "multicore.h" #include "nrnpy.h" #include "utils/profile/profiler_interface.h" +#include "node_order_optim/node_order_optim.h" #include #include @@ -807,7 +808,7 @@ static double alltoall(void*) { ns = vector_capacity(vsrc); double* s = vector_vec(vsrc); if (vector_capacity(vscnt) != np) { - hoc_execerror("size of source counts vector is not nhost", 0); + hoc_execerror("size of source counts vector is not nhost", nullptr); } double* x = vector_vec(vscnt); int* scnt = new int[np]; @@ -818,7 +819,7 @@ static double alltoall(void*) { sdispl[i + 1] = sdispl[i] + scnt[i]; } if (ns != sdispl[np]) { - hoc_execerror("sum of source counts is not the size of the src vector", 0); + hoc_execerror("sum of source counts is not the size of the src vector", nullptr); } Vect* vdest = vector_arg(3); if (nrnmpi_numprocs > 1) { @@ -956,6 +957,14 @@ static double thread_how_many_proc(void*) { return double(i); } +static double optimize_node_order(void*) { + hoc_return_type_code = 1; // integer + if (ifarg(1)) { + neuron::nrn_optimize_node_order(int(chkarg(1, 0, 2))); + } + return double(neuron::interleave_permute_type); +} + static double sec_in_thread(void*) { hoc_return_type_code = 2; // boolean Section* sec = chk_access(); @@ -991,7 +1000,7 @@ static double thread_dt(void*) { static double nrncorewrite_argvec(void*) { if (ifarg(2) && !(hoc_is_object_arg(2) && is_vector_arg(2))) { - hoc_execerror("nrnbbcore_write: optional second arg is not a Vector", NULL); + hoc_execerror("nrnbbcore_write: optional second arg is not a Vector", nullptr); } return double(nrncore_write()); } @@ -1013,7 +1022,8 @@ static double print_memory_stats(void*) { static double nrncorewrite_argappend(void*) { if (ifarg(2) && !hoc_is_double_arg(2)) { hoc_execerror( - "nrncore_write: optional second arg is not a number (True or False append flag)", NULL); + "nrncore_write: optional second arg is not a number (True or False append flag)", + nullptr); } return double(nrncore_write()); } @@ -1111,6 +1121,7 @@ static Member_func members[] = {{"submit", submit}, {"thread_stat", thread_stat}, {"thread_busywait", thread_busywait}, {"thread_how_many_proc", thread_how_many_proc}, + {"optimize_node_order", optimize_node_order}, {"sec_in_thread", sec_in_thread}, {"thread_ctime", thread_ctime}, {"dt", thread_dt}, @@ -1160,8 +1171,28 @@ void ParallelContext_reg() { class2oc("ParallelContext", cons, destruct, members, nullptr, retobj_members, retstr_members); } +// A BBS message is something to execute. +// This helper execute depending of the style of the message. +// style == 0: +// This is a string that is a hoc statement, execute it with `hoc_obj_run`. +// Return `nullptr`, set `size` argument to 0. +// Not listen to `exec`. +// No arguments. +// style == 1: +// This is a function name as a string followed by a list of arguments. +// Return `nullptr` and set `size` to 0. +// Dry run if `exec` is false. +// style == 2: +// From a template, find a object with an id and inside this object find a member +// with a name as a string. Execute this member with arguments. +// Return `nullptr` and set `size` to 0. +// Dry run if `exec` is false. +// style == 3: +// A python pickle (https://docs.python.org/3/library/pickle.html) followed by arguments. +// Return a string that is of size `size`. +// Dry run if `exec` is false. char* BBSImpl::execute_helper(size_t* size, int id, bool exec) { - char* s; + char* python_pickle; // Used only for style == 3 int subworld = (nrnmpi_numprocs > 1 && nrnmpi_numprocs_bbs < nrnmpi_numprocs_world); int style = upkint(); if (subworld) { @@ -1171,113 +1202,113 @@ char* BBSImpl::execute_helper(size_t* size, int id, bool exec) { info[1] = style; nrnmpi_int_broadcast(info, 2, 0); } - char* rs = 0; + char* rs = nullptr; *size = 0; switch (style) { - case 0: - s = upkstr(); + case 0: { + char* statement = upkstr(); if (subworld) { - int size = strlen(s) + 1; + int size = strlen(statement) + 1; nrnmpi_int_broadcast(&size, 1, 0); - nrnmpi_char_broadcast(s, size, 0); + nrnmpi_char_broadcast(statement, size, 0); } - hoc_obj_run(s, nullptr); - delete[] s; - break; + hoc_obj_run(statement, nullptr); + delete[] statement; + } break; default: { -#if 1 - int i, j; size_t npickle; - Symbol* fname = 0; + Symbol* fname = nullptr; Object* ob = nullptr; - char* sarg[20]; // upto 20 argument may be strings - int ns = 0; // number of args that are strings - int narg = 0; // total number of args - if (style == 2) { // object first - s = upkstr(); // template name - i = upkint(); // object index - // printf("template |%s| index=%d\n", s, i); - Symbol* sym = hoc_lookup(s); + std::list sarg; // Store the strings pointer to delete[] them later + // Use a list because, we push pointers of the object into + // the hoc stack + int narg = 0; // total number of args + if (style == 2) { // object first + char* template_name = upkstr(); + int object_index = upkint(); // object index + Symbol* sym = hoc_lookup(template_name); if (sym) { sym = hoc_which_template(sym); } if (!sym) { - hoc_execerror(s, "is not a template"); + hoc_execerror(template_name, "is not a template"); } hoc_Item *q, *ql; ql = sym->u.ctemplate->olist; ITERATE(q, ql) { ob = OBJ(q); - if (ob->index == i) { + if (ob->index == object_index) { break; } ob = nullptr; } if (!ob) { - fprintf(stderr, "%s[%d] is not an Object in this process\n", s, i); - hoc_execerror("ParallelContext execution error", 0); + fprintf(stderr, + "%s[%d] is not an Object in this process\n", + template_name, + object_index); + hoc_execerror("ParallelContext execution error", nullptr); } - delete[] s; - s = upkstr(); - fname = hoc_table_lookup(s, sym->u.ctemplate->symtable); + delete[] template_name; + char* fname_str = upkstr(); + fname = hoc_table_lookup(fname_str, sym->u.ctemplate->symtable); + if (!fname) { + fprintf(stderr, "%s not a function in %s\n", fname_str, hoc_object_name(ob)); + hoc_execerror("ParallelContext execution error", nullptr); + } + delete[] fname_str; if (subworld) { - hoc_execerror("with subworlds, this submit style not implemented", 0); + hoc_execerror("with subworlds, this submit style not implemented", nullptr); } } else if (style == 3) { // Python callable - s = upkpickle(&npickle); + python_pickle = upkpickle(&npickle); if (subworld) { int size = npickle; nrnmpi_int_broadcast(&size, 1, 0); - nrnmpi_char_broadcast(s, size, 0); + nrnmpi_char_broadcast(python_pickle, size, 0); } } else { - s = upkstr(); + char* fname_str = upkstr(); if (subworld) { - int size = strlen(s) + 1; - // printf("%d exec hoc fun size = %d\n", nrnmpi_myid_world, size); + int size = strlen(fname_str) + 1; nrnmpi_int_broadcast(&size, 1, 0); - nrnmpi_char_broadcast(s, size, 0); + nrnmpi_char_broadcast(fname_str, size, 0); } - fname = hoc_lookup(s); - } - // printf("execute helper style %d fname=%s obj=%s\n", style, fname->name, - // hoc_object_name(ob)); - if (style != 3 && !fname) { - fprintf(stderr, "%s not a function in %s\n", s, hoc_object_name(ob)); - hoc_execerror("ParallelContext execution error", 0); + fname = hoc_lookup(fname_str); + if (!fname) { + fprintf(stderr, "%s not a function in %s\n", fname_str, hoc_object_name(ob)); + hoc_execerror("ParallelContext execution error", nullptr); + } + delete[] fname_str; } + int argtypes = upkint(); // first is least signif if (subworld) { // printf("%d exec argtypes = %d\n", nrnmpi_myid_world, argtypes); nrnmpi_int_broadcast(&argtypes, 1, 0); } - for (j = argtypes; (i = j % 5) != 0; j /= 5) { + for (int i = 0, j = argtypes; (i = j % 5) != 0; j /= 5) { ++narg; if (i == 1) { double x = upkdouble(); - // printf("%d arg %d scalar %g\n", nrnmpi_myid_world, narg, x); if (subworld) { nrnmpi_dbl_broadcast(&x, 1, 0); } hoc_pushx(x); } else if (i == 2) { - sarg[ns] = upkstr(); - // printf("arg %d string |%s|\n", narg, sarg[ns]); + sarg.push_back(upkstr()); if (subworld) { - int size = strlen(sarg[ns]) + 1; + int size = strlen(sarg.back()) + 1; nrnmpi_int_broadcast(&size, 1, 0); - nrnmpi_char_broadcast(sarg[ns], size, 0); + nrnmpi_char_broadcast(sarg.back(), size, 0); } - hoc_pushstr(sarg + ns); - ns++; + hoc_pushstr(&(sarg.back())); } else if (i == 3) { - int n; - n = upkint(); + int n = upkint(); if (subworld) { nrnmpi_int_broadcast(&n, 1, 0); } Vect* vec = new Vect(n); - // printf("arg %d vector size=%d\n", narg, n); upkvec(n, vec->data()); if (subworld) { nrnmpi_dbl_broadcast(vec->data(), n, 0); @@ -1285,15 +1316,15 @@ char* BBSImpl::execute_helper(size_t* size, int id, bool exec) { hoc_pushobj(vec->temp_objvar()); } else { // PythonObject size_t n; - char* s = upkpickle(&n); + char* pickle = upkpickle(&n); int size = n; if (subworld) { nrnmpi_int_broadcast(&size, 1, 0); - nrnmpi_char_broadcast(s, size, 0); + nrnmpi_char_broadcast(pickle, size, 0); } assert(neuron::python::methods.pickle2po); - Object* po = neuron::python::methods.pickle2po(s, n); - delete[] s; + Object* po = neuron::python::methods.pickle2po(pickle, n); + delete[] pickle; hoc_pushobj(hoc_temp_objptr(po)); } } @@ -1301,27 +1332,23 @@ char* BBSImpl::execute_helper(size_t* size, int id, bool exec) { assert(neuron::python::methods.call_picklef); if (pickle_ret_) { delete[] pickle_ret_; - pickle_ret_ = 0; + pickle_ret_ = nullptr; pickle_ret_size_ = 0; } if (exec) { - rs = neuron::python::methods.call_picklef(s, npickle, narg, size); + rs = neuron::python::methods.call_picklef(python_pickle, npickle, narg, size); } hoc_ac_ = 0.; + delete[] python_pickle; } else { - // printf("%d exec hoc call %s narg=%d\n", nrnmpi_myid_world, fname->name, narg); hoc_ac_ = 0.; if (exec) { hoc_ac_ = hoc_call_objfunc(fname, narg, ob); } - // printf("%d exec return from hoc call %s narg=%d\n", nrnmpi_myid_world, fname->name, - // narg); } - delete[] s; - for (i = 0; i < ns; ++i) { - delete[] sarg[i]; + for (auto& arg: sarg) { + delete[] arg; } -#endif } break; } return rs; diff --git a/src/sparse13/spdefs.h b/src/sparse13/spdefs.h index f328b9d247..f7977c77ad 100644 --- a/src/sparse13/spdefs.h +++ b/src/sparse13/spdefs.h @@ -387,22 +387,8 @@ /* * MEMORY ALLOCATION */ -#if 1 #include "spmatrix.h" #include -#else -#if !defined(__MWERKS__) -extern char *malloc(), *calloc(), *realloc(); - -#ifdef ultrix -extern void free(); -extern void abort(); -#else -extern free(); -extern abort(); -#endif -#endif -#endif #define ALLOC(type, number) ((type*)malloc((unsigned)(sizeof(type) * (number)))) #define REALLOC(ptr, type, number) \ diff --git a/src/sparse13/spfactor.cpp b/src/sparse13/spfactor.cpp index c1d04bda2f..c3f960f6f4 100644 --- a/src/sparse13/spfactor.cpp +++ b/src/sparse13/spfactor.cpp @@ -639,36 +639,11 @@ void spPartition(char* eMatrix, int Mode) * No is the number of operations in the inner loop. */ -#define generic -#ifdef hp9000s300 -#if REAL - DoRealDirect[Step] = (Nm[Step] + No[Step] > 3 * Nc[Step] - 2 * Nm[Step]); -#endif -#if spCOMPLEX - /* On the hp350, it is never profitable to use direct for complex. */ - DoCmplxDirect[Step] = NO; -#endif -#undef generic -#endif - -#ifdef vax #if REAL DoRealDirect[Step] = (Nm[Step] + No[Step] > 3 * Nc[Step] - 2 * Nm[Step]); #endif #if spCOMPLEX DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7 * Nc[Step] - 4 * Nm[Step]); -#endif -#undef generic -#endif - -#ifdef generic -#if REAL - DoRealDirect[Step] = (Nm[Step] + No[Step] > 3 * Nc[Step] - 2 * Nm[Step]); -#endif -#if spCOMPLEX - DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7 * Nc[Step] - 4 * Nm[Step]); -#endif -#undef generic #endif } diff --git a/src/sundials/sundials_config.h.in b/src/sundials/sundials_config.h.in index 49977f3819..a378e81360 100755 --- a/src/sundials/sundials_config.h.in +++ b/src/sundials/sundials_config.h.in @@ -1,73 +1,41 @@ -/* config.hin. Generated from configure.ac by autoheader. */ - /* FCMIX: Define name-mangling macro */ -#undef F77_FUNC +#cmakedefine F77_FUNC @F77_FUNC@ /* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H +#cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@ /* Define to 1 if you have the `m' library (-lm). */ -#undef HAVE_LIBM +#cmakedefine HAVE_LIBM @HAVE_LIBM@ /* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME +#cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ /* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* The size of a `double', as computed by sizeof. */ -#undef SIZEOF_DOUBLE - -/* The size of a `float', as computed by sizeof. */ -#undef SIZEOF_FLOAT - -/* The size of a `int', as computed by sizeof. */ -#undef SIZEOF_INT - -/* The size of a `long double', as computed by sizeof. */ -#undef SIZEOF_LONG_DOUBLE - -/* The size of a `long int', as computed by sizeof. */ -#undef SIZEOF_LONG_INT - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS +#cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* FCMIX: Make function names lowercase */ -#undef SUNDIALS_CASE_LOWER +#cmakedefine SUNDIALS_CASE_LOWER @SUNDIALS_CASE_LOWER@ /* FCMIX: Make function names uppercase */ -#undef SUNDIALS_CASE_UPPER +#cmakedefine SUNDIALS_CASE_UPPER @SUNDIALS_CASE_UPPER@ /* Define SUNDIALS data type 'realtype' as 'double' */ -#undef SUNDIALS_DOUBLE_PRECISION +#cmakedefine SUNDIALS_DOUBLE_PRECISION @SUNDIALS_DOUBLE_PRECISION@ /* Define SUNDIALS data type 'realtype' as 'long double' */ -#undef SUNDIALS_EXTENDED_PRECISION +#cmakedefine SUNDIALS_EXTENDED_PRECISION @SUNDIALS_EXTENDED_PRECISION@ /* Define SUNDIALS data type 'realtype' as 'float' */ -#undef SUNDIALS_SINGLE_PRECISION +#cmakedefine SUNDIALS_SINGLE_PRECISION @SUNDIALS_SINGLE_PRECISION@ /* FCMIX: Do NOT append any underscores to functions names */ -#undef SUNDIALS_UNDERSCORE_NONE +#cmakedefine SUNDIALS_UNDERSCORE_NONE @SUNDIALS_UNDERSCORE_NONE@ /* FCMIX: Append ONE underscore to function names */ -#undef SUNDIALS_UNDERSCORE_ONE +#cmakedefine SUNDIALS_UNDERSCORE_ONE @SUNDIALS_UNDERSCORE_ONE@ /* FCMIX: Append TWO underscores to function names */ -#undef SUNDIALS_UNDERSCORE_TWO +#cmakedefine SUNDIALS_UNDERSCORE_TWO @SUNDIALS_UNDERSCORE_TWO@ /* Use generic math functions */ -#undef SUNDIALS_USE_GENERIC_MATH +#cmakedefine SUNDIALS_USE_GENERIC_MATH @SUNDIALS_USE_GENERIC_MATH@ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d696d3b76a..f5ac59824f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable( unit_tests/container/generic_data_handle.cpp unit_tests/container/mechanism.cpp unit_tests/container/node.cpp + unit_tests/node_order_optim/permutations.cpp unit_tests/utils/enumerate.cpp unit_tests/oc/hoc_interpreter.cpp cover/unit_tests/cover.cpp) @@ -675,6 +676,25 @@ if(NRN_ENABLE_PYTHON) ENVIRONMENT ${modtests_processor_env} ${nrnpython_mpi_env} COVERAGE_FILE=.coverage.coreneuron_test_natrans_py COMMAND ${modtests_launch_py} test/gjtests/test_natrans.py) + + foreach(run_mode RANGE 2) + nrn_add_test( + GROUP coreneuron_modtests + NAME array_variable_transfer_run_mode_${run_mode}_py_${processor} + REQUIRES coreneuron ${processor} ${modtests_preload_sanitizer} + SCRIPT_PATTERNS test/coreneuron/test_array_variables_transfer.py + ENVIRONMENT ${processor_env} NRN_TEST_RUN_MODE=${run_mode} NRN_TEST_FILE_MODE=false + COMMAND ${modtests_launch_py} test/coreneuron/test_array_variables_transfer.py) + endforeach() + + nrn_add_test( + GROUP coreneuron_modtests + NAME array_variable_transfer_file_mode_py_${processor} + REQUIRES coreneuron ${processor} ${modtests_preload_sanitizer} + SCRIPT_PATTERNS test/coreneuron/test_array_variables_transfer.py + ENVIRONMENT ${processor_env} NRN_TEST_RUN_MODE=0 NRN_TEST_FILE_MODE=true + COMMAND ${modtests_launch_py} test/coreneuron/test_array_variables_transfer.py) + if(NRN_ENABLE_MPI) nrn_find_python_module(MODULE mpi4py) if(mpi4py_FOUND) diff --git a/test/coreneuron/mod files/green.mod b/test/coreneuron/mod files/green.mod new file mode 100644 index 0000000000..2280da9387 --- /dev/null +++ b/test/coreneuron/mod files/green.mod @@ -0,0 +1,25 @@ +NEURON { + SUFFIX green + RANGE tau, upsilon +} + +DEFINE M 16 + +ASSIGNED { + tau + upsilon[M] +} + + +INITIAL { + FROM i = 0 TO M-1 { + upsilon[i] = 2.0 + 8*tau + } +} + +BREAKPOINT { + FROM i = 0 TO M-1 { + upsilon[i] = upsilon[i] + (i/3.0+1)*dt*cos((i/3.0+1) * t) + } +} + diff --git a/test/coreneuron/mod files/red.mod b/test/coreneuron/mod files/red.mod new file mode 100644 index 0000000000..c7b008c390 --- /dev/null +++ b/test/coreneuron/mod files/red.mod @@ -0,0 +1,25 @@ +NEURON { + SUFFIX red + RANGE tau, upsilon +} + +DEFINE M 16 + +ASSIGNED { + tau + upsilon[M] +} + + +INITIAL { + FROM i = 0 TO M-1 { + upsilon[i] = 0.0 + } +} + +BREAKPOINT { + FROM i = 0 TO M-1 { + upsilon[i] = sin((i + 1.0) * t) + } +} + diff --git a/test/coreneuron/test_array_variables_transfer.py b/test/coreneuron/test_array_variables_transfer.py new file mode 100644 index 0000000000..db06f53a19 --- /dev/null +++ b/test/coreneuron/test_array_variables_transfer.py @@ -0,0 +1,134 @@ +import itertools +import os + +import numpy as np + +from neuron import h, gui +from neuron import coreneuron +from neuron.tests.utils.strtobool import strtobool + +enable_coreneuron_option = bool( + strtobool(os.environ.get("NRN_TEST_ENABLE_CORENEURON", "true")) +) +enable_gpu_option = bool(strtobool(os.environ.get("CORENEURON_ENABLE_GPU", "false"))) +file_mode_option = bool(strtobool(os.environ.get("NRN_TEST_FILE_MODE", "false"))) +run_mode_option = os.environ.get("NRN_TEST_RUN_MODE", 2) +create_plots_option = bool(strtobool(os.environ.get("NRN_TEST_CREATE_PLOTS", "false"))) + + +def test_array_variable_transfer( + enable_coreneuron=enable_coreneuron_option, + enable_gpu=enable_gpu_option, + run_mode=run_mode_option, + file_mode=file_mode_option, + create_plots=create_plots_option, +): + + h("""create soma""") + h.soma.L = 5.6419 + h.soma.diam = 5.6419 + h.soma.insert("red") + h.soma.insert("green") + h.soma.nseg = 3 + + h.tstop = 6.0 + h.dt = 0.001 + + # TODO check replay. + + c_red, c_green = "red", "green" + m = 16 + + # `upsilon` is an array variable with at least `m` elements. It's value is + # approximately: + def upsilon(i, tau, c, t): + if c == c_red: + return np.sin((i + 1.0) * t) + elif c == c_green: + return 2.0 + 8 * tau + np.sin((i / 3.0 + 1) * t) + + cs = [c_green, c_red] + tols = [0.02, 1e-10] + + record_index = [0, m - 1] + taus = [0.25, 0.5, 0.75] + + i_tau_c_tol = list(itertools.product(record_index, taus, zip(cs, tols))) + i_tau_c = [(i, tau, c) for (i, tau, (c, tol)) in i_tau_c_tol] + tols = [tol for (_, _, (_, tol)) in i_tau_c_tol] + + t_vector = h.Vector().record(h._ref_t) + + for tau in taus: + h.soma(tau).tau_green = tau + + def record(i, tau, c): + if c == c_red: + return h.Vector().record(h.soma(tau)._ref_upsilon_red[i]) + elif c == c_green: + return h.Vector().record(h.soma(tau)._ref_upsilon_green[i]) + + upsilon_vector = [record(i, tau, c) for (i, tau, c) in i_tau_c] + + if enable_coreneuron: + pc = h.ParallelContext() + + # set gid for the cell (necessary for coreneuron data transfer) + pc.set_gid2node(pc.id() + 1, pc.id()) + myobj = h.NetCon(h.soma(0.5)._ref_v, None, sec=h.soma) + pc.cell(pc.id() + 1, myobj) + + with coreneuron(enable=enable_coreneuron, file_mode=file_mode): + h.stdinit() + + if run_mode == 0: + pc.psolve(h.tstop) + elif run_mode == 1: + while h.t < h.tstop: + pc.psolve(h.t + 1.0) + else: + while h.t < h.tstop: + h.continuerun(h.t + 0.5) + pc.psolve(h.t + 0.5) + + else: + h.stdinit() + h.run() + + t = np.array(t_vector.as_numpy()) + + assert t.size > 0, "No time instances recorded." + + upsilon_approx = [np.array(u.as_numpy()) for u in upsilon_vector] + upsilon_exact = [upsilon(i, tau, c, t) for (i, tau, c) in i_tau_c] + + if create_plots: + # Only import `matplotlib` if needed, it's not available + # during CI. + import matplotlib.pyplot as plt + + for k, u in enumerate(upsilon_exact): + legend_kwargs = {"label": "exact"} if k == 0 else dict() + plt.plot(t, u, "-k", linewidth=2, **legend_kwargs) + + for u in upsilon_approx: + plt.plot(t, u, "--", linewidth=2) + + plt.xlabel("time") + plt.ylabel("upsilon") + plt.title(f"Mode {run_mode}, File mode = {file_mode}, GPU = {enable_gpu}") + plt.savefig( + f"upsilon_traces-mode{run_mode}_filemode{file_mode}_gpu{enable_gpu}.png", + dpi=300, + ) + + for k, (tol, u_exact, u_approx) in enumerate( + zip(tols, upsilon_exact, upsilon_approx) + ): + abs_err_inf = np.max(np.abs(u_approx - u_exact)) + rel_err_inf = abs_err_inf / np.max(np.abs(u_exact)) + assert rel_err_inf < tol, f"{k}: {rel_err_inf} < {tol} failed." + + +if __name__ == "__main__": + test_array_variable_transfer() diff --git a/test/coreneuron/test_spikes.py b/test/coreneuron/test_spikes.py index 6522774648..cecb8a3d0e 100644 --- a/test/coreneuron/test_spikes.py +++ b/test/coreneuron/test_spikes.py @@ -127,7 +127,7 @@ def run(mode): coreneuron.model_path = "coreneuron_input" run(0) # revert setting for the following coreneuron runs - coreneuron._model_path = None + coreneuron.model_path = None return h diff --git a/test/coreneuron/unit/CMakeLists.txt b/test/coreneuron/unit/CMakeLists.txt index 0b099c3eac..b21d48abd5 100644 --- a/test/coreneuron/unit/CMakeLists.txt +++ b/test/coreneuron/unit/CMakeLists.txt @@ -23,6 +23,7 @@ if(CORENRN_ENABLE_UNIT_TESTS) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/alignment) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/queueing) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/solver) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/random) # lfp test uses nrnmpi_* wrappers but does not load the dynamic MPI library TODO: re-enable after # NEURON and CoreNEURON dynamic MPI are merged if(NOT NRN_ENABLE_MPI_DYNAMIC) diff --git a/test/coreneuron/unit/random/CMakeLists.txt b/test/coreneuron/unit/random/CMakeLists.txt new file mode 100644 index 0000000000..0c5effaa21 --- /dev/null +++ b/test/coreneuron/unit/random/CMakeLists.txt @@ -0,0 +1,16 @@ +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# ============================================================================= +if(${CMAKE_C_COMPILER_VERSION} VERSION_GREATER_EQUAL 21.11) + # Random123 does not play nicely with NVHPC 21.11+'s detection of ABM features, see: + # https://github.com/BlueBrain/CoreNeuron/issues/724 and + # https://github.com/DEShawResearch/random123/issues/6. + list(APPEND NRN_R123_COMPILE_DEFS R123_USE_INTRIN_H=0) +endif() +add_executable(random_test_bin test_random.cpp) +target_link_libraries(random_test_bin coreneuron-unit-test Catch2::Catch2WithMain) +target_compile_definitions(random_test_bin PRIVATE ${NRN_R123_COMPILE_DEFS}) +add_test(NAME random_test COMMAND $) +cpp_cc_configure_sanitizers(TARGET random_test_bin TEST random_test) diff --git a/test/coreneuron/unit/random/test_random.cpp b/test/coreneuron/unit/random/test_random.cpp new file mode 100644 index 0000000000..32db3435a6 --- /dev/null +++ b/test/coreneuron/unit/random/test_random.cpp @@ -0,0 +1,52 @@ +/* +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# =============================================================================. +*/ +#include +#include +#include "coreneuron/utils/randoms/nrnran123.h" + +using namespace coreneuron; + +TEST_CASE("random123 smoke test") { + const int SEED_KEY = 1; + const int NUM_STREAMS = 20; + const int NUM_SAMPLES = 1000; + nrnran123_State* rand_streams[NUM_STREAMS]; + + const int res_size = NUM_SAMPLES * NUM_STREAMS; + double res[res_size]; + + for (int i = 0; i < NUM_STREAMS; i++) { + rand_streams[i] = nrnran123_newstream(SEED_KEY, i); + nrnran123_setseq(rand_streams[i], 0, 0); + } + + nrn_pragma_omp(target teams distribute parallel for map(tofrom: res[0:res_size]) map(to: rand_streams[0:NUM_STREAMS])) + nrn_pragma_acc(parallel loop copy(res [0:res_size]) copyin(rand_streams [0:NUM_STREAMS])) + for (int i = 0; i < NUM_STREAMS; i++) { + for (int j = 0; j < NUM_SAMPLES; j++) { + double val = nrnran123_dblpick(rand_streams[i]); + res[i * NUM_SAMPLES + j] = val; + } + } + + // there should be no duplicates + + std::set check_set; + + for (int i = 0; i < NUM_STREAMS * NUM_SAMPLES; i++) { + double d = res[i]; + size_t old_size = check_set.size(); + check_set.insert(res[i]); + size_t new_size = check_set.size(); + + if (old_size == new_size) { + std::cerr << "Duplicate found! i = " << i << ", d = " << d << std::endl; + } + } + REQUIRE(check_set.size() == NUM_SAMPLES * NUM_STREAMS); +} \ No newline at end of file diff --git a/test/external/CMakeLists.txt b/test/external/CMakeLists.txt index c4dfafe589..5dcf1a4bce 100644 --- a/test/external/CMakeLists.txt +++ b/test/external/CMakeLists.txt @@ -7,7 +7,7 @@ include(FetchContent) FetchContent_Declare( ringtest GIT_REPOSITORY https://github.com/neuronsimulator/ringtest - GIT_TAG d40caf64ac24c6ddcf2c082f4b0b7f7ca50657c9 + GIT_TAG 4ede4751c68f72a9884c6af4f94f65ff638054a8 SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/tests/ringtest) FetchContent_Declare( diff --git a/test/external/ringtest/CMakeLists.txt b/test/external/ringtest/CMakeLists.txt index 22edacc360..a6383de6db 100644 --- a/test/external/ringtest/CMakeLists.txt +++ b/test/external/ringtest/CMakeLists.txt @@ -46,6 +46,20 @@ nrn_add_test( ENVIRONMENT NEURON_INIT_MPI=1 COMMAND ${ringtest_python} -tstop 100) +# creep into this file to add a nrn only test for ParallelContext.optimize_node_order(i) +nrn_add_test_group( + NAME external_ringtest_nrn + SUBMODULE tests/ringtest # git submodule where the relevant tests are defined + ENVIRONMENT OMP_NUM_THREADS=1 + MODFILE_PATTERNS "mod/*.mod" + SCRIPT_PATTERNS "*.py" "*.hoc") + +nrn_add_test( + GROUP external_ringtest_nrn + NAME optim_node_order + REQUIRES python + COMMAND nrniv -python test_optimize_node_order.py -tstop 50 -rparm -npt 1) + foreach(processor cpu gpu) if("${processor}" STREQUAL "gpu") set(gpu_arg -gpu) diff --git a/test/hoctests/tests/test_nrniv-launch.py b/test/hoctests/tests/test_nrniv-launch.py index c9407da81d..43682dee88 100644 --- a/test/hoctests/tests/test_nrniv-launch.py +++ b/test/hoctests/tests/test_nrniv-launch.py @@ -40,6 +40,7 @@ def nrniv(args, input): print "a = ", a print "square(a)=", square(a) quit() + """, ) diff --git a/test/hoctests/tests/test_optim_node_order.json b/test/hoctests/tests/test_optim_node_order.json new file mode 100644 index 0000000000..afee3567b3 --- /dev/null +++ b/test/hoctests/tests/test_optim_node_order.json @@ -0,0 +1,86 @@ +{ + "node order 0 nthread 1": [ + 10.0, + 20.0, + 30.0, + 11.0, + 12.0, + 21.0, + 22.0, + 23.0, + 31.0, + 32.0, + 33.0, + 34.0 + ], + "node order 1 nthread 1": [ + 10.0, + 20.0, + 30.0, + 11.0, + 21.0, + 31.0, + 12.0, + 22.0, + 32.0, + 23.0, + 33.0, + 34.0 + ], + "node order 2 nthread 1": [ + 10.0, + 20.0, + 30.0, + 11.0, + 21.0, + 31.0, + 22.0, + 32.0, + 33.0, + 12.0, + 23.0, + 34.0 + ], + "node order 0 nthread 2": [ + 10.0, + 20.0, + 11.0, + 12.0, + 21.0, + 22.0, + 23.0, + 30.0, + 31.0, + 32.0, + 33.0, + 34.0 + ], + "node order 1 nthread 2": [ + 10.0, + 20.0, + 11.0, + 21.0, + 12.0, + 22.0, + 23.0, + 30.0, + 31.0, + 32.0, + 33.0, + 34.0 + ], + "node order 2 nthread 2": [ + 10.0, + 20.0, + 11.0, + 21.0, + 22.0, + 12.0, + 23.0, + 30.0, + 31.0, + 32.0, + 33.0, + 34.0 + ] +} diff --git a/test/hoctests/tests/test_optim_node_order.py b/test/hoctests/tests/test_optim_node_order.py new file mode 100644 index 0000000000..7a5f1c979b --- /dev/null +++ b/test/hoctests/tests/test_optim_node_order.py @@ -0,0 +1,95 @@ +from neuron import h +from neuron.tests.utils.checkresult import Chk +import os + +# Create a helper for managing reference results +dir_path = os.path.dirname(os.path.realpath(__file__)) +chk = Chk(os.path.join(dir_path, "test_optim_node_order.json")) + +pc = h.ParallelContext() + +# Default Node order for simulation given a particular construction order +# is the order of section creation. + + +class Cell: + def __init__(self, id): + self.id = id + d = self.den = h.Section(name="d", cell=self) + d.nseg = id + for i, seg in enumerate(d.allseg()): + seg.v = i + 10 * id + + pc.set_gid2node(id, pc.id()) + pc.cell(id, h.NetCon(d(1)._ref_v, None, sec=d)) + syns = self.syns = [h.ExpSyn(d(0)) for i in range(3)] + for i, syn in enumerate(syns): + syn.tau = i + 10 * id + + def __str__(self): + return "Cell_%d" % self.id + + +cells = [Cell(id) for id in range(1, 4)] + +pr = False + + +def printv(): + s = {} # associate root sections with thread id. + for i in range(pc.nthread()): + for sec in pc.get_partition(i): + s[sec] = i + if pr: + print(s) + ns = 0 + for sec in h.allsec(): + for seg in sec.allseg(): + if pr: + print( + seg, + seg.v, + seg.node_index(), + seg._ref_v, + str(s[sec] if sec in s else ""), + ) + ns += 1 + + names = [None for i in range(ns)] + for sec in h.allsec(): + for seg in sec.allseg(): + ix = int(str(seg._ref_v).split()[1][4:].split("/")[0]) + names[ix] = seg.v + for i, seg in enumerate(names): + if pr: + print(i, seg) + tag = "node order %d nthread %d" % (pc.optimize_node_order(), pc.nthread()) + chk(tag, names) + + +# printv() + +# Iteration order (sec,seg) is associated with construction order +# (provided nseg set after each section construction?) +# and the only difference in simulation order is that all root nodes are at the beginning. +# (provided there is only one thread) + + +def pvmes(i): + if pr: + print("node order %d nthread %d" % (i, pc.nthread())) + h.finitialize() + printv() + + +def p(): + for i in range(3): + pc.optimize_node_order(i) + pvmes(i) + + +p() +pc.nthread(2) +p() + +chk.save() diff --git a/test/pytest_coreneuron/test_coreneuron_configuration.py b/test/pytest_coreneuron/test_coreneuron_configuration.py new file mode 100644 index 0000000000..cd6f158d97 --- /dev/null +++ b/test/pytest_coreneuron/test_coreneuron_configuration.py @@ -0,0 +1,24 @@ +from neuron import coreneuron + + +def test_coreneuron_configuration(): + with coreneuron( + restore_path="restore", + save_path="save", + model_path="coredat", + skip_write_model_to_disk=True, + ): + assert coreneuron.restore_path == "restore" + assert coreneuron.save_path == "save" + assert coreneuron.model_path == "coredat" + assert coreneuron.skip_write_model_to_disk == True + + # back to the old values outside the "with" context + assert coreneuron.restore_path is None + assert coreneuron.save_path is None + assert coreneuron.model_path is None + assert coreneuron.skip_write_model_to_disk == False + + +if __name__ == "__main__": + test_coreneuron_configuration() diff --git a/test/unit_tests/node_order_optim/permutations.cpp b/test/unit_tests/node_order_optim/permutations.cpp new file mode 100644 index 0000000000..15cb63f6ea --- /dev/null +++ b/test/unit_tests/node_order_optim/permutations.cpp @@ -0,0 +1,30 @@ +#include "node_order_optim/permute_utils.hpp" + +#include + +#include + +TEST_CASE("Permutation algorithms", "[Neuron][node_order_optim][permute]") { + GIVEN("A vector of elements and a permutation vector") { + const std::vector values{49, 32, 17, 29}; + const std::vector forward_permutation{3, 2, 0, 1}; + const std::vector inverse_permutation{2, 3, 1, 0}; + THEN("forward_permute is done correctly") { + std::vector forward_permutted_values{29, 17, 49, 32}; + auto values_copy{values}; + forward_permute(values_copy, forward_permutation); + REQUIRE(values_copy == forward_permutted_values); + } + THEN("Inversion of the permute vector is done correctly") { + REQUIRE(inverse_permute_vector(forward_permutation) == inverse_permutation); + /// Make sure that pinv[p[i]] = i = p[pinv[i]] + const std::vector index{0, 1, 2, 3}; + auto permutation_copy{forward_permutation}; + forward_permute(permutation_copy, inverse_permutation); + REQUIRE(permutation_copy == index); + auto inverse_permutation_copy{inverse_permutation}; + forward_permute(inverse_permutation_copy, forward_permutation); + REQUIRE(inverse_permutation_copy == index); + } + } +}