From 17be061e031701736cb03a5088b7ddf253a069a4 Mon Sep 17 00:00:00 2001 From: Weina Ji Date: Wed, 13 Mar 2024 21:08:01 +0100 Subject: [PATCH] Callbacks for reading mapping information from memory (#2555) * Implement necessary changes for in-memory transfer for `gid_3.dat` i.e. report related information * no need to send ReportEvent back to nrn, skip them * add new test from ringtests --- src/coreneuron/io/core2nrn_data_return.cpp | 4 + src/coreneuron/io/nrn2core_direct.h | 15 +++ src/coreneuron/io/nrn_filehandler.hpp | 13 +-- src/coreneuron/io/nrn_setup.cpp | 38 ++----- src/coreneuron/io/phase3.cpp | 101 ++++++++++++++++++ src/coreneuron/io/phase3.hpp | 21 ++++ src/coreneuron/io/reports/report_event.hpp | 3 + src/coreneuron/network/netcon.hpp | 1 + .../callbacks/nrncore_callbacks.cpp | 36 +++++++ .../callbacks/nrncore_callbacks.h | 15 +++ src/nrniv/nrncore_write/io/nrncore_io.cpp | 59 +++++++--- test/external/CMakeLists.txt | 2 +- test/external/ringtest/CMakeLists.txt | 39 +++++++ 13 files changed, 292 insertions(+), 55 deletions(-) create mode 100644 src/coreneuron/io/phase3.cpp create mode 100644 src/coreneuron/io/phase3.hpp diff --git a/src/coreneuron/io/core2nrn_data_return.cpp b/src/coreneuron/io/core2nrn_data_return.cpp index 5b13d4880d..3aa230911a 100644 --- a/src/coreneuron/io/core2nrn_data_return.cpp +++ b/src/coreneuron/io/core2nrn_data_return.cpp @@ -572,6 +572,10 @@ static bool core2nrn_tqueue_item(TQItem* q, SelfEventWeightMap& sewm, NrnThread& // nothing to transfer break; } + case ReportEventType: { + // no need to transfer ReportEvent + break; + } default: { // In particular, InputPreSyn does not appear in tqueue as it // immediately fans out to NetCon. diff --git a/src/coreneuron/io/nrn2core_direct.h b/src/coreneuron/io/nrn2core_direct.h index 5790f2197d..2c81fda5a7 100644 --- a/src/coreneuron/io/nrn2core_direct.h +++ b/src/coreneuron/io/nrn2core_direct.h @@ -10,6 +10,7 @@ #include #include +#include extern "C" { // The callbacks into nrn/src/nrniv/nrnbbcore_write.cpp to get @@ -96,6 +97,20 @@ extern int (*nrn2core_get_dat2_vecplay_inst_)(int tid, extern void (*nrn2core_part2_clean_)(); +extern void (*nrn2core_get_dat3_cell_count_)(int& cell_count); +extern void ( + *nrn2core_get_dat3_cellmapping_)(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +extern void (*nrn2core_get_dat3_secmapping_)(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); + /* what variables to send back to NEURON on each time step */ extern void (*nrn2core_get_trajectory_requests_)(int tid, int& bsize, diff --git a/src/coreneuron/io/nrn_filehandler.hpp b/src/coreneuron/io/nrn_filehandler.hpp index 9ac7f048e8..3241fd3506 100644 --- a/src/coreneuron/io/nrn_filehandler.hpp +++ b/src/coreneuron/io/nrn_filehandler.hpp @@ -127,16 +127,13 @@ class FileHandler { mapinfo->name = std::string(name); if (nseg) { - std::vector sec, seg; - std::vector lfp_factors; - - sec.reserve(nseg); - seg.reserve(nseg); - lfp_factors.reserve(total_lfp_factors); + auto sec = read_vector(nseg); + auto seg = read_vector(nseg); - read_array(&sec[0], nseg); - read_array(&seg[0], nseg); + 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); } diff --git a/src/coreneuron/io/nrn_setup.cpp b/src/coreneuron/io/nrn_setup.cpp index 0a3dc11e87..888f07949a 100644 --- a/src/coreneuron/io/nrn_setup.cpp +++ b/src/coreneuron/io/nrn_setup.cpp @@ -34,6 +34,7 @@ #include "coreneuron/utils/nrnoc_aux.hpp" #include "coreneuron/io/phase1.hpp" #include "coreneuron/io/phase2.hpp" +#include "coreneuron/io/phase3.hpp" #include "coreneuron/io/mech_report.h" #include "coreneuron/io/reports/nrnreport.hpp" @@ -516,7 +517,7 @@ void nrn_setup(const char* filesdat, } if (is_mapping_needed) - coreneuron::phase_wrapper(userParams); + coreneuron::phase_wrapper(userParams, !corenrn_file_mode); *mindelay = set_mindelay(*mindelay); @@ -928,37 +929,16 @@ void read_phase2(NrnThread& nt, UserParams& userParams) { /** read mapping information for neurons */ void read_phase3(NrnThread& nt, UserParams& userParams) { - /** restore checkpoint state (before restoring queue items */ - auto& F = userParams.file_reader[nt.id]; - F.restore_checkpoint(); - /** mapping information for all neurons in single NrnThread */ NrnThreadMappingInfo* ntmapping = new NrnThreadMappingInfo(); - int count = 0; - - F.read_mapping_cell_count(&count); - - /** number of cells in mapping file should equal to cells in NrnThread */ - nrn_assert(count == nt.ncell); - - /** for every neuron */ - for (int i = 0; i < nt.ncell; i++) { - int gid, nsec, nseg, nseclist; - - // read counts - F.read_mapping_count(&gid, &nsec, &nseg, &nseclist); - - CellMapping* cmap = new CellMapping(gid); - - // read section-segment mapping for every section list - for (int j = 0; j < nseclist; j++) { - SecMapping* smap = new SecMapping(); - F.read_mapping_info(smap, ntmapping, cmap); - cmap->add_sec_map(smap); - } - - ntmapping->add_cell_mapping(cmap); + Phase3 p3; + if (corenrn_embedded && !corenrn_file_mode) { + p3.read_direct(ntmapping); + } else { + auto& F = userParams.file_reader[nt.id]; + F.restore_checkpoint(); + p3.read_file(F, ntmapping); } // make number #cells match with mapping size diff --git a/src/coreneuron/io/phase3.cpp b/src/coreneuron/io/phase3.cpp new file mode 100644 index 0000000000..ca73ac829c --- /dev/null +++ b/src/coreneuron/io/phase3.cpp @@ -0,0 +1,101 @@ +/* +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# ============================================================================= +*/ +#include + +#include "coreneuron/io/phase3.hpp" +// Where nrn2core_get_dat3_secmapping_ is declared with extern "C" to avoid symbol name mangling +// caused by dual ABI for std::string +#include "coreneuron/io/nrn2core_direct.h" + +void (*nrn2core_get_dat3_cell_count_)(int& cell_count); +void (*nrn2core_get_dat3_cellmapping_)(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +void (*nrn2core_get_dat3_secmapping_)(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); + +namespace coreneuron { +void Phase3::read_file(FileHandler& F, NrnThreadMappingInfo* ntmapping) { + int count = 0; + F.read_mapping_cell_count(&count); + /** for every neuron */ + for (int i = 0; i < count; i++) { + int gid, nsec, nseg, nseclist; + // read counts + F.read_mapping_count(&gid, &nsec, &nseg, &nseclist); + CellMapping* cmap = new CellMapping(gid); + // read section-segment mapping for every section list + for (int j = 0; j < nseclist; j++) { + SecMapping* smap = new SecMapping(); + F.read_mapping_info(smap, ntmapping, cmap); + cmap->add_sec_map(smap); + } + ntmapping->add_cell_mapping(cmap); + } +} + +void Phase3::read_direct(NrnThreadMappingInfo* ntmapping) { + int count; + nrn2core_get_dat3_cell_count_(count); + /** for every neuron */ + for (int i = 0; i < count; i++) { + int gid; + int t_sec; + int t_seg; + int nseclist; + nrn2core_get_dat3_cellmapping_(i, gid, t_sec, t_seg, nseclist); + auto cmap = new CellMapping(gid); + for (int j = 0; j < nseclist; j++) { + std::string sclname; + int n_sec; + int n_seg; + int n_electrodes; + size_t total_lfp_factors; + std::vector data_sec; + std::vector data_seg; + std::vector data_lfp; + nrn2core_get_dat3_secmapping_(i, + j, + sclname, + n_sec, + n_seg, + total_lfp_factors, + n_electrodes, + data_sec, + data_seg, + data_lfp); + auto smap = new SecMapping(); + smap->name = sclname; + for (int i_seg = 0; i_seg < n_seg; i_seg++) { + smap->add_segment(data_sec[i_seg], data_seg[i_seg]); + ntmapping->add_segment_id(data_seg[i_seg]); + int factor_offset = i_seg * n_electrodes; + if (total_lfp_factors > 0) { + // Abort if the factors contains a NaN + nrn_assert(count_if(data_lfp.begin(), data_lfp.end(), [](double d) { + return std::isnan(d); + }) == 0); + std::vector segment_factors(data_lfp.begin() + factor_offset, + data_lfp.begin() + factor_offset + + n_electrodes); + cmap->add_segment_lfp_factor(data_seg[i], segment_factors); + } + } + cmap->add_sec_map(smap); + } + ntmapping->add_cell_mapping(cmap); + } +} + +} // namespace coreneuron diff --git a/src/coreneuron/io/phase3.hpp b/src/coreneuron/io/phase3.hpp new file mode 100644 index 0000000000..eae88a437a --- /dev/null +++ b/src/coreneuron/io/phase3.hpp @@ -0,0 +1,21 @@ +/* +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# ============================================================================= +*/ + +#pragma once + +#include "coreneuron/io/nrn_filehandler.hpp" + +namespace coreneuron { +struct NrnThreadMappingInfo; + +class Phase3 { + public: + void read_file(FileHandler& F, NrnThreadMappingInfo* ntmapping); + void read_direct(NrnThreadMappingInfo* ntmapping); +}; +} // namespace coreneuron diff --git a/src/coreneuron/io/reports/report_event.hpp b/src/coreneuron/io/reports/report_event.hpp index 0bb9a922b2..e019fe6582 100644 --- a/src/coreneuron/io/reports/report_event.hpp +++ b/src/coreneuron/io/reports/report_event.hpp @@ -44,6 +44,9 @@ class ReportEvent: public DiscreteEvent { bool require_checkpoint() override; void summation_alu(NrnThread* nt); void lfp_calc(NrnThread* nt); + int type() const override { + return ReportEventType; + } private: double dt; diff --git a/src/coreneuron/network/netcon.hpp b/src/coreneuron/network/netcon.hpp index 613a454f43..fc1ee75aab 100644 --- a/src/coreneuron/network/netcon.hpp +++ b/src/coreneuron/network/netcon.hpp @@ -26,6 +26,7 @@ class NetCvode; #define PreSynType 4 #define NetParEventType 7 #define InputPreSynType 20 +#define ReportEventType 8 struct DiscreteEvent { DiscreteEvent() = default; diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp index ec88e782a7..124e2d071d 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp @@ -18,6 +18,7 @@ extern TQueue* net_cvode_instance_event_queue(NrnThread*); #include "vrecitem.h" // for nrnbbcore_vecplay_write #include "nrnwrap_dlfcn.h" +#include "nrnsection_mapping.h" extern bbcore_write_t* nrn_bbcore_write_; extern bbcore_write_t* nrn_bbcore_read_; @@ -26,6 +27,7 @@ extern bool corenrn_direct; extern int* bbcore_dparam_size; extern double nrn_ion_charge(Symbol*); extern CellGroup* cellgroups_; +extern NrnMappingInfo mapinfo; extern NetCvode* net_cvode_instance; extern char* pnt_map; extern void* nrn_interthread_enqueue(NrnThread*); @@ -219,6 +221,40 @@ int nrnthread_dat1(int tid, return 1; } +void nrnthread_dat3_cell_count(int& cell_count) { + cell_count = mapinfo.size(); +} + +void nrnthread_dat3_cellmapping(int i, int& gid, int& nsec, int& nseg, int& n_seclist) { + CellMapping* c = mapinfo.mapping[i]; + gid = c->gid; + nsec = c->num_sections(); + nseg = c->num_segments(); + n_seclist = c->size(); +} + +void nrnthread_dat3_secmapping(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp) { + CellMapping* c = mapinfo.mapping[i_c]; + SecMapping* s = c->secmapping[i_sec]; + sclname = s->name; + nsec = s->nsec; + nseg = s->size(); + total_lfp_factors = s->seglfp_factors.size(); + n_electrodes = s->num_electrodes; + data_sec = s->sections; + data_seg = s->segments; + data_lfp = s->seglfp_factors; +} + // sizes and total data count int nrnthread_dat2_1(int tid, int& ncell, diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h index efce1359fb..b93b0cd53f 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h @@ -97,6 +97,18 @@ int nrnthread_dat2_vecplay_inst(int tid, int& last_index, int& discon_index, int& ubound_index); +void nrnthread_dat3_cell_count(int& cell_count); +void nrnthread_dat3_cellmapping(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +void nrnthread_dat3_secmapping(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); int* datum2int(int type, Memb_list* ml, @@ -215,6 +227,9 @@ static core2nrn_callback_t cnbs[] = { {"nrn2core_get_dat2_vecplay_", (CNB) nrnthread_dat2_vecplay}, {"nrn2core_get_dat2_vecplay_inst_", (CNB) nrnthread_dat2_vecplay_inst}, {"nrn2core_part2_clean_", (CNB) part2_clean}, + {"nrn2core_get_dat3_cell_count_", (CNB) nrnthread_dat3_cell_count}, + {"nrn2core_get_dat3_cellmapping_", (CNB) nrnthread_dat3_cellmapping}, + {"nrn2core_get_dat3_secmapping_", (CNB) nrnthread_dat3_secmapping}, {"nrn2core_get_trajectory_requests_", (CNB) nrnthread_get_trajectory_requests}, {"nrn2core_trajectory_values_", (CNB) nrnthread_trajectory_values}, diff --git a/src/nrniv/nrncore_write/io/nrncore_io.cpp b/src/nrniv/nrncore_write/io/nrncore_io.cpp index 4b3fa7f709..46e0913ce5 100644 --- a/src/nrniv/nrncore_write/io/nrncore_io.cpp +++ b/src/nrniv/nrncore_write/io/nrncore_io.cpp @@ -529,6 +529,10 @@ void write_nrnthread_task(const char* path, CellGroup* cgs, bool append) { /** @brief dump mapping information to gid_3.dat file */ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { + if (minfo.size() <= 0) { + return; + } + /** full path of mapping file */ std::stringstream ss; ss << path << "/" << gid << "_3.dat"; @@ -543,33 +547,54 @@ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { fprintf(f, "%s\n", bbcore_write_version); /** number of gids in NrnThread */ - fprintf(f, "%zd\n", minfo.size()); + int count; + nrnthread_dat3_cell_count(count); + fprintf(f, "%zd\n", count); /** all cells mapping information in NrnThread */ - for (size_t i = 0; i < minfo.size(); i++) { - CellMapping* c = minfo.mapping[i]; - + for (size_t i = 0; i < count; i++) { + int cgid; + int t_sec; + int t_seg; + 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", c->gid, c->num_sections(), c->num_segments(), c->size()); - - for (size_t j = 0; j < c->size(); j++) { - SecMapping* s = c->secmapping[j]; - size_t total_lfp_factors = s->seglfp_factors.size(); + fprintf(f, "%d %d %d %zd\n", cgid, t_sec, t_seg, n_seclist); + + for (size_t j = 0; j < n_seclist; j++) { + std::string sclname; + int nsec; + int nseg; + int n_electrodes; + size_t total_lfp_factors; + std::vector data_sec; + std::vector data_seg; + std::vector data_lfp; + nrnthread_dat3_secmapping(i, + j, + sclname, + nsec, + nseg, + total_lfp_factors, + n_electrodes, + data_sec, + data_seg, + data_lfp); /** section list name, number of sections, number of segments */ fprintf(f, "%s %d %zd %zd %d\n", - s->name.c_str(), - s->nsec, - s->size(), + sclname.c_str(), + nsec, + nseg, total_lfp_factors, - s->num_electrodes); + n_electrodes); /** section - segment mapping */ - if (s->size()) { - writeint(&(s->sections.front()), s->size()); - writeint(&(s->segments.front()), s->size()); + if (nseg) { + writeint(&(data_sec.front()), nseg); + writeint(&(data_seg.front()), nseg); if (total_lfp_factors) { - writedbl(&(s->seglfp_factors.front()), total_lfp_factors); + writedbl(&(data_lfp.front()), total_lfp_factors); } } } diff --git a/test/external/CMakeLists.txt b/test/external/CMakeLists.txt index 7a9c8f3e63..c4dfafe589 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 ee24c8 + GIT_TAG d40caf64ac24c6ddcf2c082f4b0b7f7ca50657c9 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 d082f1fd99..22edacc360 100644 --- a/test/external/ringtest/CMakeLists.txt +++ b/test/external/ringtest/CMakeLists.txt @@ -126,7 +126,46 @@ foreach(processor cpu gpu) ENVIRONMENT OMP_NUM_THREADS=${ringtest_num_threads} NEURON_INIT_MPI=1 COMMAND ${ringtest_python} -tstop 100 -coreneuron -nt ${ringtest_num_datasets} ${gpu_arg}) endforeach() + +nrn_add_test( + GROUP external_ringtest + NAME coreneuron_mpi_registermapping_directmode + REQUIRES coreneuron mpi python + PROCESSORS ${ringtest_mpi_ranks} + ENVIRONMENT NEURON_INIT_MPI=1 LIBSONATA_ZERO_BASED_GIDS=1 + COMMAND ${ringtest_special} -tstop 100 -coreneuron -registermapping) +nrn_add_test( + GROUP external_ringtest + NAME coreneuron_mpi_registermapping_filemode + REQUIRES coreneuron mpi python + PROCESSORS ${ringtest_mpi_ranks} + ENVIRONMENT NEURON_INIT_MPI=1 LIBSONATA_ZERO_BASED_GIDS=1 + COMMAND ${ringtest_special} -tstop 100 -coreneuron -registermapping -filemode) + # Step 3 -- add a job that compares the outputs of all the tests added in Step 2 nrn_add_test_group_comparison( GROUP external_ringtest REFERENCE_OUTPUT asciispikes::external/tests/ringtest/reference_data/spk1.100ms.std.ref) + +if(CORENRN_ENABLE_REPORTING) + find_program(H5DIFF_EXECUTABLE "h5diff") + if(H5DIFF_EXECUTABLE) + set(reference_path + "${PROJECT_BINARY_DIR}/test/external/tests/ringtest/reference_data/soma.ref.h5") + cpp_cc_build_time_copy( + INPUT "${PROJECT_SOURCE_DIR}/external/tests/ringtest/reference_data/soma.ref.h5" + OUTPUT "${reference_path}") + foreach(testfolder coreneuron_mpi_registermapping_directmode + coreneuron_mpi_registermapping_filemode) + add_test(NAME external_ringtest::compare_soma_report_${testfolder} + COMMAND "h5diff" ${PROJECT_BINARY_DIR}/test/external_ringtest/${testfolder}/soma.h5 + ${reference_path}) + set_tests_properties(external_ringtest::compare_soma_report_${testfolder} + PROPERTIES DEPENDS "external_ringtest::${testfolder}") + endforeach() + else() + message(WARNING "Can not find h5diff, skip comparing the soma report for " + "external_ringtest::coreneuron_mpi_registermapping_directmode " + "external_ringtest::coreneuron_mpi_registermapping_filemode") + endif() +endif()