// Do NOT change. Changes will be lost next time file is generated #define R__DICTIONARY_FILENAME dIhomedIahmaddIbiodynamo2dIbuilddIlibbiodynamo_dict #define R__NO_DEPRECATION /*******************************************************************/ #include #include #include #include #include #define G__DICTIONARY #include "RConfig.h" #include "TClass.h" #include "TDictAttributeMap.h" #include "TInterpreter.h" #include "TROOT.h" #include "TBuffer.h" #include "TMemberInspector.h" #include "TInterpreter.h" #include "TVirtualMutex.h" #include "TError.h" #ifndef G__ROOT #define G__ROOT #endif #include "RtypesImp.h" #include "TIsAProxy.h" #include "TFileMergeInfo.h" #include #include "TCollectionProxyInfo.h" /*******************************************************************/ #include "TDataMember.h" // The generated code does not explicitly qualifies STL entities namespace std {} using namespace std; // Header files passed as explicit arguments #include "/home/ahmad/biodynamo2/src/biodynamo.h" #include "/home/ahmad/biodynamo2/src/core/biology_module/biology_module.h" #include "/home/ahmad/biodynamo2/src/core/biology_module/grow_divide.h" #include "/home/ahmad/biodynamo2/src/core/biology_module/regulate_genes.h" #include "/home/ahmad/biodynamo2/src/core/container/fixed_size_vector.h" #include "/home/ahmad/biodynamo2/src/core/container/inline_vector.h" #include "/home/ahmad/biodynamo2/src/core/container/math_array.h" #include "/home/ahmad/biodynamo2/src/core/container/parallel_resize_vector.h" #include "/home/ahmad/biodynamo2/src/core/container/sim_object_vector.h" #include "/home/ahmad/biodynamo2/src/core/default_force.h" #include "/home/ahmad/biodynamo2/src/core/diffusion_grid.h" #include "/home/ahmad/biodynamo2/src/core/event/cell_division_event.h" #include "/home/ahmad/biodynamo2/src/core/event/event.h" #include "/home/ahmad/biodynamo2/src/core/execution_context/in_place_exec_ctxt.h" #include "/home/ahmad/biodynamo2/src/core/exporter.h" #include "/home/ahmad/biodynamo2/src/core/gpu/displacement_op_cuda_kernel.h" #include "/home/ahmad/biodynamo2/src/core/gpu/gpu_helper.h" #include "/home/ahmad/biodynamo2/src/core/gpu/opencl_state.h" #include "/home/ahmad/biodynamo2/src/core/grid.h" #include "/home/ahmad/biodynamo2/src/core/model_initializer.h" #include "/home/ahmad/biodynamo2/src/core/operation/bound_space_op.h" #include "/home/ahmad/biodynamo2/src/core/operation/diffusion_op.h" #include "/home/ahmad/biodynamo2/src/core/operation/displacement_op.h" #include "/home/ahmad/biodynamo2/src/core/operation/displacement_op_cpu.h" #include "/home/ahmad/biodynamo2/src/core/operation/displacement_op_cuda.h" #include "/home/ahmad/biodynamo2/src/core/operation/displacement_op_opencl.h" #include "/home/ahmad/biodynamo2/src/core/operation/dividing_cell_op.h" #include "/home/ahmad/biodynamo2/src/core/operation/op_timer.h" #include "/home/ahmad/biodynamo2/src/core/operation/operation.h" #include "/home/ahmad/biodynamo2/src/core/operation/vtune_op_wrapper.h" #include "/home/ahmad/biodynamo2/src/core/param/command_line_options.h" #include "/home/ahmad/biodynamo2/src/core/param/module_param.h" #include "/home/ahmad/biodynamo2/src/core/param/param.h" #include "/home/ahmad/biodynamo2/src/core/resource_manager.h" #include "/home/ahmad/biodynamo2/src/core/scheduler.h" #include "/home/ahmad/biodynamo2/src/core/shape.h" #include "/home/ahmad/biodynamo2/src/core/sim_object/cell.h" #include "/home/ahmad/biodynamo2/src/core/sim_object/sim_object.h" #include "/home/ahmad/biodynamo2/src/core/sim_object/so_pointer.h" #include "/home/ahmad/biodynamo2/src/core/sim_object/so_uid.h" #include "/home/ahmad/biodynamo2/src/core/sim_object/so_visitor.h" #include "/home/ahmad/biodynamo2/src/core/simulation.h" #include "/home/ahmad/biodynamo2/src/core/simulation_backup.h" #include "/home/ahmad/biodynamo2/src/core/substance_initializers.h" #include "/home/ahmad/biodynamo2/src/core/util/cpptoml.h" #include "/home/ahmad/biodynamo2/src/core/util/debug.h" #include "/home/ahmad/biodynamo2/src/core/util/io.h" #include "/home/ahmad/biodynamo2/src/core/util/log.h" #include "/home/ahmad/biodynamo2/src/core/util/macros.h" #include "/home/ahmad/biodynamo2/src/core/util/math.h" #include "/home/ahmad/biodynamo2/src/core/util/numa.h" #include "/home/ahmad/biodynamo2/src/core/util/random.h" #include "/home/ahmad/biodynamo2/src/core/util/root.h" #include "/home/ahmad/biodynamo2/src/core/util/spinlock.h" #include "/home/ahmad/biodynamo2/src/core/util/string.h" #include "/home/ahmad/biodynamo2/src/core/util/thread_info.h" #include "/home/ahmad/biodynamo2/src/core/util/timing.h" #include "/home/ahmad/biodynamo2/src/core/util/timing_aggregator.h" #include "/home/ahmad/biodynamo2/src/core/util/tuple.h" #include "/home/ahmad/biodynamo2/src/core/util/type.h" #include "/home/ahmad/biodynamo2/src/core/util/vtune.h" #include "/home/ahmad/biodynamo2/src/core/visualization/catalyst_adaptor.h" #include "/home/ahmad/biodynamo2/src/core/visualization/catalyst_helper_structs.h" #include "/home/ahmad/biodynamo2/src/core/visualization/catalyst_so_visitor.h" #include "/home/ahmad/biodynamo2/src/core/visualization/insitu_pipeline.h" #include "/home/ahmad/biodynamo2/src/core/visualization/notebook_util.h" #include "/home/ahmad/biodynamo2/src/core/visualization/root_adaptor.h" #include "/home/ahmad/biodynamo2/src/neuroscience/event/neurite_bifurcation_event.h" #include "/home/ahmad/biodynamo2/src/neuroscience/event/neurite_branching_event.h" #include "/home/ahmad/biodynamo2/src/neuroscience/event/new_neurite_extension_event.h" #include "/home/ahmad/biodynamo2/src/neuroscience/event/side_neurite_extension_event.h" #include "/home/ahmad/biodynamo2/src/neuroscience/event/split_neurite_element_event.h" #include "/home/ahmad/biodynamo2/src/neuroscience/module.h" #include "/home/ahmad/biodynamo2/src/neuroscience/neurite_element.h" #include "/home/ahmad/biodynamo2/src/neuroscience/neuron_or_neurite.h" #include "/home/ahmad/biodynamo2/src/neuroscience/neuron_soma.h" #include "/home/ahmad/biodynamo2/src/neuroscience/neuroscience.h" #include "/home/ahmad/biodynamo2/src/neuroscience/param.h" // Header files passed via #pragma extra_include namespace ROOT { static TClass *bdmcLcLMathArraylEdoublecO3gR_Dictionary(); static void bdmcLcLMathArraylEdoublecO3gR_TClassManip(TClass*); static void *new_bdmcLcLMathArraylEdoublecO3gR(void *p = 0); static void *newArray_bdmcLcLMathArraylEdoublecO3gR(Long_t size, void *p); static void delete_bdmcLcLMathArraylEdoublecO3gR(void *p); static void deleteArray_bdmcLcLMathArraylEdoublecO3gR(void *p); static void destruct_bdmcLcLMathArraylEdoublecO3gR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::MathArray*) { ::bdm::MathArray *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::MathArray >(0); static ::ROOT::TGenericClassInfo instance("bdm::MathArray", ::bdm::MathArray::Class_Version(), "core/container/math_array.h", 34, typeid(::bdm::MathArray), ::ROOT::Internal::DefineBehavior(ptr, ptr), &bdmcLcLMathArraylEdoublecO3gR_Dictionary, isa_proxy, 4, sizeof(::bdm::MathArray) ); instance.SetNew(&new_bdmcLcLMathArraylEdoublecO3gR); instance.SetNewArray(&newArray_bdmcLcLMathArraylEdoublecO3gR); instance.SetDelete(&delete_bdmcLcLMathArraylEdoublecO3gR); instance.SetDeleteArray(&deleteArray_bdmcLcLMathArraylEdoublecO3gR); instance.SetDestructor(&destruct_bdmcLcLMathArraylEdoublecO3gR); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::MathArray*) { return GenerateInitInstanceLocal((::bdm::MathArray*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *bdmcLcLMathArraylEdoublecO3gR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); bdmcLcLMathArraylEdoublecO3gR_TClassManip(theClass); return theClass; } static void bdmcLcLMathArraylEdoublecO3gR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace ROOT { static TClass *bdmcLcLMathArraylEdoublecO4gR_Dictionary(); static void bdmcLcLMathArraylEdoublecO4gR_TClassManip(TClass*); static void *new_bdmcLcLMathArraylEdoublecO4gR(void *p = 0); static void *newArray_bdmcLcLMathArraylEdoublecO4gR(Long_t size, void *p); static void delete_bdmcLcLMathArraylEdoublecO4gR(void *p); static void deleteArray_bdmcLcLMathArraylEdoublecO4gR(void *p); static void destruct_bdmcLcLMathArraylEdoublecO4gR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::MathArray*) { ::bdm::MathArray *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::MathArray >(0); static ::ROOT::TGenericClassInfo instance("bdm::MathArray", ::bdm::MathArray::Class_Version(), "core/container/math_array.h", 34, typeid(::bdm::MathArray), ::ROOT::Internal::DefineBehavior(ptr, ptr), &bdmcLcLMathArraylEdoublecO4gR_Dictionary, isa_proxy, 4, sizeof(::bdm::MathArray) ); instance.SetNew(&new_bdmcLcLMathArraylEdoublecO4gR); instance.SetNewArray(&newArray_bdmcLcLMathArraylEdoublecO4gR); instance.SetDelete(&delete_bdmcLcLMathArraylEdoublecO4gR); instance.SetDeleteArray(&deleteArray_bdmcLcLMathArraylEdoublecO4gR); instance.SetDestructor(&destruct_bdmcLcLMathArraylEdoublecO4gR); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::MathArray*) { return GenerateInitInstanceLocal((::bdm::MathArray*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *bdmcLcLMathArraylEdoublecO4gR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); bdmcLcLMathArraylEdoublecO4gR_TClassManip(theClass); return theClass; } static void bdmcLcLMathArraylEdoublecO4gR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLRandom(void *p = 0); static void *newArray_bdmcLcLRandom(Long_t size, void *p); static void delete_bdmcLcLRandom(void *p); static void deleteArray_bdmcLcLRandom(void *p); static void destruct_bdmcLcLRandom(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::Random*) { ::bdm::Random *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::Random >(0); static ::ROOT::TGenericClassInfo instance("bdm::Random", ::bdm::Random::Class_Version(), "core/util/random.h", 28, typeid(::bdm::Random), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::Random::Dictionary, isa_proxy, 4, sizeof(::bdm::Random) ); instance.SetNew(&new_bdmcLcLRandom); instance.SetNewArray(&newArray_bdmcLcLRandom); instance.SetDelete(&delete_bdmcLcLRandom); instance.SetDeleteArray(&deleteArray_bdmcLcLRandom); instance.SetDestructor(&destruct_bdmcLcLRandom); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::Random*) { return GenerateInitInstanceLocal((::bdm::Random*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::Random*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLSimulation(void *p = 0); static void delete_bdmcLcLSimulation(void *p); static void deleteArray_bdmcLcLSimulation(void *p); static void destruct_bdmcLcLSimulation(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::Simulation*) { ::bdm::Simulation *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::Simulation >(0); static ::ROOT::TGenericClassInfo instance("bdm::Simulation", ::bdm::Simulation::Class_Version(), "core/simulation.h", 41, typeid(::bdm::Simulation), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::Simulation::Dictionary, isa_proxy, 4, sizeof(::bdm::Simulation) ); instance.SetNew(&new_bdmcLcLSimulation); instance.SetDelete(&delete_bdmcLcLSimulation); instance.SetDeleteArray(&deleteArray_bdmcLcLSimulation); instance.SetDestructor(&destruct_bdmcLcLSimulation); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::Simulation*) { return GenerateInitInstanceLocal((::bdm::Simulation*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::Simulation*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void delete_bdmcLcLBaseBiologyModule(void *p); static void deleteArray_bdmcLcLBaseBiologyModule(void *p); static void destruct_bdmcLcLBaseBiologyModule(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::BaseBiologyModule*) { ::bdm::BaseBiologyModule *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::BaseBiologyModule >(0); static ::ROOT::TGenericClassInfo instance("bdm::BaseBiologyModule", ::bdm::BaseBiologyModule::Class_Version(), "core/biology_module/biology_module.h", 26, typeid(::bdm::BaseBiologyModule), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::BaseBiologyModule::Dictionary, isa_proxy, 4, sizeof(::bdm::BaseBiologyModule) ); instance.SetDelete(&delete_bdmcLcLBaseBiologyModule); instance.SetDeleteArray(&deleteArray_bdmcLcLBaseBiologyModule); instance.SetDestructor(&destruct_bdmcLcLBaseBiologyModule); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::BaseBiologyModule*) { return GenerateInitInstanceLocal((::bdm::BaseBiologyModule*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::BaseBiologyModule*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void delete_bdmcLcLModuleParam(void *p); static void deleteArray_bdmcLcLModuleParam(void *p); static void destruct_bdmcLcLModuleParam(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::ModuleParam*) { ::bdm::ModuleParam *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::ModuleParam >(0); static ::ROOT::TGenericClassInfo instance("bdm::ModuleParam", ::bdm::ModuleParam::Class_Version(), "core/param/module_param.h", 43, typeid(::bdm::ModuleParam), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::ModuleParam::Dictionary, isa_proxy, 4, sizeof(::bdm::ModuleParam) ); instance.SetDelete(&delete_bdmcLcLModuleParam); instance.SetDeleteArray(&deleteArray_bdmcLcLModuleParam); instance.SetDestructor(&destruct_bdmcLcLModuleParam); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::ModuleParam*) { return GenerateInitInstanceLocal((::bdm::ModuleParam*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::ModuleParam*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLParam(void *p = 0); static void *newArray_bdmcLcLParam(Long_t size, void *p); static void delete_bdmcLcLParam(void *p); static void deleteArray_bdmcLcLParam(void *p); static void destruct_bdmcLcLParam(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::Param*) { ::bdm::Param *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::Param >(0); static ::ROOT::TGenericClassInfo instance("bdm::Param", ::bdm::Param::Class_Version(), "core/param/param.h", 33, typeid(::bdm::Param), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::Param::Dictionary, isa_proxy, 4, sizeof(::bdm::Param) ); instance.SetNew(&new_bdmcLcLParam); instance.SetNewArray(&newArray_bdmcLcLParam); instance.SetDelete(&delete_bdmcLcLParam); instance.SetDeleteArray(&deleteArray_bdmcLcLParam); instance.SetDestructor(&destruct_bdmcLcLParam); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::Param*) { return GenerateInitInstanceLocal((::bdm::Param*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::Param*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLCell(void *p = 0); static void delete_bdmcLcLCell(void *p); static void deleteArray_bdmcLcLCell(void *p); static void destruct_bdmcLcLCell(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::Cell*) { ::bdm::Cell *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::Cell >(0); static ::ROOT::TGenericClassInfo instance("bdm::Cell", ::bdm::Cell::Class_Version(), "core/sim_object/cell.h", 39, typeid(::bdm::Cell), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::Cell::Dictionary, isa_proxy, 4, sizeof(::bdm::Cell) ); instance.SetNew(&new_bdmcLcLCell); instance.SetDelete(&delete_bdmcLcLCell); instance.SetDeleteArray(&deleteArray_bdmcLcLCell); instance.SetDestructor(&destruct_bdmcLcLCell); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::Cell*) { return GenerateInitInstanceLocal((::bdm::Cell*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::Cell*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLGrowDivide(void *p = 0); static void *newArray_bdmcLcLGrowDivide(Long_t size, void *p); static void delete_bdmcLcLGrowDivide(void *p); static void deleteArray_bdmcLcLGrowDivide(void *p); static void destruct_bdmcLcLGrowDivide(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::GrowDivide*) { ::bdm::GrowDivide *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::GrowDivide >(0); static ::ROOT::TGenericClassInfo instance("bdm::GrowDivide", ::bdm::GrowDivide::Class_Version(), "core/biology_module/grow_divide.h", 28, typeid(::bdm::GrowDivide), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::GrowDivide::Dictionary, isa_proxy, 4, sizeof(::bdm::GrowDivide) ); instance.SetNew(&new_bdmcLcLGrowDivide); instance.SetNewArray(&newArray_bdmcLcLGrowDivide); instance.SetDelete(&delete_bdmcLcLGrowDivide); instance.SetDeleteArray(&deleteArray_bdmcLcLGrowDivide); instance.SetDestructor(&destruct_bdmcLcLGrowDivide); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::GrowDivide*) { return GenerateInitInstanceLocal((::bdm::GrowDivide*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::GrowDivide*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLRegulateGenes(void *p = 0); static void *newArray_bdmcLcLRegulateGenes(Long_t size, void *p); static void delete_bdmcLcLRegulateGenes(void *p); static void deleteArray_bdmcLcLRegulateGenes(void *p); static void destruct_bdmcLcLRegulateGenes(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::RegulateGenes*) { ::bdm::RegulateGenes *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::RegulateGenes >(0); static ::ROOT::TGenericClassInfo instance("bdm::RegulateGenes", ::bdm::RegulateGenes::Class_Version(), "core/biology_module/regulate_genes.h", 36, typeid(::bdm::RegulateGenes), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::RegulateGenes::Dictionary, isa_proxy, 4, sizeof(::bdm::RegulateGenes) ); instance.SetNew(&new_bdmcLcLRegulateGenes); instance.SetNewArray(&newArray_bdmcLcLRegulateGenes); instance.SetDelete(&delete_bdmcLcLRegulateGenes); instance.SetDeleteArray(&deleteArray_bdmcLcLRegulateGenes); instance.SetDestructor(&destruct_bdmcLcLRegulateGenes); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::RegulateGenes*) { return GenerateInitInstanceLocal((::bdm::RegulateGenes*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::RegulateGenes*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLDiffusionGrid(void *p = 0); static void delete_bdmcLcLDiffusionGrid(void *p); static void deleteArray_bdmcLcLDiffusionGrid(void *p); static void destruct_bdmcLcLDiffusionGrid(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::DiffusionGrid*) { ::bdm::DiffusionGrid *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::DiffusionGrid >(0); static ::ROOT::TGenericClassInfo instance("bdm::DiffusionGrid", ::bdm::DiffusionGrid::Class_Version(), "core/diffusion_grid.h", 39, typeid(::bdm::DiffusionGrid), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::DiffusionGrid::Dictionary, isa_proxy, 4, sizeof(::bdm::DiffusionGrid) ); instance.SetNew(&new_bdmcLcLDiffusionGrid); instance.SetDelete(&delete_bdmcLcLDiffusionGrid); instance.SetDeleteArray(&deleteArray_bdmcLcLDiffusionGrid); instance.SetDestructor(&destruct_bdmcLcLDiffusionGrid); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::DiffusionGrid*) { return GenerateInitInstanceLocal((::bdm::DiffusionGrid*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::DiffusionGrid*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLSoHandle(void *p = 0); static void *newArray_bdmcLcLSoHandle(Long_t size, void *p); static void delete_bdmcLcLSoHandle(void *p); static void deleteArray_bdmcLcLSoHandle(void *p); static void destruct_bdmcLcLSoHandle(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::SoHandle*) { ::bdm::SoHandle *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::SoHandle >(0); static ::ROOT::TGenericClassInfo instance("bdm::SoHandle", ::bdm::SoHandle::Class_Version(), "core/resource_manager.h", 61, typeid(::bdm::SoHandle), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::SoHandle::Dictionary, isa_proxy, 4, sizeof(::bdm::SoHandle) ); instance.SetNew(&new_bdmcLcLSoHandle); instance.SetNewArray(&newArray_bdmcLcLSoHandle); instance.SetDelete(&delete_bdmcLcLSoHandle); instance.SetDeleteArray(&deleteArray_bdmcLcLSoHandle); instance.SetDestructor(&destruct_bdmcLcLSoHandle); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::SoHandle*) { return GenerateInitInstanceLocal((::bdm::SoHandle*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::SoHandle*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLResourceManager(void *p = 0); static void delete_bdmcLcLResourceManager(void *p); static void deleteArray_bdmcLcLResourceManager(void *p); static void destruct_bdmcLcLResourceManager(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::ResourceManager*) { ::bdm::ResourceManager *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::ResourceManager >(0); static ::ROOT::TGenericClassInfo instance("bdm::ResourceManager", ::bdm::ResourceManager::Class_Version(), "core/resource_manager.h", 114, typeid(::bdm::ResourceManager), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::ResourceManager::Dictionary, isa_proxy, 4, sizeof(::bdm::ResourceManager) ); instance.SetNew(&new_bdmcLcLResourceManager); instance.SetDelete(&delete_bdmcLcLResourceManager); instance.SetDeleteArray(&deleteArray_bdmcLcLResourceManager); instance.SetDestructor(&destruct_bdmcLcLResourceManager); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::ResourceManager*) { return GenerateInitInstanceLocal((::bdm::ResourceManager*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::ResourceManager*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p = 0); static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p); static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p); static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::experimental::neuroscience::NeuronSoma*) { ::bdm::experimental::neuroscience::NeuronSoma *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::experimental::neuroscience::NeuronSoma >(0); static ::ROOT::TGenericClassInfo instance("bdm::experimental::neuroscience::NeuronSoma", ::bdm::experimental::neuroscience::NeuronSoma::Class_Version(), "neuroscience/neuron_soma.h", 31, typeid(::bdm::experimental::neuroscience::NeuronSoma), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::experimental::neuroscience::NeuronSoma::Dictionary, isa_proxy, 4, sizeof(::bdm::experimental::neuroscience::NeuronSoma) ); instance.SetNew(&new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma); instance.SetDelete(&delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma); instance.SetDeleteArray(&deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma); instance.SetDestructor(&destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::experimental::neuroscience::NeuronSoma*) { return GenerateInitInstanceLocal((::bdm::experimental::neuroscience::NeuronSoma*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuronSoma*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p = 0); static void *newArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam(Long_t size, void *p); static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p); static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p); static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::experimental::neuroscience::Param*) { ::bdm::experimental::neuroscience::Param *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::experimental::neuroscience::Param >(0); static ::ROOT::TGenericClassInfo instance("bdm::experimental::neuroscience::Param", ::bdm::experimental::neuroscience::Param::Class_Version(), "neuroscience/param.h", 31, typeid(::bdm::experimental::neuroscience::Param), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::experimental::neuroscience::Param::Dictionary, isa_proxy, 4, sizeof(::bdm::experimental::neuroscience::Param) ); instance.SetNew(&new_bdmcLcLexperimentalcLcLneurosciencecLcLParam); instance.SetNewArray(&newArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam); instance.SetDelete(&delete_bdmcLcLexperimentalcLcLneurosciencecLcLParam); instance.SetDeleteArray(&deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam); instance.SetDestructor(&destruct_bdmcLcLexperimentalcLcLneurosciencecLcLParam); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::experimental::neuroscience::Param*) { return GenerateInitInstanceLocal((::bdm::experimental::neuroscience::Param*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::Param*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p = 0); static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p); static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p); static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::experimental::neuroscience::NeuriteElement*) { ::bdm::experimental::neuroscience::NeuriteElement *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::experimental::neuroscience::NeuriteElement >(0); static ::ROOT::TGenericClassInfo instance("bdm::experimental::neuroscience::NeuriteElement", ::bdm::experimental::neuroscience::NeuriteElement::Class_Version(), "neuroscience/neurite_element.h", 56, typeid(::bdm::experimental::neuroscience::NeuriteElement), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::experimental::neuroscience::NeuriteElement::Dictionary, isa_proxy, 4, sizeof(::bdm::experimental::neuroscience::NeuriteElement) ); instance.SetNew(&new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement); instance.SetDelete(&delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement); instance.SetDeleteArray(&deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement); instance.SetDestructor(&destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::experimental::neuroscience::NeuriteElement*) { return GenerateInitInstanceLocal((::bdm::experimental::neuroscience::NeuriteElement*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuriteElement*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static void *new_bdmcLcLRuntimeVariables(void *p = 0); static void delete_bdmcLcLRuntimeVariables(void *p); static void deleteArray_bdmcLcLRuntimeVariables(void *p); static void destruct_bdmcLcLRuntimeVariables(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::RuntimeVariables*) { ::bdm::RuntimeVariables *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::RuntimeVariables >(0); static ::ROOT::TGenericClassInfo instance("bdm::RuntimeVariables", ::bdm::RuntimeVariables::Class_Version(), "core/util/io.h", 28, typeid(::bdm::RuntimeVariables), ::ROOT::Internal::DefineBehavior(ptr, ptr), &::bdm::RuntimeVariables::Dictionary, isa_proxy, 4, sizeof(::bdm::RuntimeVariables) ); instance.SetNew(&new_bdmcLcLRuntimeVariables); instance.SetDelete(&delete_bdmcLcLRuntimeVariables); instance.SetDeleteArray(&deleteArray_bdmcLcLRuntimeVariables); instance.SetDestructor(&destruct_bdmcLcLRuntimeVariables); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::RuntimeVariables*) { return GenerateInitInstanceLocal((::bdm::RuntimeVariables*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::RuntimeVariables*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); } // end of namespace ROOT namespace ROOT { static TClass *bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_Dictionary(); static void bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_TClassManip(TClass*); static void *new_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p = 0); static void delete_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p); static void deleteArray_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p); static void destruct_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const ::bdm::IntegralTypeWrapper*) { ::bdm::IntegralTypeWrapper *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< ::bdm::IntegralTypeWrapper >(0); static ::ROOT::TGenericClassInfo instance("bdm::IntegralTypeWrapper", ::bdm::IntegralTypeWrapper::Class_Version(), "core/util/io.h", 63, typeid(::bdm::IntegralTypeWrapper), ::ROOT::Internal::DefineBehavior(ptr, ptr), &bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_Dictionary, isa_proxy, 4, sizeof(::bdm::IntegralTypeWrapper) ); instance.SetNew(&new_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR); instance.SetDelete(&delete_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR); instance.SetDeleteArray(&deleteArray_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR); instance.SetDestructor(&destruct_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR); ::ROOT::AddClassAlternate("bdm::IntegralTypeWrapper","bdm::IntegralTypeWrapper"); return &instance; } TGenericClassInfo *GenerateInitInstance(const ::bdm::IntegralTypeWrapper*) { return GenerateInitInstanceLocal((::bdm::IntegralTypeWrapper*)0); } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0)->GetClass(); bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_TClassManip(theClass); return theClass; } static void bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace bdm { //______________________________________________________________________________ template <> atomic_TClass_ptr MathArray::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ template <> const char *MathArray::Class_Name() { return "bdm::MathArray"; } //______________________________________________________________________________ template <> const char *MathArray::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetImplFileName(); } //______________________________________________________________________________ template <> int MathArray::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ template <> TClass *MathArray::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ template <> TClass *MathArray::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ template <> atomic_TClass_ptr MathArray::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ template <> const char *MathArray::Class_Name() { return "bdm::MathArray"; } //______________________________________________________________________________ template <> const char *MathArray::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetImplFileName(); } //______________________________________________________________________________ template <> int MathArray::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ template <> TClass *MathArray::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ template <> TClass *MathArray::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::MathArray*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr Random::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *Random::Class_Name() { return "bdm::Random"; } //______________________________________________________________________________ const char *Random::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Random*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int Random::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Random*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *Random::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Random*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *Random::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Random*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr Simulation::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *Simulation::Class_Name() { return "bdm::Simulation"; } //______________________________________________________________________________ const char *Simulation::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Simulation*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int Simulation::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Simulation*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *Simulation::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Simulation*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *Simulation::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Simulation*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr BaseBiologyModule::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *BaseBiologyModule::Class_Name() { return "bdm::BaseBiologyModule"; } //______________________________________________________________________________ const char *BaseBiologyModule::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::BaseBiologyModule*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int BaseBiologyModule::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::BaseBiologyModule*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *BaseBiologyModule::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::BaseBiologyModule*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *BaseBiologyModule::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::BaseBiologyModule*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr ModuleParam::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *ModuleParam::Class_Name() { return "bdm::ModuleParam"; } //______________________________________________________________________________ const char *ModuleParam::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::ModuleParam*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int ModuleParam::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::ModuleParam*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *ModuleParam::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::ModuleParam*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *ModuleParam::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::ModuleParam*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr Param::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *Param::Class_Name() { return "bdm::Param"; } //______________________________________________________________________________ const char *Param::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Param*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int Param::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Param*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *Param::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Param*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *Param::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Param*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr Cell::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *Cell::Class_Name() { return "bdm::Cell"; } //______________________________________________________________________________ const char *Cell::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Cell*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int Cell::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::Cell*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *Cell::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Cell*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *Cell::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::Cell*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr GrowDivide::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *GrowDivide::Class_Name() { return "bdm::GrowDivide"; } //______________________________________________________________________________ const char *GrowDivide::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::GrowDivide*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int GrowDivide::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::GrowDivide*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *GrowDivide::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::GrowDivide*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *GrowDivide::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::GrowDivide*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr RegulateGenes::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *RegulateGenes::Class_Name() { return "bdm::RegulateGenes"; } //______________________________________________________________________________ const char *RegulateGenes::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::RegulateGenes*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int RegulateGenes::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::RegulateGenes*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *RegulateGenes::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::RegulateGenes*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *RegulateGenes::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::RegulateGenes*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr DiffusionGrid::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *DiffusionGrid::Class_Name() { return "bdm::DiffusionGrid"; } //______________________________________________________________________________ const char *DiffusionGrid::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::DiffusionGrid*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int DiffusionGrid::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::DiffusionGrid*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *DiffusionGrid::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::DiffusionGrid*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *DiffusionGrid::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::DiffusionGrid*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr SoHandle::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *SoHandle::Class_Name() { return "bdm::SoHandle"; } //______________________________________________________________________________ const char *SoHandle::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::SoHandle*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int SoHandle::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::SoHandle*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *SoHandle::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::SoHandle*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *SoHandle::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::SoHandle*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr ResourceManager::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *ResourceManager::Class_Name() { return "bdm::ResourceManager"; } //______________________________________________________________________________ const char *ResourceManager::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::ResourceManager*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int ResourceManager::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::ResourceManager*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *ResourceManager::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::ResourceManager*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *ResourceManager::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::ResourceManager*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ atomic_TClass_ptr NeuronSoma::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *NeuronSoma::Class_Name() { return "bdm::experimental::neuroscience::NeuronSoma"; } //______________________________________________________________________________ const char *NeuronSoma::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuronSoma*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int NeuronSoma::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuronSoma*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *NeuronSoma::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuronSoma*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *NeuronSoma::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuronSoma*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ atomic_TClass_ptr Param::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *Param::Class_Name() { return "bdm::experimental::neuroscience::Param"; } //______________________________________________________________________________ const char *Param::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::Param*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int Param::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::Param*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *Param::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::Param*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *Param::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::Param*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ atomic_TClass_ptr NeuriteElement::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *NeuriteElement::Class_Name() { return "bdm::experimental::neuroscience::NeuriteElement"; } //______________________________________________________________________________ const char *NeuriteElement::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuriteElement*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int NeuriteElement::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuriteElement*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *NeuriteElement::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuriteElement*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *NeuriteElement::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::experimental::neuroscience::NeuriteElement*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace bdm { //______________________________________________________________________________ atomic_TClass_ptr RuntimeVariables::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ const char *RuntimeVariables::Class_Name() { return "bdm::RuntimeVariables"; } //______________________________________________________________________________ const char *RuntimeVariables::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::RuntimeVariables*)0x0)->GetImplFileName(); } //______________________________________________________________________________ int RuntimeVariables::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::RuntimeVariables*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ TClass *RuntimeVariables::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::RuntimeVariables*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ TClass *RuntimeVariables::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::RuntimeVariables*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ template <> atomic_TClass_ptr IntegralTypeWrapper::fgIsA(0); // static to hold class pointer //______________________________________________________________________________ template <> const char *IntegralTypeWrapper::Class_Name() { return "bdm::IntegralTypeWrapper"; } //______________________________________________________________________________ template <> const char *IntegralTypeWrapper::ImplFileName() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0)->GetImplFileName(); } //______________________________________________________________________________ template <> int IntegralTypeWrapper::ImplFileLine() { return ::ROOT::GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0)->GetImplFileLine(); } //______________________________________________________________________________ template <> TClass *IntegralTypeWrapper::Dictionary() { fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0)->GetClass(); return fgIsA; } //______________________________________________________________________________ template <> TClass *IntegralTypeWrapper::Class() { if (!fgIsA.load()) { R__LOCKGUARD(gInterpreterMutex); fgIsA = ::ROOT::GenerateInitInstanceLocal((const ::bdm::IntegralTypeWrapper*)0x0)->GetClass(); } return fgIsA; } } // namespace bdm namespace bdm { //______________________________________________________________________________ template <> void MathArray::Streamer(TBuffer &R__b) { // Stream an object of class bdm::MathArray. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::MathArray::Class(),this); } else { R__b.WriteClassBuffer(bdm::MathArray::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLMathArraylEdoublecO3gR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::MathArray : new ::bdm::MathArray; } static void *newArray_bdmcLcLMathArraylEdoublecO3gR(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::MathArray[nElements] : new ::bdm::MathArray[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLMathArraylEdoublecO3gR(void *p) { delete ((::bdm::MathArray*)p); } static void deleteArray_bdmcLcLMathArraylEdoublecO3gR(void *p) { delete [] ((::bdm::MathArray*)p); } static void destruct_bdmcLcLMathArraylEdoublecO3gR(void *p) { typedef ::bdm::MathArray current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::MathArray namespace bdm { //______________________________________________________________________________ template <> void MathArray::Streamer(TBuffer &R__b) { // Stream an object of class bdm::MathArray. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::MathArray::Class(),this); } else { R__b.WriteClassBuffer(bdm::MathArray::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLMathArraylEdoublecO4gR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::MathArray : new ::bdm::MathArray; } static void *newArray_bdmcLcLMathArraylEdoublecO4gR(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::MathArray[nElements] : new ::bdm::MathArray[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLMathArraylEdoublecO4gR(void *p) { delete ((::bdm::MathArray*)p); } static void deleteArray_bdmcLcLMathArraylEdoublecO4gR(void *p) { delete [] ((::bdm::MathArray*)p); } static void destruct_bdmcLcLMathArraylEdoublecO4gR(void *p) { typedef ::bdm::MathArray current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::MathArray namespace bdm { //______________________________________________________________________________ void Random::Streamer(TBuffer &R__b) { // Stream an object of class bdm::Random. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::Random::Class(),this); } else { R__b.WriteClassBuffer(bdm::Random::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLRandom(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Random : new ::bdm::Random; } static void *newArray_bdmcLcLRandom(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Random[nElements] : new ::bdm::Random[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLRandom(void *p) { delete ((::bdm::Random*)p); } static void deleteArray_bdmcLcLRandom(void *p) { delete [] ((::bdm::Random*)p); } static void destruct_bdmcLcLRandom(void *p) { typedef ::bdm::Random current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::Random namespace bdm { //______________________________________________________________________________ void Simulation::Streamer(TBuffer &R__b) { // Stream an object of class bdm::Simulation. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::Simulation::Class(),this); } else { R__b.WriteClassBuffer(bdm::Simulation::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLSimulation(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Simulation( (TRootIOCtor *)nullptr ) : new ::bdm::Simulation( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLSimulation(void *p) { delete ((::bdm::Simulation*)p); } static void deleteArray_bdmcLcLSimulation(void *p) { delete [] ((::bdm::Simulation*)p); } static void destruct_bdmcLcLSimulation(void *p) { typedef ::bdm::Simulation current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::Simulation namespace bdm { //______________________________________________________________________________ void BaseBiologyModule::Streamer(TBuffer &R__b) { // Stream an object of class bdm::BaseBiologyModule. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::BaseBiologyModule::Class(),this); } else { R__b.WriteClassBuffer(bdm::BaseBiologyModule::Class(),this); } } } // namespace bdm namespace ROOT { // Wrapper around operator delete static void delete_bdmcLcLBaseBiologyModule(void *p) { delete ((::bdm::BaseBiologyModule*)p); } static void deleteArray_bdmcLcLBaseBiologyModule(void *p) { delete [] ((::bdm::BaseBiologyModule*)p); } static void destruct_bdmcLcLBaseBiologyModule(void *p) { typedef ::bdm::BaseBiologyModule current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::BaseBiologyModule namespace bdm { //______________________________________________________________________________ void ModuleParam::Streamer(TBuffer &R__b) { // Stream an object of class bdm::ModuleParam. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::ModuleParam::Class(),this); } else { R__b.WriteClassBuffer(bdm::ModuleParam::Class(),this); } } } // namespace bdm namespace ROOT { // Wrapper around operator delete static void delete_bdmcLcLModuleParam(void *p) { delete ((::bdm::ModuleParam*)p); } static void deleteArray_bdmcLcLModuleParam(void *p) { delete [] ((::bdm::ModuleParam*)p); } static void destruct_bdmcLcLModuleParam(void *p) { typedef ::bdm::ModuleParam current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::ModuleParam namespace bdm { //______________________________________________________________________________ void Param::Streamer(TBuffer &R__b) { // Stream an object of class bdm::Param. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::Param::Class(),this); } else { R__b.WriteClassBuffer(bdm::Param::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLParam(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Param : new ::bdm::Param; } static void *newArray_bdmcLcLParam(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Param[nElements] : new ::bdm::Param[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLParam(void *p) { delete ((::bdm::Param*)p); } static void deleteArray_bdmcLcLParam(void *p) { delete [] ((::bdm::Param*)p); } static void destruct_bdmcLcLParam(void *p) { typedef ::bdm::Param current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::Param namespace bdm { //______________________________________________________________________________ void Cell::Streamer(TBuffer &R__b) { // Stream an object of class bdm::Cell. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::Cell::Class(),this); } else { R__b.WriteClassBuffer(bdm::Cell::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLCell(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::Cell( (TRootIOCtor *)nullptr ) : new ::bdm::Cell( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLCell(void *p) { delete ((::bdm::Cell*)p); } static void deleteArray_bdmcLcLCell(void *p) { delete [] ((::bdm::Cell*)p); } static void destruct_bdmcLcLCell(void *p) { typedef ::bdm::Cell current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::Cell namespace bdm { //______________________________________________________________________________ void GrowDivide::Streamer(TBuffer &R__b) { // Stream an object of class bdm::GrowDivide. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::GrowDivide::Class(),this); } else { R__b.WriteClassBuffer(bdm::GrowDivide::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLGrowDivide(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::GrowDivide : new ::bdm::GrowDivide; } static void *newArray_bdmcLcLGrowDivide(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::GrowDivide[nElements] : new ::bdm::GrowDivide[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLGrowDivide(void *p) { delete ((::bdm::GrowDivide*)p); } static void deleteArray_bdmcLcLGrowDivide(void *p) { delete [] ((::bdm::GrowDivide*)p); } static void destruct_bdmcLcLGrowDivide(void *p) { typedef ::bdm::GrowDivide current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::GrowDivide namespace bdm { //______________________________________________________________________________ void RegulateGenes::Streamer(TBuffer &R__b) { // Stream an object of class bdm::RegulateGenes. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::RegulateGenes::Class(),this); } else { R__b.WriteClassBuffer(bdm::RegulateGenes::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLRegulateGenes(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::RegulateGenes : new ::bdm::RegulateGenes; } static void *newArray_bdmcLcLRegulateGenes(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::RegulateGenes[nElements] : new ::bdm::RegulateGenes[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLRegulateGenes(void *p) { delete ((::bdm::RegulateGenes*)p); } static void deleteArray_bdmcLcLRegulateGenes(void *p) { delete [] ((::bdm::RegulateGenes*)p); } static void destruct_bdmcLcLRegulateGenes(void *p) { typedef ::bdm::RegulateGenes current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::RegulateGenes namespace bdm { //______________________________________________________________________________ void DiffusionGrid::Streamer(TBuffer &R__b) { // Stream an object of class bdm::DiffusionGrid. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::DiffusionGrid::Class(),this); } else { R__b.WriteClassBuffer(bdm::DiffusionGrid::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLDiffusionGrid(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::DiffusionGrid( (TRootIOCtor *)nullptr ) : new ::bdm::DiffusionGrid( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLDiffusionGrid(void *p) { delete ((::bdm::DiffusionGrid*)p); } static void deleteArray_bdmcLcLDiffusionGrid(void *p) { delete [] ((::bdm::DiffusionGrid*)p); } static void destruct_bdmcLcLDiffusionGrid(void *p) { typedef ::bdm::DiffusionGrid current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::DiffusionGrid namespace bdm { //______________________________________________________________________________ void SoHandle::Streamer(TBuffer &R__b) { // Stream an object of class bdm::SoHandle. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::SoHandle::Class(),this); } else { R__b.WriteClassBuffer(bdm::SoHandle::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLSoHandle(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::SoHandle : new ::bdm::SoHandle; } static void *newArray_bdmcLcLSoHandle(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::SoHandle[nElements] : new ::bdm::SoHandle[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLSoHandle(void *p) { delete ((::bdm::SoHandle*)p); } static void deleteArray_bdmcLcLSoHandle(void *p) { delete [] ((::bdm::SoHandle*)p); } static void destruct_bdmcLcLSoHandle(void *p) { typedef ::bdm::SoHandle current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::SoHandle namespace bdm { //______________________________________________________________________________ void ResourceManager::Streamer(TBuffer &R__b) { // Stream an object of class bdm::ResourceManager. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::ResourceManager::Class(),this); } else { R__b.WriteClassBuffer(bdm::ResourceManager::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLResourceManager(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::ResourceManager( (TRootIOCtor *)nullptr ) : new ::bdm::ResourceManager( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLResourceManager(void *p) { delete ((::bdm::ResourceManager*)p); } static void deleteArray_bdmcLcLResourceManager(void *p) { delete [] ((::bdm::ResourceManager*)p); } static void destruct_bdmcLcLResourceManager(void *p) { typedef ::bdm::ResourceManager current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::ResourceManager namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ void NeuronSoma::Streamer(TBuffer &R__b) { // Stream an object of class bdm::experimental::neuroscience::NeuronSoma. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::experimental::neuroscience::NeuronSoma::Class(),this); } else { R__b.WriteClassBuffer(bdm::experimental::neuroscience::NeuronSoma::Class(),this); } } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::experimental::neuroscience::NeuronSoma( (TRootIOCtor *)nullptr ) : new ::bdm::experimental::neuroscience::NeuronSoma( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p) { delete ((::bdm::experimental::neuroscience::NeuronSoma*)p); } static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p) { delete [] ((::bdm::experimental::neuroscience::NeuronSoma*)p); } static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuronSoma(void *p) { typedef ::bdm::experimental::neuroscience::NeuronSoma current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::experimental::neuroscience::NeuronSoma namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ void Param::Streamer(TBuffer &R__b) { // Stream an object of class bdm::experimental::neuroscience::Param. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::experimental::neuroscience::Param::Class(),this); } else { R__b.WriteClassBuffer(bdm::experimental::neuroscience::Param::Class(),this); } } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::experimental::neuroscience::Param : new ::bdm::experimental::neuroscience::Param; } static void *newArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::experimental::neuroscience::Param[nElements] : new ::bdm::experimental::neuroscience::Param[nElements]; } // Wrapper around operator delete static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p) { delete ((::bdm::experimental::neuroscience::Param*)p); } static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p) { delete [] ((::bdm::experimental::neuroscience::Param*)p); } static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLParam(void *p) { typedef ::bdm::experimental::neuroscience::Param current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::experimental::neuroscience::Param namespace bdm { namespace experimental { namespace neuroscience { //______________________________________________________________________________ void NeuriteElement::Streamer(TBuffer &R__b) { // Stream an object of class bdm::experimental::neuroscience::NeuriteElement. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::experimental::neuroscience::NeuriteElement::Class(),this); } else { R__b.WriteClassBuffer(bdm::experimental::neuroscience::NeuriteElement::Class(),this); } } } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience } // namespace bdm::experimental::neuroscience namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::experimental::neuroscience::NeuriteElement( (TRootIOCtor *)nullptr ) : new ::bdm::experimental::neuroscience::NeuriteElement( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p) { delete ((::bdm::experimental::neuroscience::NeuriteElement*)p); } static void deleteArray_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p) { delete [] ((::bdm::experimental::neuroscience::NeuriteElement*)p); } static void destruct_bdmcLcLexperimentalcLcLneurosciencecLcLNeuriteElement(void *p) { typedef ::bdm::experimental::neuroscience::NeuriteElement current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::experimental::neuroscience::NeuriteElement namespace bdm { //______________________________________________________________________________ void RuntimeVariables::Streamer(TBuffer &R__b) { // Stream an object of class bdm::RuntimeVariables. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::RuntimeVariables::Class(),this); } else { R__b.WriteClassBuffer(bdm::RuntimeVariables::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLRuntimeVariables(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::RuntimeVariables( (TRootIOCtor *)nullptr ) : new ::bdm::RuntimeVariables( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLRuntimeVariables(void *p) { delete ((::bdm::RuntimeVariables*)p); } static void deleteArray_bdmcLcLRuntimeVariables(void *p) { delete [] ((::bdm::RuntimeVariables*)p); } static void destruct_bdmcLcLRuntimeVariables(void *p) { typedef ::bdm::RuntimeVariables current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::RuntimeVariables namespace bdm { //______________________________________________________________________________ template <> void IntegralTypeWrapper::Streamer(TBuffer &R__b) { // Stream an object of class bdm::IntegralTypeWrapper. if (R__b.IsReading()) { R__b.ReadClassBuffer(bdm::IntegralTypeWrapper::Class(),this); } else { R__b.WriteClassBuffer(bdm::IntegralTypeWrapper::Class(),this); } } } // namespace bdm namespace ROOT { // Wrappers around operator new static void *new_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) ::bdm::IntegralTypeWrapper( (TRootIOCtor *)nullptr ) : new ::bdm::IntegralTypeWrapper( (TRootIOCtor *)nullptr ); } // Wrapper around operator delete static void delete_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p) { delete ((::bdm::IntegralTypeWrapper*)p); } static void deleteArray_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p) { delete [] ((::bdm::IntegralTypeWrapper*)p); } static void destruct_bdmcLcLIntegralTypeWrapperlEunsignedsPlonggR(void *p) { typedef ::bdm::IntegralTypeWrapper current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class ::bdm::IntegralTypeWrapper namespace ROOT { static TClass *unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_Dictionary(); static void unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_TClassManip(TClass*); static void *new_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p = 0); static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(Long_t size, void *p); static void delete_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p); static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p); static void destruct_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const unordered_map*) { unordered_map *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TIsAProxy(typeid(unordered_map)); static ::ROOT::TGenericClassInfo instance("unordered_map", -2, "unordered_map", 98, typeid(unordered_map), ::ROOT::Internal::DefineBehavior(ptr, ptr), &unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_Dictionary, isa_proxy, 4, sizeof(unordered_map) ); instance.SetNew(&new_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR); instance.SetNewArray(&newArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR); instance.SetDelete(&delete_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR); instance.SetDeleteArray(&deleteArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR); instance.SetDestructor(&destruct_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR); instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::MapInsert< unordered_map >())); return &instance; } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const unordered_map*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const unordered_map*)0x0)->GetClass(); unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_TClassManip(theClass); return theClass; } static void unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace ROOT { // Wrappers around operator new static void *new_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map : new unordered_map; } static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map[nElements] : new unordered_map[nElements]; } // Wrapper around operator delete static void delete_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p) { delete ((unordered_map*)p); } static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p) { delete [] ((unordered_map*)p); } static void destruct_unordered_maplEunsignedsPlongcObdmcLcLSoHandlegR(void *p) { typedef unordered_map current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class unordered_map namespace ROOT { static TClass *unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_Dictionary(); static void unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_TClassManip(TClass*); static void *new_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p = 0); static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(Long_t size, void *p); static void delete_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p); static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p); static void destruct_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const unordered_map*) { unordered_map *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TIsAProxy(typeid(unordered_map)); static ::ROOT::TGenericClassInfo instance("unordered_map", -2, "unordered_map", 98, typeid(unordered_map), ::ROOT::Internal::DefineBehavior(ptr, ptr), &unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_Dictionary, isa_proxy, 4, sizeof(unordered_map) ); instance.SetNew(&new_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR); instance.SetNewArray(&newArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR); instance.SetDelete(&delete_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR); instance.SetDeleteArray(&deleteArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR); instance.SetDestructor(&destruct_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR); instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::MapInsert< unordered_map >())); return &instance; } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const unordered_map*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const unordered_map*)0x0)->GetClass(); unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_TClassManip(theClass); return theClass; } static void unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace ROOT { // Wrappers around operator new static void *new_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map : new unordered_map; } static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map[nElements] : new unordered_map[nElements]; } // Wrapper around operator delete static void delete_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p) { delete ((unordered_map*)p); } static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p) { delete [] ((unordered_map*)p); } static void destruct_unordered_maplEunsignedsPlongcObdmcLcLModuleParammUgR(void *p) { typedef unordered_map current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class unordered_map namespace ROOT { static TClass *unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_Dictionary(); static void unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_TClassManip(TClass*); static void *new_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p = 0); static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(Long_t size, void *p); static void delete_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p); static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p); static void destruct_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p); // Function generating the singleton type initializer static TGenericClassInfo *GenerateInitInstanceLocal(const unordered_map*) { unordered_map *ptr = 0; static ::TVirtualIsAProxy* isa_proxy = new ::TIsAProxy(typeid(unordered_map)); static ::ROOT::TGenericClassInfo instance("unordered_map", -2, "unordered_map", 98, typeid(unordered_map), ::ROOT::Internal::DefineBehavior(ptr, ptr), &unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_Dictionary, isa_proxy, 4, sizeof(unordered_map) ); instance.SetNew(&new_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR); instance.SetNewArray(&newArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR); instance.SetDelete(&delete_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR); instance.SetDeleteArray(&deleteArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR); instance.SetDestructor(&destruct_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR); instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::MapInsert< unordered_map >())); return &instance; } // Static variable to force the class initialization static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal((const unordered_map*)0x0); R__UseDummy(_R__UNIQUE_DICT_(Init)); // Dictionary for non-ClassDef classes static TClass *unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_Dictionary() { TClass* theClass =::ROOT::GenerateInitInstanceLocal((const unordered_map*)0x0)->GetClass(); unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_TClassManip(theClass); return theClass; } static void unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR_TClassManip(TClass* ){ } } // end of namespace ROOT namespace ROOT { // Wrappers around operator new static void *new_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map : new unordered_map; } static void *newArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(Long_t nElements, void *p) { return p ? ::new((::ROOT::Internal::TOperatorNewHelper*)p) unordered_map[nElements] : new unordered_map[nElements]; } // Wrapper around operator delete static void delete_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p) { delete ((unordered_map*)p); } static void deleteArray_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p) { delete [] ((unordered_map*)p); } static void destruct_unordered_maplEunsignedsPlongcObdmcLcLDiffusionGridmUgR(void *p) { typedef unordered_map current_t; ((current_t*)p)->~current_t(); } } // end of namespace ROOT for class unordered_map namespace { void TriggerDictionaryInitialization_libbiodynamo_dict_Impl() { static const char* headers[] = { 0 }; static const char* includePaths[] = { "/usr/include", "/home/ahmad/biodynamo2/build/third_party/paraview/include/paraview-5.6", "/home/ahmad/biodynamo2/build/third_party/paraview/include", "/home/testuser/bdm-build-third-party/paraview-build/install/include", "/home/testuser/bdm-build-third-party/paraview-build/install/include/python2.7", "/usr/lib/openmpi/include/openmpi/opal/mca/event/libevent2021/libevent", "/usr/lib/openmpi/include/openmpi/opal/mca/event/libevent2021/libevent/include", "/usr/lib/openmpi/include", "/usr/lib/openmpi/include/openmpi", "/home/ahmad/biodynamo2/build/third_party/root/include", "/home/ahmad/biodynamo2/src", "/home/ahmad/biodynamo2/test", "/home/ahmad/biodynamo2/third_party", "/home/ahmad/biodynamo2/third_party/omp", "/home/ahmad/biodynamo2/third_party/opencl", "/home/ahmad/biodynamo2/third_party/cxxopts-v2.2.0", "/home/ahmad/biodynamo2/build/extracted-third-party-libs", "/home/ahmad/biodynamo2/build/version", "/home/ahmad/biodynamo2/build/omp", "/home/ahmad/biodynamo2/build/third_party/root/include", "/home/ahmad/biodynamo2/", 0 }; static const char* fwdDeclCode = R"DICTFWDDCLS( #line 1 "libbiodynamo_dict dictionary forward declarations' payload" #pragma clang diagnostic ignored "-Wkeyword-compat" #pragma clang diagnostic ignored "-Wignored-attributes" #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" extern int __Cling_Autoloading_Map; namespace bdm{struct __attribute__((annotate("$clingAutoload$core/param/module_param.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/grow_divide.h"))) ModuleParam;} namespace std{template struct __attribute__((annotate("$clingAutoload$bits/functional_hash.h"))) __attribute__((annotate("$clingAutoload$string"))) hash; } namespace std{template struct __attribute__((annotate("$clingAutoload$bits/stl_function.h"))) __attribute__((annotate("$clingAutoload$string"))) equal_to; } namespace std{template struct __attribute__((annotate("$clingAutoload$bits/stl_pair.h"))) __attribute__((annotate("$clingAutoload$string"))) pair; } namespace std{template class __attribute__((annotate("$clingAutoload$bits/allocator.h"))) __attribute__((annotate("$clingAutoload$string"))) allocator; } namespace bdm{class __attribute__((annotate("$clingAutoload$core/diffusion_grid.h"))) __attribute__((annotate("$clingAutoload$core/grid.h"))) DiffusionGrid;} namespace bdm{class __attribute__((annotate("$clingAutoload$core/resource_manager.h"))) __attribute__((annotate("$clingAutoload$core/grid.h"))) SoHandle;} namespace bdm{template class __attribute__((annotate("$clingAutoload$core/container/math_array.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/biology_module.h"))) MathArray; } namespace bdm{class __attribute__((annotate("$clingAutoload$core/util/random.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/biology_module.h"))) Random;} namespace bdm{class __attribute__((annotate("$clingAutoload$core/simulation.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/biology_module.h"))) Simulation;} namespace bdm{struct __attribute__((annotate("$clingAutoload$core/biology_module/biology_module.h"))) BaseBiologyModule;} namespace bdm{struct __attribute__((annotate("$clingAutoload$core/param/param.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/grow_divide.h"))) Param;} namespace bdm{class __attribute__((annotate("$clingAutoload$core/sim_object/cell.h"))) __attribute__((annotate("$clingAutoload$core/biology_module/grow_divide.h"))) Cell;} namespace bdm{struct __attribute__((annotate("$clingAutoload$core/biology_module/grow_divide.h"))) GrowDivide;} namespace bdm{struct __attribute__((annotate("$clingAutoload$core/biology_module/regulate_genes.h"))) RegulateGenes;} namespace bdm{class __attribute__((annotate("$clingAutoload$core/resource_manager.h"))) __attribute__((annotate("$clingAutoload$core/grid.h"))) ResourceManager;} namespace bdm{namespace experimental{namespace neuroscience{class __attribute__((annotate("$clingAutoload$neuroscience/neuron_soma.h"))) __attribute__((annotate("$clingAutoload$core/visualization/notebook_util.h"))) NeuronSoma;}}} namespace bdm{namespace experimental{namespace neuroscience{struct __attribute__((annotate("$clingAutoload$neuroscience/param.h"))) __attribute__((annotate("$clingAutoload$core/visualization/notebook_util.h"))) Param;}}} namespace bdm{namespace experimental{namespace neuroscience{class __attribute__((annotate("$clingAutoload$neuroscience/neurite_element.h"))) __attribute__((annotate("$clingAutoload$core/visualization/notebook_util.h"))) NeuriteElement;}}} namespace bdm{class __attribute__((annotate("$clingAutoload$core/util/io.h"))) RuntimeVariables;} namespace bdm{template class __attribute__((annotate("$clingAutoload$core/util/io.h"))) IntegralTypeWrapper; } )DICTFWDDCLS"; static const char* payloadCode = R"DICTPAYLOAD( #line 1 "libbiodynamo_dict dictionary payload" #ifndef LINUX #define LINUX 1 #endif #ifndef BDM_SRC_DIR #define BDM_SRC_DIR "/home/ahmad/biodynamo2/src" #endif #ifndef USE_DICT #define USE_DICT 1 #endif #ifndef USE_NUMA #define USE_NUMA 1 #endif #ifndef MPICH_IGNORE_CXX_SEEK #define MPICH_IGNORE_CXX_SEEK 1 #endif #ifndef USE_CATALYST #define USE_CATALYST 1 #endif #define _BACKWARD_BACKWARD_WARNING_H // Inline headers // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef BIODYNAMO_H_ #define BIODYNAMO_H_ #include #include "core/biology_module/biology_module.h" #include "core/biology_module/grow_divide.h" #include "core/biology_module/regulate_genes.h" #include "core/event/cell_division_event.h" #include "core/event/event.h" #include "core/grid.h" #include "core/model_initializer.h" #include "core/param/command_line_options.h" #include "core/param/param.h" #include "core/resource_manager.h" #include "core/scheduler.h" #include "core/shape.h" #include "core/sim_object/cell.h" #include "core/sim_object/sim_object.h" #include "core/util/vtune.h" #include "core/visualization/notebook_util.h" #endif // BIODYNAMO_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_BIOLOGY_MODULE_BIOLOGY_MODULE_H_ #define CORE_BIOLOGY_MODULE_BIOLOGY_MODULE_H_ #include "core/event/event.h" #include "core/sim_object/sim_object.h" #include "core/util/type.h" namespace bdm { /// BaseBiologyModule encapsulates logic to decide for which EventIds /// a biology module should be copied or removed struct BaseBiologyModule { /// Default ctor sets `copy_mask_` and remove_mask_` to 0; meaning that /// `Copy` and `Remove` will always return false BaseBiologyModule() : copy_mask_(0), remove_mask_(0) {} explicit BaseBiologyModule(EventId copy_event, EventId remove_event = 0) : copy_mask_(copy_event), remove_mask_(remove_event) {} BaseBiologyModule(std::initializer_list copy_events, std::initializer_list remove_events = {}) { // copy mask copy_mask_ = 0; for (EventId event : copy_events) { copy_mask_ |= event; } // delete mask remove_mask_ = 0; for (EventId event : remove_events) { remove_mask_ |= event; } } BaseBiologyModule(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) { copy_mask_ = other->copy_mask_; remove_mask_ = other->remove_mask_; } BaseBiologyModule(const BaseBiologyModule& other) : copy_mask_(other.copy_mask_), remove_mask_(other.remove_mask_) {} virtual ~BaseBiologyModule() {} /// Create a new instance of this object using the default constructor. virtual BaseBiologyModule* GetInstance(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) const = 0; /// Create a copy of this biology module. virtual BaseBiologyModule* GetCopy() const = 0; virtual void EventHandler(const Event& event, BaseBiologyModule* other1, BaseBiologyModule* other2 = nullptr) {} virtual void Run(SimObject* so) = 0; /// Function returns whether the biology module should be copied for the /// given event. bool Copy(EventId event) const { return (event & copy_mask_) != 0; } /// Function returns whether the biology module should be removed for the /// given event. bool Remove(EventId event) const { return (event & remove_mask_) != 0; } private: EventId copy_mask_; EventId remove_mask_; BDM_CLASS_DEF(BaseBiologyModule, 2); }; /// Inserts boilerplate code for stateless biology modules #define BDM_STATELESS_BM_HEADER(class_name, base_class, class_version_id) \ public: \ /** Empty default event constructor, because module does not have state. */ \ class_name(const Event& event, BaseBiologyModule* other, \ uint64_t new_oid = 0) \ : base_class(event, other, new_oid) {} \ \ /** Event handler not needed, because this module does not have state. */ \ \ /** Create a new instance of this object using the default constructor. */ \ BaseBiologyModule* GetInstance(const Event& event, BaseBiologyModule* other, \ uint64_t new_oid = 0) const override { \ return new class_name(event, other, new_oid); \ } \ \ /** Create a copy of this biology module. */ \ BaseBiologyModule* GetCopy() const override { \ return new class_name(*this); \ } \ \ private: \ BDM_CLASS_DEF_OVERRIDE(class_name, class_version_id); } // namespace bdm #endif // CORE_BIOLOGY_MODULE_BIOLOGY_MODULE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_BIOLOGY_MODULE_GROW_DIVIDE_H_ #define CORE_BIOLOGY_MODULE_GROW_DIVIDE_H_ #include "core/biology_module/biology_module.h" #include "core/event/cell_division_event.h" #include "core/sim_object/cell.h" #include "core/util/log.h" #include "core/util/root.h" namespace bdm { /// This biology module grows the simulation object until the diameter reaches /// the specified threshold and divides the object afterwards. struct GrowDivide : public BaseBiologyModule { GrowDivide() : BaseBiologyModule(gAllEventIds) {} GrowDivide(double threshold, double growth_rate, std::initializer_list event_list) : BaseBiologyModule(event_list), threshold_(threshold), growth_rate_(growth_rate) {} GrowDivide(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) : BaseBiologyModule(event, other, new_oid) { if (GrowDivide* gdbm = dynamic_cast(other)) { threshold_ = gdbm->threshold_; growth_rate_ = gdbm->growth_rate_; } else { Log::Fatal("GrowDivide::EventConstructor", "other was not of type GrowDivide"); } } /// Create a new instance of this object using the default constructor. BaseBiologyModule* GetInstance(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) const override { return new GrowDivide(event, other, new_oid); } /// Create a copy of this biology module. BaseBiologyModule* GetCopy() const override { return new GrowDivide(*this); } /// Default event handler (exising biology module won't be modified on /// any event) void EventHandler(const Event& event, BaseBiologyModule* other1, BaseBiologyModule* other2 = nullptr) override { BaseBiologyModule::EventHandler(event, other1, other2); } void Run(SimObject* so) override { if (Cell* cell = dynamic_cast(so)) { if (cell->GetDiameter() <= threshold_) { cell->ChangeVolume(growth_rate_); } else { cell->Divide(); } } else { Log::Fatal("GrowDivide::Run", "SimObject is not a Cell"); } } private: BDM_CLASS_DEF_OVERRIDE(GrowDivide, 1); double threshold_ = 40; double growth_rate_ = 300; }; } // namespace bdm #endif // CORE_BIOLOGY_MODULE_GROW_DIVIDE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_BIOLOGY_MODULE_REGULATE_GENES_H_ #define CORE_BIOLOGY_MODULE_REGULATE_GENES_H_ #include #include #include "core/biology_module/biology_module.h" #include "core/param/param.h" #include "core/scheduler.h" #include "core/simulation.h" #include "core/util/root.h" namespace bdm { /// This module simulates expression of genes and contains all required /// additional variables for tracking of the concentration of proteins. /// Thus, it can work with any type of simulation object. /// It has the implementation of Euler and Runge-Kutta numerical methods /// for solving ODE. Both methods implemented inside the body of method Run(). /// The user determines which method is picked in particular simulation /// through variable `Param::numerical_ode_solver_`. struct RegulateGenes : public BaseBiologyModule { RegulateGenes() : BaseBiologyModule(gAllEventIds) {} explicit RegulateGenes(EventId event) : BaseBiologyModule(event) {} RegulateGenes(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) : BaseBiologyModule(event, other, new_oid) { if (RegulateGenes* rgbm = dynamic_cast(other)) { concentrations_ = rgbm->concentrations_; first_derivatives_ = rgbm->first_derivatives_; } else { Log::Fatal("RegulateGenes::EventConstructor", "other was not of type RegulateGenes"); } } /// Create a new instance of this object using the default constructor. BaseBiologyModule* GetInstance(const Event& event, BaseBiologyModule* other, uint64_t new_oid = 0) const override { return new RegulateGenes(event, other, new_oid); } /// Create a copy of this biology module. BaseBiologyModule* GetCopy() const override { return new RegulateGenes(*this); } /// Empty default event handler. void EventHandler(const Event& event, BaseBiologyModule* other1, BaseBiologyModule* other2 = nullptr) override { BaseBiologyModule::EventHandler(event, other1, other2); } /// AddGene adds a new differential equation. /// \param first_derivative differential equation in the form: /// `slope = f(time, last_concentration)` -- e.g.: /// /// [](double time, double last_concentration) { /// return 1 - time * last_concentration; /// } /// /// \param initial_concentration void AddGene(std::function first_derivative, double initial_concentration) { first_derivatives_.push_back(first_derivative); concentrations_.push_back(initial_concentration); } const std::vector& GetConcentrations() const { return concentrations_; } /// Method Run() contains the implementation for Runge-Khutta and Euler /// methods for solving ODE. void Run(SimObject* sim_object) override { auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); auto* scheduler = sim->GetScheduler(); const auto& timestep = param->simulation_time_step_; uint64_t simulated_steps = scheduler->GetSimulatedSteps(); const auto absolute_time = simulated_steps * timestep; if (param->numerical_ode_solver_ == Param::NumericalODESolver::kEuler) { // Euler for (uint64_t i = 0; i < first_derivatives_.size(); i++) { double slope = first_derivatives_[i](absolute_time, concentrations_[i]); concentrations_[i] += slope * timestep; } } else if (param->numerical_ode_solver_ == Param::NumericalODESolver::kRK4) { // Runge-Kutta 4 for (uint64_t i = 0; i < first_derivatives_.size(); i++) { double interval_midpoint = absolute_time + timestep / 2.0; double interval_endpoint = absolute_time + timestep; double k1 = first_derivatives_[i](absolute_time, concentrations_[i]); double k2 = first_derivatives_[i]( interval_midpoint, concentrations_[i] + timestep * k1 / 2.0); double k3 = first_derivatives_[i]( interval_midpoint, concentrations_[i] + timestep * k2 / 2.0); double k4 = first_derivatives_[i](interval_endpoint, concentrations_[i] + timestep * k3); concentrations_[i] += timestep / 6.0 * (k1 + 2 * k2 + 2 * k3 + k4); } } } private: /// Store the current concentration for each gene std::vector concentrations_ = {}; /// Store the gene differential equations, which define how the concentration /// change. /// New functions can be added through method AddGene() std::vector> first_derivatives_ = {}; BDM_CLASS_DEF_OVERRIDE(RegulateGenes, 1); }; } // namespace bdm #endif // CORE_BIOLOGY_MODULE_REGULATE_GENES_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_CONTAINER_FIXED_SIZE_VECTOR_H_ #define CORE_CONTAINER_FIXED_SIZE_VECTOR_H_ #include #include namespace bdm { /// Vector with fixed number of elements == Array with push_back function that /// keeps track of its size /// NB: No bounds checking. Do not push_back more often than the number of /// maximum elements given by the template parameter N template class FixedSizeVector { public: size_t size() const { return size_; } // NOLINT const T& operator[](size_t idx) const { return data_[idx]; } T& operator[](size_t idx) { return data_[idx]; } FixedSizeVector& operator++() { #pragma omp simd for (size_t i = 0; i < N; i++) { ++data_[i]; } return *this; } void clear() { size_ = 0; } // NOLINT void push_back(const T& value) { // NOLINT assert(size_ < N); data_[size_++] = value; } const T* begin() const { return &(data_[0]); } // NOLINT const T* end() const { return &(data_[size_]); } // NOLINT T* begin() { return &(data_[0]); } // NOLINT T* end() { return &(data_[size_]); } // NOLINT private: T data_[N]; std::size_t size_ = 0; }; } // namespace bdm #endif // CORE_CONTAINER_FIXED_SIZE_VECTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_CONTAINER_INLINE_VECTOR_H_ #define CORE_CONTAINER_INLINE_VECTOR_H_ #include #include #include #include #include #include #include "core/util/log.h" #include "core/util/root.h" namespace bdm { /// This containes stores up to N elements without heap allocations /// If further elements are added elements are stored on the heap /// Container grows in a geometric sequence /// Elements are contiguous in memory exept the transition from internal /// to heap allocated memory (between index N-1 and N) /// This container is optimized for minimal overhead. Therefore, it can only /// store 65535 elements. template class InlineVector { public: template struct Iterator { uint16_t index_ = 0; TIV* vector_; Iterator(uint16_t index, TIV* iv) : index_(index), vector_(iv) {} Iterator(const Iterator& other) : index_(other.index_), vector_(other.vector_) {} TT& operator*() const { return (*vector_)[index_]; } Iterator& operator=(const Iterator& other) { if (this != &other) { index_ = other.index_; vector_ = other.vector_; } return *this; } bool operator==(const Iterator& other) const { if (other.index_ != index_ || other.vector_ != vector_) { return false; } return true; } bool operator!=(const Iterator& other) const { return !this->operator==(other); } Iterator& operator++() { ++index_; return *this; } Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; } Iterator& operator--() { --index_; return *this; } Iterator operator--(int) { Iterator tmp(*this); operator--(); return tmp; } Iterator& operator+=(const Iterator& rhs) { assert(vector_ == rhs.vector_); index_ += rhs.index_; return *this; } Iterator operator+(const Iterator& rhs) { assert(vector_ == rhs.vector_); Iterator tmp(*this); tmp.index_ += rhs.index_; return tmp; } Iterator& operator+=(uint16_t i) { index_ += i; return *this; } Iterator operator+(uint16_t i) { Iterator tmp(*this); tmp.index_ += i; return tmp; } Iterator& operator-=(const Iterator& rhs) { assert(vector_ == rhs.vector_); index_ -= rhs.index_; return *this; } Iterator operator-(const Iterator& rhs) { assert(vector_ == rhs.vector_); Iterator tmp(*this); tmp.index_ -= rhs.index_; return tmp; } Iterator& operator-=(uint16_t i) { index_ -= i; return *this; } Iterator operator-(uint16_t i) { Iterator tmp(*this); tmp.index_ -= i; return tmp; } }; using value_type = T; using iterator = typename InlineVector::template Iterator; using const_iterator = typename InlineVector::template Iterator; explicit InlineVector(TRootIOCtor* io_ctor) {} // Constructor for ROOT I/O InlineVector() {} InlineVector(const InlineVector& other) { data_ = std::move(other.data_); size_ = other.size_; heap_capacity_ = other.heap_capacity_; if (other.heap_data_ != nullptr) { heap_data_ = new T[heap_capacity_]; std::copy_n(other.heap_data_, HeapSize(), heap_data_); } } InlineVector(InlineVector&& other) noexcept { data_ = other.data_; size_ = other.size_; heap_capacity_ = other.heap_capacity_; heap_data_ = other.heap_data_; other.heap_data_ = nullptr; } virtual ~InlineVector() { if (heap_data_ != nullptr) { heap_capacity_ = 0; delete[] heap_data_; } } /// Returns the number of elements that the container has currently /// allocated space for. uint16_t capacity() const { return N + heap_capacity_; } // NOLINT /// Removes all elements from the container. /// Leaves capacity() unchanged. void clear() { // NOLINT size_ = 0; } /// Increase the capacity of the container to a value that's greater or equal /// to new_capacity. If new_cap is greater than the current `capacity()`, /// new storage is allocated, otherwise the method does nothing. /// /// If new_cap is greater than `capacity()`, all iterators and references, /// including the past-the-end iterator, are invalidated. /// Otherwise, no iterators or references are invalidated. void reserve(uint16_t new_capacity) { // NOLINT if (new_capacity > capacity()) { heap_capacity_ = new_capacity - N; T* new_heap_data = new T[heap_capacity_]; if (heap_data_ != nullptr) { std::copy_n(heap_data_, HeapSize(), new_heap_data); delete[] heap_data_; } heap_data_ = new_heap_data; } } /// \brief returns the number of elements in this container uint16_t size() const { return size_; } // NOLINT /// adds elements to this container and allocates additional memory on the /// heap if required void push_back(const T& element) { // NOLINT if (size_ < N) { data_[size_++] = element; } else { // allocate heap memory assert(size_ != std::numeric_limits::max() && "Maxium number of elements exceeded"); if (size_ == capacity()) { uint64_t tmp = static_cast(capacity()) * kGrowFactor; uint16_t new_capacity = static_cast(tmp); if (tmp > std::numeric_limits::max()) { new_capacity = std::numeric_limits::max(); } reserve(new_capacity); } heap_data_[HeapSize()] = element; size_++; } } iterator erase(const iterator& it) { auto idx = it.index_; if (idx >= size_) { Log::Fatal( "InlineVector::erase", "You tried to erase an element that is outside the InlineVector."); return it; } else if (idx == size_ - 1) { // last element size_--; return end(); } // element in the middle for (uint16_t i = it.index_; i < size_ - 1; i++) { (*this)[i] = (*this)[i + 1]; } size_--; return iterator(idx, this); } std::vector make_std_vector() const { // NOLINT std::vector std_vector(size_); for (uint16_t i = 0; i < size_; i++) { std_vector[i] = (*this)[i]; } return std_vector; } InlineVector& operator=(const InlineVector& other) { if (this != &other) { data_ = other.data_; size_ = other.size_; heap_capacity_ = other.heap_capacity_; if (other.heap_data_ != nullptr) { if (heap_data_ != nullptr) { delete[] heap_data_; } heap_data_ = new T[heap_capacity_]; if (size_ > N) { std::copy_n(other.heap_data_, HeapSize(), heap_data_); } } } return *this; } InlineVector& operator=(InlineVector&& other) { if (this != &other) { data_ = std::move(other.data_); size_ = other.size_; heap_capacity_ = other.heap_capacity_; heap_data_ = other.heap_data_; other.heap_data_ = nullptr; other.heap_capacity_ = 0; } return *this; } T& operator[](uint16_t index) { if (index < N) { return data_[index]; } else { return heap_data_[index - N]; } } const T& operator[](uint16_t index) const { if (index < N) { return data_[index]; } else { return heap_data_[index - N]; } } bool operator==(const InlineVector& other) const { if (size_ != other.size_) { return false; } // inline data for (uint16_t i = 0; i < std::min(size_, N); i++) { if (data_[i] != other.data_[i]) { return false; } } // heap data if (size_ > N) { for (uint16_t i = 0; i < HeapSize(); i++) { if (heap_data_[i] != other.heap_data_[i]) { return false; } } } return true; } friend std::ostream& operator<<(std::ostream& out, const InlineVector& other) { for (uint16_t i = 0; i < other.size_; i++) { out << other[i] << ", "; } return out; } iterator begin() { return iterator(0, this); } iterator end() { return iterator(size_, this); } const_iterator begin() const { return const_iterator(0, this); } const_iterator end() const { return const_iterator(size_, this); } private: static constexpr float kGrowFactor = 1.5; std::array data_; uint16_t size_ = 0; UInt_t heap_capacity_ = 0; // needed to help ROOT with array size T* heap_data_ = nullptr; //[heap_capacity_] // NOLINT uint16_t HeapSize() const { if (size_ < N) { return 0; } return size_ - N; } BDM_TEMPLATE_CLASS_DEF(InlineVector, 1); // NOLINT }; } // namespace bdm #endif // CORE_CONTAINER_INLINE_VECTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_CONTAINER_MATH_ARRAY_H_ #define CORE_CONTAINER_MATH_ARRAY_H_ #include #include #include #include #include #include #include #include "core/util/root.h" namespace bdm { /// Array with a fixed number of elements. It implements the same behaviour /// of the standard `std::array` container. However, it provides also /// several custom mathematical operations (e.g. Sum(), Norm() etc.). template class MathArray { // NOLINT public: /// Default constructor constexpr MathArray() {} /// Constructor which accepts an std::initiliazer_list to set /// the array's content. /// \param l an initializer list constexpr MathArray(std::initializer_list l) { assert(l.size() == N); auto it = l.begin(); for (uint64_t i = 0; i < N; i++) { data_[i] = *(it++); } } /// Return a pointer to the underlying data. /// \return cont T pointer to the first entry of the array. inline const T* data() const { return &data_[0]; } // NOLINT /// Return the size of the array. /// \return integer denoting the array's size. inline const size_t size() const { return N; } // NOLINT /// Check if the array is empty. /// \return true if size() == 0, false otherwise. inline const bool empty() const { return N == 0; } // NOLINT /// Overloaded array subscript operator. It does not perform /// any boundary checks. /// \param idx element's index. /// \return the requested element. T& operator[](size_t idx) { return data_[idx]; } /// Const overloaded array subscript operator. /// \param idx element's index. /// \return the requested element. const T& operator[](size_t idx) const { return data_[idx]; } /// Returns the element at the given position. It will throw /// an std::out_of_range exception if the given index is out /// of the array's boundaries. /// \param idx the index of the element. /// \return the requested element. T& at(size_t idx) noexcept(false) { // NOLINT if (idx > size() || idx < 0) { throw std::out_of_range("The index is out of range"); } return data_[idx]; } const T* begin() const { return &(data_[0]); } // NOLINT const T* end() const { return &(data_[N]); } // NOLINT T* begin() { return &(data_[0]); } // NOLINT T* end() { return &(data_[N]); } // NOLINT /// Returns the element at the beginning of the array. /// \return first element. T& front() { return *(this->begin()); } // NOLINT /// Return the element at the end of the array. /// \return last element. T& back() { // NOLINT auto tmp = this->end(); tmp--; return *tmp; } /// Assignment operator. /// \param other the other MathArray instance. /// \return the current MathArray. MathArray& operator=(const MathArray& other) { if (this != &other) { assert(other.size() == N); std::copy(other.data_, other.data_ + other.size(), data_); } return *this; } /// Equality operator. /// \param other a MathArray instance. /// \return true if they have the same content, false otherwise. bool operator==(const MathArray& other) const { if (other.size() != N) { return false; } for (size_t i = 0; i < N; i++) { if (other[i] != data_[i]) { return false; } } return true; } MathArray& operator++() { #pragma omp simd for (size_t i = 0; i < N; i++) { ++data_[i]; } return *this; } MathArray operator++(int) { MathArray tmp(*this); operator++(); return tmp; } MathArray& operator--() { #pragma omp simd for (size_t i = 0; i < N; i++) { --data_[i]; } return *this; } MathArray operator--(int) { MathArray tmp(*this); operator--(); return tmp; } MathArray& operator+=(const MathArray& rhs) { assert(N == rhs.size()); #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] += rhs[i]; } return *this; } MathArray operator+(const MathArray& rhs) { assert(size() == rhs.size()); MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] + rhs[i]; } return tmp; } const MathArray operator+(const MathArray& rhs) const { assert(size() == rhs.size()); MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] + rhs[i]; } return tmp; } MathArray& operator+=(const T& rhs) { #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] += rhs; } return *this; } MathArray operator+(const T& rhs) { MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] + rhs; } return tmp; } MathArray& operator-=(const MathArray& rhs) { assert(size() == rhs.size()); #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] -= rhs[i]; } return *this; } MathArray operator-(const MathArray& rhs) { assert(size() == rhs.size()); MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] - rhs[i]; } return tmp; } const MathArray operator-(const MathArray& rhs) const { assert(size() == rhs.size()); MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] - rhs[i]; } return tmp; } MathArray& operator-=(const T& rhs) { #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] -= rhs; } return *this; } MathArray operator-(const T& rhs) { MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] - rhs; } return tmp; } T& operator*=(const MathArray& rhs) = delete; T operator*(const MathArray& rhs) { assert(size() == rhs.size()); T result = 0; #pragma omp simd for (size_t i = 0; i < N; i++) { result += data_[i] * rhs[i]; } return result; } const T operator*(const MathArray& rhs) const { assert(size() == rhs.size()); T result = 0; #pragma omp simd for (size_t i = 0; i < N; i++) { result += data_[i] * rhs[i]; } return result; } MathArray& operator*=(const T& k) { #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] *= k; } return *this; } MathArray operator*(const T& k) { MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] * k; } return tmp; } const MathArray operator*(const T& k) const { MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] * k; } return tmp; } MathArray& operator/=(const T& k) { #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] /= k; } return *this; } MathArray operator/(const T& k) { MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; i++) { tmp[i] = data_[i] / k; } return tmp; } /// Fill the MathArray with a constant value. /// \param k the constant value /// \return the array MathArray& fill(const T& k) { // NOLINT std::fill(std::begin(data_), std::end(data_), k); return *this; } /// Return the sum of all the array's elements. /// \return sum of the array's content. T Sum() const { return std::accumulate(begin(), end(), 0); } /// Compute the norm of the array's content. /// \return array's norm. T Norm() const { T result = 0; #pragma omp simd for (size_t i = 0; i < N; i++) { result += data_[i] * data_[i]; } result = std::sqrt(result); return result == 0 ? 1.0 : result; } /// Normalize the array. It will be done in-place. /// \return the normalized array. MathArray& Normalize() { T norm = Norm(); #pragma omp simd for (size_t i = 0; i < N; i++) { data_[i] /= norm; } return *this; } /// Compute the entry wise product given another array /// of the same size. /// \param rhs the other array /// \return a new array with the result MathArray EntryWiseProduct(const MathArray& rhs) { assert(rhs.size() == N); MathArray tmp; #pragma omp simd for (size_t i = 0; i < N; ++i) { tmp[i] = data_[i] * rhs[i]; } return tmp; } private: T data_[N]; BDM_CLASS_DEF_NV(MathArray, 1); // NOLINT }; template std::ostream& operator<<(std::ostream& o, const MathArray& arr) { for (size_t i = 0; i < N; i++) { o << arr[i]; if (i != N - 1) { o << ", "; } } return o; } /// Alias for a size 3 MathArray using Double3 = MathArray; /// Alias for a size 4 MathArray using Double4 = MathArray; } // namespace bdm #endif // CORE_CONTAINER_MATH_ARRAY_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_CONTAINER_PARALLEL_RESIZE_VECTOR_H_ #define CORE_CONTAINER_PARALLEL_RESIZE_VECTOR_H_ #include #include namespace bdm { /// \brief std::vector with parallel resize template class ParallelResizeVector { public: using iterator = T*; using const_iterator = const T*; using value_type = T; ParallelResizeVector() {} ParallelResizeVector(std::initializer_list init) { reserve(init.size()); for (auto& el : init) { push_back(el); } } ParallelResizeVector(const ParallelResizeVector& other) { if (other.data_ != nullptr && other.capacity_ != 0) { reserve(other.capacity_); // initialize using copy ctor #pragma omp parallel for for (std::size_t i = 0; i < other.size_; i++) { new (&(data_[i])) T(other.data_[i]); } } size_ = other.size_; capacity_ = other.capacity_; } ~ParallelResizeVector() { if (data_ != nullptr) { #pragma omp parallel for for (std::size_t i = 0; i < size_; i++) { data_[i].~T(); } capacity_ = 0; free(data_); data_ = nullptr; } } std::size_t size() const { return size_; } // NOLINT T* data() noexcept { return data_; } // NOLINT const T* data() const noexcept { return data_; } // NOLINT void swap(ParallelResizeVector& other) { // NOLINT // size_ size_ ^= other.size_; other.size_ ^= size_; size_ ^= other.size_; // capacity_ capacity_ ^= other.capacity_; other.capacity_ ^= capacity_; capacity_ ^= other.capacity_; // data_ auto* tmp = data_; data_ = other.data_; other.data_ = tmp; } std::size_t capacity() const { return capacity_; } // NOLINT void push_back(const T& element) { // NOLINT if (capacity_ == size_) { reserve(capacity_ * kGrowFactor); } new (&(data_[size_++])) T(element); } void reserve(std::size_t new_capacity) { // NOLINT if (new_capacity > capacity_) { T* new_data = static_cast(malloc(new_capacity * sizeof(T))); if (data_ != nullptr) { // initialize using copy ctor #pragma omp parallel for for (std::size_t i = 0; i < size_; i++) { new (&(new_data[i])) T(data_[i]); } // destruct old elements #pragma omp parallel for for (std::size_t i = 0; i < size_; i++) { data_[i].~T(); } free(data_); } data_ = new_data; capacity_ = new_capacity; } } void resize(std::size_t new_size, const T& t = T()) { // NOLINT if (capacity_ < new_size) { reserve(new_size); } // grow #pragma omp parallel for for (std::size_t i = size_; i < new_size; i++) { new (&(data_[i])) T(t); } // shrink #pragma omp parallel for for (std::size_t i = new_size; i < size_; i++) { data_[i].~T(); } size_ = new_size; } void clear() { // NOLINT for (std::size_t i = 0; i < size_; i++) { data_[i].~T(); } size_ = 0; } ParallelResizeVector& operator=(const ParallelResizeVector& other) { free(data_); data_ = nullptr; reserve(other.capacity_); size_ = other.size_; capacity_ = other.capacity_; #pragma omp parallel for for (std::size_t i = 0; i < size_; i++) { data_[i] = other.data_[i]; } return *this; } T& operator[](std::size_t index) { return data_[index]; } const T& operator[](std::size_t index) const { return data_[index]; } iterator begin() { return &(data_[0]); } // NOLINT iterator end() { return &(data_[size_]); } // NOLINT const_iterator cbegin() { return &(data_[0]); } // NOLINT const_iterator cend() { return &(data_[size_]); } // NOLINT private: static constexpr float kGrowFactor = 1.5; std::size_t size_ = 0; std::size_t capacity_ = 0; T* data_ = nullptr; }; } // namespace bdm #endif // CORE_CONTAINER_PARALLEL_RESIZE_VECTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_CONTAINER_SIM_OBJECT_VECTOR_H_ #define CORE_CONTAINER_SIM_OBJECT_VECTOR_H_ #include #include "core/resource_manager.h" // SoHandle #include "core/simulation.h" namespace bdm { /// Two dimensional vector. Holds one element of type `T` for each simulation /// object in the simulation. template class SimObjectVector { public: /// NB: Elements will not be initilized. SimObjectVector() { data_.resize(thread_info_->GetNumaNodes()); size_.resize(thread_info_->GetNumaNodes()); reserve(); } /// Reserves enough memory to hold N instances of type T (N being the number /// of simulation objects in the simulation). /// e.g. ResourceManager has two types `A` and `B`. `A` has 10 elements /// and `B` 20. `data_[0]` corresponds to `A` and reserves 10 elements, /// while `data_[1]` corresponds to `B` and reserves 20 elements. /// NB: Elements will not be initilized. void reserve() { // NOLINT clear(); auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); for (int n = 0; n < thread_info_->GetNumaNodes(); n++) { auto num_sos = rm->GetNumSimObjects(n); if (data_[n].capacity() < num_sos) { data_[n].reserve(num_sos * 1.5); } size_[n] = num_sos; } } void clear() { // NOLINT for (auto& el : size_) { el = 0; } for (auto& vec : data_) { vec.clear(); } } // Returns the number of elements of specified type size_t size(uint16_t numa_node) { return size_[numa_node]; } // NOLINT const T& operator[](const SoHandle& handle) const { return data_[handle.GetNumaNode()][handle.GetElementIdx()]; } T& operator[](const SoHandle& handle) { return data_[handle.GetNumaNode()][handle.GetElementIdx()]; } private: /// one std::vector for each numa node std::vector> data_; std::vector size_; ThreadInfo* thread_info_ = ThreadInfo::GetInstance(); }; } // namespace bdm #endif // CORE_CONTAINER_SIM_OBJECT_VECTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_DEFAULT_FORCE_H_ #define CORE_DEFAULT_FORCE_H_ #include #include "core/container/math_array.h" namespace bdm { class SimObject; class DefaultForce { public: DefaultForce() {} ~DefaultForce() {} DefaultForce(const DefaultForce&) = delete; DefaultForce& operator=(const DefaultForce&) = delete; Double4 GetForce(const SimObject* lhs, const SimObject* rhs); private: void ForceBetweenSpheres(const SimObject* sphere_lhs, const SimObject* sphere_rhs, Double3* result) const; void ForceOnACylinderFromASphere(const SimObject* cylinder, const SimObject* sphere, Double4* result) const; void ForceOnASphereFromACylinder(const SimObject* sphere, const SimObject* cylinder, Double3* result) const; void ForceBetweenCylinders(const SimObject* cylinder1, const SimObject* cylinder2, Double4* result) const; Double4 ComputeForceOfASphereOnASphere(const Double3& c1, double r1, const Double3& c2, double r2) const; }; } // namespace bdm #endif // CORE_DEFAULT_FORCE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_DIFFUSION_GRID_H_ #define CORE_DIFFUSION_GRID_H_ #include #include #include #include #include #include #include #include #include "core/util/root.h" #include "core/container/math_array.h" #include "core/container/parallel_resize_vector.h" #include "core/param/param.h" #include "core/util/log.h" #include "core/util/math.h" namespace bdm { /// A class that computes the diffusion of extracellular substances /// It maintains the concentration and gradient of a single substance class DiffusionGrid { public: explicit DiffusionGrid(TRootIOCtor* p) {} DiffusionGrid(int substance_id, std::string substance_name, double dc, double mu, int resolution = 11) : substance_(substance_id), substance_name_(substance_name), dc_({{1 - dc, dc / 6, dc / 6, dc / 6, dc / 6, dc / 6, dc / 6}}), mu_(mu), resolution_(resolution) {} virtual ~DiffusionGrid() {} /// @brief Initializes the grid by calculating the grid dimensions /// and number of boxes along the axis from the input arguments /// /// @param[in] grid_dimensions The grid dimensions /// @param[in] box_length The box length /// void Initialize(const std::array& grid_dimensions) { // Get grid properties from neighbor grid grid_dimensions_ = grid_dimensions; assert(resolution_ > 0 && "The resolution cannot be zero!"); num_boxes_axis_[0] = resolution_; num_boxes_axis_[1] = resolution_; num_boxes_axis_[2] = resolution_; // Example: diffusion grid dimensions from 0-40 and resolution // of 4. Resolution must be adjusted otherwise one data pointer will be // missing. // Without adjustment: // box_length_: 10 // data points {0, 10, 20, 30} - 40 will be misssing! // With adjustment // box_length_: 13.3 // data points: {0, 13.3, 26.6, 39.9} box_length_ = (grid_dimensions_[1] - grid_dimensions_[0]) / static_cast(resolution_ - 1); ParametersCheck(); box_volume_ = box_length_ * box_length_ * box_length_; assert(box_length_ > 0 && "Box length of diffusion grid must be greater than zero!"); // Set the parity of the number of boxes along the dimensions (since all // dimensions are the same, we just take the x-axis here) parity_ = num_boxes_axis_[0] % 2; total_num_boxes_ = num_boxes_axis_[0] * num_boxes_axis_[1] * num_boxes_axis_[2]; // Allocate memory for the concentration and gradient arrays c1_.resize(total_num_boxes_); c2_.resize(total_num_boxes_); gradients_.resize(3 * total_num_boxes_); initialized_ = true; } void ParametersCheck() { // The 1.0 is to impose floating point operations if ((1.0 * (1 - dc_[0]) * dt_) / (1.0 * box_length_ * box_length_) >= (1.0 / 6)) { Log::Fatal( "DiffusionGrid", "The specified parameters of the diffusion grid with substance [", substance_name_, "] will result in unphysical behavior (diffusion coefficient = ", (1 - dc_[0]), ", resolution = ", resolution_, "). Please refer to the user guide for more information."); } } void RunInitializers() { assert(num_boxes_axis_[0] > 0 && "The number of boxes along an axis was found to be zero!"); if (initializers_.empty()) { return; } auto nx = num_boxes_axis_[0]; auto ny = num_boxes_axis_[1]; auto nz = num_boxes_axis_[2]; // Apply all functions that initialize this diffusion grid for (size_t f = 0; f < initializers_.size(); f++) { for (size_t x = 0; x < nx; x++) { double real_x = grid_dimensions_[0] + x * box_length_; for (size_t y = 0; y < ny; y++) { double real_y = grid_dimensions_[2] + y * box_length_; for (size_t z = 0; z < nz; z++) { double real_z = grid_dimensions_[4] + z * box_length_; std::array box_coord; box_coord[0] = x; box_coord[1] = y; box_coord[2] = z; size_t idx = GetBoxIndex(box_coord); IncreaseConcentrationBy(idx, initializers_[f](real_x, real_y, real_z)); } } } } // Clear the initializer to free up space initializers_.clear(); initializers_.shrink_to_fit(); } /// @brief Updates the grid dimensions, based on the given threshold /// values. The diffusion grid dimensions need always be larger /// than the neighbor grid dimensions, so that each simulation /// object can obtain its local concentration / gradient /// /// @param[in] threshold_dimensions The threshold values /// void Update(const std::array& threshold_dimensions) { // Update the grid dimensions such that each dimension ranges from // {treshold_dimensions[0] - treshold_dimensions[1]} auto min_gd = threshold_dimensions[0]; auto max_gd = threshold_dimensions[1]; grid_dimensions_ = {min_gd, max_gd, min_gd, max_gd, min_gd, max_gd}; // If the grid is not perfectly divisible along each dimension by the // box length, extend the grid so that it is int dimension_length = max_gd - min_gd; for (int i = 0; i < 3; i++) { int r = fmod(dimension_length, box_length_); if (r > 1e-9) { // std::abs for the case that box_length_ > dimension_length grid_dimensions_[2 * i + 1] += (box_length_ - r); } } // Calculate by how many boxes each dimension has grown int new_dimension_length = grid_dimensions_[1] - grid_dimensions_[0]; int new_num_boxes = std::ceil(new_dimension_length / box_length_); int growth = new_num_boxes - num_boxes_axis_[0]; if (growth > 0) { // Store the old number of boxes along each axis for comparison std::array tmp_num_boxes_axis = num_boxes_axis_; // Increase number of boxes along axis accordingly num_boxes_axis_[0] += growth; num_boxes_axis_[1] += growth; num_boxes_axis_[2] += growth; // We need to maintain the parity of the number of boxes along each // dimension, otherwise copying of the substances to the increases grid // will not be symmetrically done; resulting in shifting of boxes // We add a box in the negative direction, because the only way the parity // could have changed is because of adding a box in the positive direction // (due to the grid not being perfectly divisible; see above) if (num_boxes_axis_[0] % 2 != parity_) { for (int i = 0; i < 3; i++) { grid_dimensions_[2 * i] -= box_length_; num_boxes_axis_[i]++; } } // Temporarily save previous grid data auto tmp_c1 = c1_; auto tmp_gradients = gradients_; c1_.clear(); c2_.clear(); gradients_.clear(); total_num_boxes_ = num_boxes_axis_[0] * num_boxes_axis_[1] * num_boxes_axis_[2]; CopyOldData(tmp_c1, tmp_gradients, tmp_num_boxes_axis); assert(total_num_boxes_ >= tmp_num_boxes_axis[0] * tmp_num_boxes_axis[1] * tmp_num_boxes_axis[2] && "The diffusion grid tried to shrink! It can only become larger"); } } /// Copies the concentration and gradients values to the new /// (larger) grid. In the 2D case it looks like the following: /// /// [0 0 0 0] /// [v1 v2] --> [0 v1 v2 0] /// [v3 v4] --> [0 v3 v4 0] /// [0 0 0 0] /// /// The dimensions are doubled in this case from 2x2 to 4x4 /// If the dimensions would be increased from 2x2 to 3x3, it will still /// be increased to 4x4 in order for GetBoxIndex to function correctly /// void CopyOldData(const ParallelResizeVector& old_c1, const ParallelResizeVector& old_gradients, const std::array& old_num_boxes_axis) { // Allocate more memory for the grid data arrays c1_.resize(total_num_boxes_); c2_.resize(total_num_boxes_); gradients_.resize(3 * total_num_boxes_); auto incr_dim_x = num_boxes_axis_[0] - old_num_boxes_axis[0]; auto incr_dim_y = num_boxes_axis_[1] - old_num_boxes_axis[1]; auto incr_dim_z = num_boxes_axis_[2] - old_num_boxes_axis[2]; int off_x = incr_dim_x / 2; int off_y = incr_dim_y / 2; int off_z = incr_dim_z / 2; int num_box_xy = num_boxes_axis_[0] * num_boxes_axis_[1]; int old_box_xy = old_num_boxes_axis[0] * old_num_boxes_axis[1]; int new_origin = off_z * (num_boxes_axis_[0] * num_boxes_axis_[1]) + off_y * num_boxes_axis_[0] + off_x; for (size_t k = 0; k < old_num_boxes_axis[2]; k++) { int offset = new_origin + k * num_box_xy; for (size_t j = 0; j < old_num_boxes_axis[1]; j++) { if (j != 0) { offset += num_boxes_axis_[0]; } for (size_t i = 0; i < old_num_boxes_axis[0]; i++) { auto idx = k * old_box_xy + j * old_num_boxes_axis[0] + i; c1_[offset + i] = old_c1[idx]; gradients_[3 * (offset + i)] = old_gradients[3 * idx]; gradients_[3 * (offset + i) + 1] = old_gradients[3 * idx + 1]; gradients_[3 * (offset + i) + 2] = old_gradients[3 * idx + 2]; } } } } /// Solves a 5-point stencil diffusion equation, with leaking-edge /// boundary conditions. Substances are allowed to leave the simulation /// space. This prevents building up concentration at the edges /// void DiffuseWithLeakingEdge() { int nx = num_boxes_axis_[0]; int ny = num_boxes_axis_[1]; int nz = num_boxes_axis_[2]; #define YBF 16 #pragma omp parallel for collapse(2) for (int yy = 0; yy < ny; yy += YBF) { for (int z = 0; z < nz; z++) { // To let the edges bleed we set some diffusion coefficients // to zero. This prevents substance building up at the edges auto dc_2_ = dc_; int ymax = yy + YBF; if (ymax >= ny) { ymax = ny; } for (int y = yy; y < ymax; y++) { dc_2_ = dc_; int x; int c, n, s, b, t; x = 0; c = x + y * nx + z * nx * ny; if (y == 0) { n = c; dc_2_[4] = 0; } else { n = c - nx; } if (y == (ny - 1)) { s = c; dc_2_[3] = 0; } else { s = c + nx; } if (z == 0) { b = c; dc_2_[5] = 0; } else { b = c - nx * ny; } if (z == (nz - 1)) { t = c; dc_2_[6] = 0; } else { t = c + nx * ny; } // x = 0; we leak out substances past this edge (so multiply by 0) c2_[c] = (dc_2_[0] * c1_[c] + 0 * c1_[c] + dc_2_[2] * c1_[c + 1] + dc_2_[3] * c1_[s] + dc_2_[4] * c1_[n] + dc_2_[5] * c1_[b] + dc_2_[6] * c1_[t]) * (1 - mu_); #pragma omp simd for (x = 1; x < nx - 1; x++) { ++c; ++n; ++s; ++b; ++t; c2_[c] = (dc_2_[0] * c1_[c] + dc_2_[1] * c1_[c - 1] + dc_2_[2] * c1_[c + 1] + dc_2_[3] * c1_[s] + dc_2_[4] * c1_[n] + dc_2_[5] * c1_[b] + dc_2_[6] * c1_[t]) * (1 - mu_); } ++c; ++n; ++s; ++b; ++t; // x = nx-1; we leak out substances past this edge (so multiply by 0) c2_[c] = (dc_2_[0] * c1_[c] + dc_2_[1] * c1_[c - 1] + 0 * c1_[c] + dc_2_[3] * c1_[s] + dc_2_[4] * c1_[n] + dc_2_[5] * c1_[b] + dc_2_[6] * c1_[t]) * (1 - mu_); } // tile ny } // tile nz } // block ny c1_.swap(c2_); } /// Solves a 5-point stencil diffusion equation, with closed-edge /// boundary conditions. Substances are not allowed to leave the simulation /// space. Keep in mind that the concentration can build up at the edges /// void DiffuseWithClosedEdge() { auto nx = num_boxes_axis_[0]; auto ny = num_boxes_axis_[1]; auto nz = num_boxes_axis_[2]; #define YBF 16 #pragma omp parallel for collapse(2) for (size_t yy = 0; yy < ny; yy += YBF) { for (size_t z = 0; z < nz; z++) { size_t ymax = yy + YBF; if (ymax >= ny) { ymax = ny; } for (size_t y = yy; y < ymax; y++) { size_t x; int c, n, s, b, t; x = 0; c = x + y * nx + z * nx * ny; n = (y == 0) ? c : c - nx; s = (y == ny - 1) ? c : c + nx; b = (z == 0) ? c : c - nx * ny; t = (z == nz - 1) ? c : c + nx * ny; c2_[c] = (dc_[0] * c1_[c] + dc_[1] * c1_[c] + dc_[2] * c1_[c + 1] + dc_[3] * c1_[s] + dc_[4] * c1_[n] + dc_[5] * c1_[b] + dc_[6] * c1_[t]) * (1 - mu_); #pragma omp simd for (x = 1; x < nx - 1; x++) { ++c; ++n; ++s; ++b; ++t; c2_[c] = (dc_[0] * c1_[c] + dc_[1] * c1_[c - 1] + dc_[2] * c1_[c + 1] + dc_[3] * c1_[s] + dc_[4] * c1_[n] + dc_[5] * c1_[b] + dc_[6] * c1_[t]) * (1 - mu_); } ++c; ++n; ++s; ++b; ++t; c2_[c] = (dc_[0] * c1_[c] + dc_[1] * c1_[c - 1] + dc_[2] * c1_[c] + dc_[3] * c1_[s] + dc_[4] * c1_[n] + dc_[5] * c1_[b] + dc_[6] * c1_[t]) * (1 - mu_); } // tile ny } // tile nz } // block ny c1_.swap(c2_); } void DiffuseEuler() { // check if diffusion coefficient and decay constant are 0 // i.e. if we don't need to calculate diffusion update if (IsFixedSubstance()) { return; } const auto nx = num_boxes_axis_[0]; const auto ny = num_boxes_axis_[1]; const auto nz = num_boxes_axis_[2]; const double ibl2 = 1 / (box_length_ * box_length_); const double d = 1 - dc_[0]; #define YBF 16 #pragma omp parallel for collapse(2) for (size_t yy = 0; yy < ny; yy += YBF) { for (size_t z = 0; z < nz; z++) { size_t ymax = yy + YBF; if (ymax >= ny) { ymax = ny; } for (size_t y = yy; y < ymax; y++) { size_t x = 0; int c, n, s, b, t; c = x + y * nx + z * nx * ny; #pragma omp simd for (x = 1; x < nx - 1; x++) { ++c; ++n; ++s; ++b; ++t; if (y == 0 || y == (ny - 1) || z == 0 || z == (nz - 1)) { continue; } n = c - nx; s = c + nx; b = c - nx * ny; t = c + nx * ny; c2_[c] = (c1_[c] + d * dt_ * (c1_[c - 1] - 2 * c1_[c] + c1_[c + 1]) * ibl2 + d * dt_ * (c1_[s] - 2 * c1_[c] + c1_[n]) * ibl2 + d * dt_ * (c1_[b] - 2 * c1_[c] + c1_[t]) * ibl2) * (1 - mu_); } ++c; ++n; ++s; ++b; ++t; } // tile ny } // tile nz } // block ny c1_.swap(c2_); } void DiffuseEulerLeakingEdge() { // check if diffusion coefficient and decay constant are 0 // i.e. if we don't need to calculate diffusion update if (IsFixedSubstance()) { return; } const auto nx = num_boxes_axis_[0]; const auto ny = num_boxes_axis_[1]; const auto nz = num_boxes_axis_[2]; const double ibl2 = 1 / (box_length_ * box_length_); const double d = 1 - dc_[0]; std::array l; #define YBF 16 #pragma omp parallel for collapse(2) for (size_t yy = 0; yy < ny; yy += YBF) { for (size_t z = 0; z < nz; z++) { size_t ymax = yy + YBF; if (ymax >= ny) { ymax = ny; } for (size_t y = yy; y < ymax; y++) { size_t x = 0; int c, n, s, b, t; c = x + y * nx + z * nx * ny; l.fill(1); if (y == 0) { n = c; l[0] = 0; } else { n = c - nx; } if (y == ny - 1) { s = c; l[1] = 0; } else { s = c + nx; } if (z == 0) { b = c; l[2] = 0; } else { b = c - nx * ny; } if (z == nz - 1) { t = c; l[3] = 0; } else { t = c + nx * ny; } c2_[c] = (c1_[c] + d * dt_ * (0 - 2 * c1_[c] + c1_[c + 1]) * ibl2 + d * dt_ * (c1_[s] - 2 * c1_[c] + c1_[n]) * ibl2 + d * dt_ * (c1_[b] - 2 * c1_[c] + c1_[t]) * ibl2) * (1 - mu_); #pragma omp simd for (x = 1; x < nx - 1; x++) { ++c; ++n; ++s; ++b; ++t; c2_[c] = (c1_[c] + d * dt_ * (c1_[c - 1] - 2 * c1_[c] + c1_[c + 1]) * ibl2 + d * dt_ * (l[0] * c1_[s] - 2 * c1_[c] + l[1] * c1_[n]) * ibl2 + d * dt_ * (l[2] * c1_[b] - 2 * c1_[c] + l[3] * c1_[t]) * ibl2) * (1 - mu_); } ++c; ++n; ++s; ++b; ++t; c2_[c] = (c1_[c] + d * dt_ * (c1_[c - 1] - 2 * c1_[c] + 0) * ibl2 + d * dt_ * (c1_[s] - 2 * c1_[c] + c1_[n]) * ibl2 + d * dt_ * (c1_[b] - 2 * c1_[c] + c1_[t]) * ibl2) * (1 - mu_); } // tile ny } // tile nz } // block ny c1_.swap(c2_); } /// Calculates the gradient for each box in the diffusion grid. /// The gradient is calculated in each direction (x, y, z) as following: /// /// c(x + box_length_) - c(x - box_length) / (2 * box_length_), /// /// where c(x) implies the concentration at position x /// /// At the edges the gradient is the same as the box next to it void CalculateGradient() { // check if gradient has been calculated once // and if diffusion coefficient and decay constant are 0 // i.e. if we don't need to calculate gradient update if (init_gradient_ && IsFixedSubstance()) { return; } double gd = 1 / (box_length_ * 2); auto nx = num_boxes_axis_[0]; auto ny = num_boxes_axis_[1]; auto nz = num_boxes_axis_[2]; #pragma omp parallel for collapse(2) for (size_t z = 0; z < nz; z++) { for (size_t y = 0; y < ny; y++) { for (size_t x = 0; x < nx; x++) { int c, e, w, n, s, b, t; c = x + y * nx + z * nx * ny; if (x == 0) { e = c; w = c + 2; } else if (x == nx - 1) { e = c - 2; w = c; } else { e = c - 1; w = c + 1; } if (y == 0) { n = c + 2 * nx; s = c; } else if (y == ny - 1) { n = c; s = c - 2 * nx; } else { n = c + nx; s = c - nx; } if (z == 0) { t = c + 2 * nx * ny; b = c; } else if (z == nz - 1) { t = c; b = c - 2 * nx * ny; } else { t = c + nx * ny; b = c - nx * ny; } // Let the gradient point from low to high concentration gradients_[3 * c + 0] = (c1_[w] - c1_[e]) * gd; gradients_[3 * c + 1] = (c1_[n] - c1_[s]) * gd; gradients_[3 * c + 2] = (c1_[t] - c1_[b]) * gd; } } } if (!init_gradient_) { init_gradient_ = true; } } /// Increase the concentration at specified position with specified amount void IncreaseConcentrationBy(const Double3& position, double amount) { auto idx = GetBoxIndex(position); IncreaseConcentrationBy(idx, amount); } /// Increase the concentration at specified box with specified amount void IncreaseConcentrationBy(size_t idx, double amount) { assert(idx < total_num_boxes_ && "Cell position is out of diffusion grid bounds"); c1_[idx] += amount; if (c1_[idx] > concentration_threshold_) { c1_[idx] = concentration_threshold_; } } /// Get the concentration at specified position double GetConcentration(const Double3& position) const { return c1_[GetBoxIndex(position)]; } /// Get the (normalized) gradient at specified position void GetGradient(const Double3& position, Double3* gradient) const { auto idx = GetBoxIndex(position); assert(idx < total_num_boxes_ && "Cell position is out of diffusion grid bounds"); (*gradient)[0] = gradients_[3 * idx]; (*gradient)[1] = gradients_[3 * idx + 1]; (*gradient)[2] = gradients_[3 * idx + 2]; auto norm = std::sqrt((*gradient)[0] * (*gradient)[0] + (*gradient)[1] * (*gradient)[1] + (*gradient)[2] * (*gradient)[2]); if (norm > 1e-10) { (*gradient)[0] /= norm; (*gradient)[1] /= norm; (*gradient)[2] /= norm; } } std::array GetBoxCoordinates(const Double3& position) const { std::array box_coord; box_coord[0] = (floor(position[0]) - grid_dimensions_[0]) / box_length_; box_coord[1] = (floor(position[1]) - grid_dimensions_[2]) / box_length_; box_coord[2] = (floor(position[2]) - grid_dimensions_[4]) / box_length_; return box_coord; } size_t GetBoxIndex(const std::array& box_coord) const { size_t ret = box_coord[2] * num_boxes_axis_[0] * num_boxes_axis_[1] + box_coord[1] * num_boxes_axis_[0] + box_coord[0]; return ret; } /// Calculates the box index of the substance at specified position size_t GetBoxIndex(const Double3& position) const { auto box_coord = GetBoxCoordinates(position); return GetBoxIndex(box_coord); } void SetDecayConstant(double mu) { mu_ = mu; } void SetConcentrationThreshold(double t) { concentration_threshold_ = t; } double GetConcentrationThreshold() const { return concentration_threshold_; } const double* GetAllConcentrations() const { return c1_.data(); } const double* GetAllGradients() const { return gradients_.data(); } const std::array& GetNumBoxesArray() const { return num_boxes_axis_; } size_t GetNumBoxes() const { return total_num_boxes_; } double GetBoxLength() const { return box_length_; } int GetSubstanceId() const { return substance_; } const std::string& GetSubstanceName() const { return substance_name_; } double GetDecayConstant() const { return mu_; } const int32_t* GetDimensionsPtr() const { return grid_dimensions_.data(); } const std::array& GetDimensions() const { return grid_dimensions_; } const std::array& GetDiffusionCoefficients() const { return dc_; } bool IsInitialized() const { return initialized_; } int GetResolution() const { return resolution_; } double GetBoxVolume() const { return box_volume_; } template void AddInitializer(F function) { initializers_.push_back(function); } // retrun true if substance concentration and gradient don't evolve over time bool IsFixedSubstance() { return (mu_ == 0 && dc_[1] == 0 && dc_[2] == 0 && dc_[3] == 0 && dc_[4] == 0 && dc_[5] == 0 && dc_[6] == 0); } private: /// The id of the substance of this grid int substance_ = 0; /// The name of the substance of this grid std::string substance_name_ = ""; /// The side length of each box double box_length_ = 0; /// the volume of each box double box_volume_ = 0; /// The array of concentration values ParallelResizeVector c1_ = {}; /// An extra concentration data buffer for faster value updating ParallelResizeVector c2_ = {}; /// The array of gradients (x, y, z) ParallelResizeVector gradients_ = {}; /// The maximum concentration value that a box can have double concentration_threshold_ = 1e15; /// The diffusion coefficients [cc, cw, ce, cs, cn, cb, ct] std::array dc_ = {{0}}; /// The timestep resolution fhe diffusion grid // TODO(ahmad): this probably needs to scale with Param::simulation_timestep double dt_ = 1; /// The decay constant double mu_ = 0; /// The grid dimensions of the diffusion grid std::array grid_dimensions_ = {{0}}; /// The number of boxes at each axis [x, y, z] std::array num_boxes_axis_ = {{0}}; /// The total number of boxes in the diffusion grid size_t total_num_boxes_ = 0; /// Flag to determine if this grid has been initialized bool initialized_ = false; /// The resolution of the diffusion grid int resolution_ = 0; /// If false, grid dimensions are even; if true, they are odd bool parity_ = false; /// A list of functions that initialize this diffusion grid std::vector> initializers_ = {}; // turn to true after gradient initialization bool init_gradient_ = false; BDM_CLASS_DEF_NV(DiffusionGrid, 1); }; } // namespace bdm #endif // CORE_DIFFUSION_GRID_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_EVENT_CELL_DIVISION_EVENT_H_ #define CORE_EVENT_CELL_DIVISION_EVENT_H_ #include "core/event/event.h" namespace bdm { /// \brief Contains the parameters to perform a cell division. /// /// A cell division divides a mother cell in two daughter cells.\n /// When the mother cell divides, by definition:\n /// 1) the mother cell becomes the 1st daughter cell\n /// 2) the new cell becomes the 2nd daughter cell /// /// The cell that triggers the event is the mother. struct CellDivisionEvent : public Event { static const EventId kEventId; CellDivisionEvent(double volume_ratio, double phi, double theta) : volume_ratio_(volume_ratio), phi_(phi), theta_(theta) {} virtual ~CellDivisionEvent() {} EventId GetId() const override { return kEventId; } /// volume_ratio_ the ratio (Volume daughter 1)/(Volume daughter 2). 1.0 gives /// equal cells. double volume_ratio_; /// phi_ azimuthal angle (spherical coordinates) double phi_; /// theta_ polar angle (spherical coordinates) double theta_; }; } // namespace bdm #endif // CORE_EVENT_CELL_DIVISION_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_EVENT_EVENT_H_ #define CORE_EVENT_EVENT_H_ #include #include #include "core/util/log.h" namespace bdm { /// EventId is used inside biology modules to determine if a biology module /// should be copied if a new simulation object is created. /// Possible events are cell division, neurite branching, ...\n /// EventId invariant: the number of bits set to 1 must be 1. using EventId = uint64_t; /// Biology module event representing the union of all events.\n /// Used to create a biology module which is copied for every event. /// @see `BaseBiologyModule` const EventId gAllEventIds = std::numeric_limits::max(); /// Biology module event representing the null element = empty set of events. /// @see `BaseBiologyModule` const EventId gNullEventId = 0; /// This class generates unique ids for biology module events satisfying the /// EventId invariant. Thread safe. class UniqueEventIdFactory { public: UniqueEventIdFactory(const UniqueEventIdFactory&) = delete; static UniqueEventIdFactory* Get() { static UniqueEventIdFactory kInstance; return &kInstance; } EventId NewUniqueEventId() { std::lock_guard lock(mutex_); constexpr uint64_t kOne = 1; if (counter_ == 64) { Log::Fatal("UniqueEventIdFactory", "BioDynaMo only supports 64 unique EventIds." " You requested a 65th one."); } return kOne << counter_++; } private: UniqueEventIdFactory() {} std::recursive_mutex mutex_; uint64_t counter_ = 0; }; struct Event { virtual ~Event() {} virtual EventId GetId() const = 0; }; } // namespace bdm #endif // CORE_EVENT_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_EXECUTION_CONTEXT_IN_PLACE_EXEC_CTXT_H_ #define CORE_EXECUTION_CONTEXT_IN_PLACE_EXEC_CTXT_H_ #include #include #include #include #include "core/operation/operation.h" #include "core/sim_object/so_uid.h" #include "core/util/thread_info.h" namespace bdm { class SimObject; /// This execution context updates simulation objects in place. \n /// Let's assume we have two sim objects `A, B` in our simulation that we want /// to update to the next timestep `A*, B*`. If we have one thread it will first /// update `A` and afterwards `B` and write the updates directly to the same /// data structure. Therefore, before we start updating `B` the array looks /// like this: `A*, B`. `B` already observes the updated `A`. \n /// Operations in method `Execute` are executed in order given by the user. /// Subsequent operations observe the changes of earlier operations.\n /// In-place updates can lead to race conditions if simulation objects not only /// modify themselves, but also neighbors. Therefore, a protection mechanism has /// been added. If neighbors are not modified, this protection can be turned off /// to improve performance using `DisableNeighborGuard()`. By default it is /// turned on.\n /// New sim objects will only be visible at the next iteration. \n /// Also removal of a sim object happens at the end of each iteration. class InPlaceExecutionContext { public: InPlaceExecutionContext(); virtual ~InPlaceExecutionContext(); /// This function is called at the beginning of each iteration to setup all /// execution contexts. /// This function is not thread-safe. /// NB: Invalidates references and pointers to simulation objects. void SetupIterationAll( const std::vector& all_exec_ctxts) const; /// This function is called at the end of each iteration to tear down all /// execution contexts. /// This function is not thread-safe. \n /// NB: Invalidates references and pointers to simulation objects. void TearDownIterationAll( const std::vector& all_exec_ctxts) const; /// Execute a series of operations on a simulation object in the order given /// in the argument void Execute(SimObject* so, const std::vector& operations); void push_back(SimObject* new_so); // NOLINT void ForEachNeighbor(const std::function& lambda, const SimObject& query); void ForEachNeighbor( const std::function& lambda, const SimObject& query); /// Forwards the call to `Grid::ForEachNeighborWithinRadius` void ForEachNeighborWithinRadius( const std::function& lambda, const SimObject& query, double squared_radius); SimObject* GetSimObject(SoUid uid); const SimObject* GetConstSimObject(SoUid uid); void RemoveFromSimulation(SoUid uid); /// If a sim objects modifies other simulation objects while it is updated, /// race conditions can occur using this execution context. This function /// turns the protection mechanism off to improve performance. This is safe /// simulation objects only update themselves. void DisableNeighborGuard(); private: ThreadInfo* tinfo_; /// Contains unique ids of sim objects that will be removed at the end of each /// iteration. std::vector remove_; /// Pointer to new sim objects tbb::concurrent_unordered_map new_sim_objects_; /// prevent race conditions for cached SimObjects std::atomic_flag mutex_ = ATOMIC_FLAG_INIT; std::vector> neighbor_cache_; SimObject* GetCachedSimObject(SoUid uid); }; } // namespace bdm #endif // CORE_EXECUTION_CONTEXT_IN_PLACE_EXEC_CTXT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_EXPORTER_H_ #define CORE_EXPORTER_H_ #include #include namespace bdm { class Exporter { public: virtual ~Exporter(); /// Export the simulation state of one iteration /// \param filename /// \param iteration - current iteration number (=time step) virtual void ExportIteration(std::string filename, uint64_t iteration) = 0; /// Export the simulation summary /// \param filename /// \param num_iterations - total number of iterations virtual void ExportSummary(std::string filename, uint64_t num_iterations) = 0; }; class BasicExporter : public Exporter { public: void ExportIteration(std::string filename, uint64_t iteration) override; void ExportSummary(std::string filename, uint64_t num_iterations) override; }; class MatlabExporter : public Exporter { public: void ExportIteration(std::string filename, uint64_t iteration) override; void ExportSummary(std::string filename, uint64_t num_iterations) override; }; class NeuroMLExporter : public Exporter { public: void ExportIteration(std::string filename, uint64_t iteration) override; void ExportSummary(std::string filename, uint64_t num_iterations) override; }; class ParaviewExporter : public Exporter { public: void ExportIteration(std::string filename, uint64_t iteration) override; /// This function creates a .pvd file that lists the individual files across /// the different times. /// This .pvd can be read by Paraview for visualization. void ExportSummary(std::string filename, uint64_t num_iterations) override; }; enum ExporterType { kBasic, kMatlab, kNeuroML, kParaview }; class ExporterFactory { public: static std::unique_ptr GenerateExporter(ExporterType type); }; } // namespace bdm #endif // CORE_EXPORTER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_GPU_DISPLACEMENT_OP_CUDA_KERNEL_H_ #define CORE_GPU_DISPLACEMENT_OP_CUDA_KERNEL_H_ #include #include #include "stdio.h" namespace bdm { class DisplacementOpCudaKernel { public: DisplacementOpCudaKernel(uint32_t num_objects, uint32_t num_boxes); virtual ~DisplacementOpCudaKernel(); void LaunchDisplacementKernel( const double* positions, const double* diameter, const double* tractor_force, const double* adherence, uint32_t* box_id, const double* mass, const double* timestep, const double* max_displacement, const double* squared_radius, uint32_t* num_objects, uint32_t* starts, uint16_t* lengths, uint32_t* successors, uint32_t* box_length, uint32_t* num_boxes_axis, int32_t* grid_dimensions, double* cell_movements); void ResizeCellBuffers(uint32_t num_cells); void ResizeGridBuffers(uint32_t num_boxes); private: double* d_positions_ = NULL; double* d_diameters_ = NULL; double* d_mass_ = NULL; double* d_timestep_ = NULL; double* d_max_displacement_ = NULL; double* d_squared_radius_ = NULL; uint32_t* d_num_objects_ = NULL; double* d_cell_movements_ = NULL; double* d_tractor_force_ = NULL; double* d_adherence_ = NULL; uint32_t* d_box_id_ = NULL; uint32_t* d_starts_ = NULL; uint16_t* d_lengths_ = NULL; uint32_t* d_successors_ = NULL; uint32_t* d_box_length_ = NULL; uint32_t* d_num_boxes_axis_ = NULL; int32_t* d_grid_dimensions_ = NULL; }; } // namespace bdm #endif // CORE_GPU_DISPLACEMENT_OP_CUDA_KERNEL_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_GPU_GPU_HELPER_H_ #define CORE_GPU_GPU_HELPER_H_ #include #include #include #include #include #ifndef __ROOTCLING__ #ifdef USE_OPENCL #ifdef __APPLE__ #define CL_HPP_ENABLE_EXCEPTIONS #define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY #define CL_HPP_MINIMUM_OPENCL_VERSION 120 #define CL_HPP_TARGET_OPENCL_VERSION 120 #include "cl2.hpp" #else #define __CL_ENABLE_EXCEPTIONS #include #endif #endif // USE_OPENCL #ifdef USE_CUDA #include "cuda_runtime_api.h" #endif // USE_CUDA #include "core/gpu/opencl_state.h" #include "core/param/param.h" #include "core/simulation.h" #include "core/util/log.h" namespace bdm { class GpuHelper { public: static GpuHelper* GetInstance() { static GpuHelper kInstance; return &kInstance; } #ifdef USE_CUDA static void FindGpuDevicesCuda() { auto* param = Simulation::GetActive()->GetParam(); int n_devices = 0; cudaGetDeviceCount(&n_devices); if (n_devices == 0) { Log::Fatal("FindGpuDevicesCuda", "No CUDA-compatible GPU found on this machine!"); return; } Log::Info("", "Found ", n_devices, " CUDA-compatible GPU device(s): "); for (int i = 0; i < n_devices; i++) { cudaSetDevice(i); cudaDeviceProp prop; cudaGetDeviceProperties(&prop, i); Log::Info("", " [", i, "] ", prop.name); } cudaSetDevice(param->preferred_gpu_); cudaDeviceProp prop; cudaGetDeviceProperties(&prop, param->preferred_gpu_); Log::Info("", "Selected GPU [", param->preferred_gpu_, "]: ", prop.name); } #endif // USE_CUDA #if defined(USE_OPENCL) && !defined(__ROOTCLING__) #define ClOk(err) \ OpenCLState::GetInstance()->ClAssert(err, __FILE__, __LINE__, true); void CompileOpenCLKernels() { auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); auto* ocl_state = OpenCLState::GetInstance(); std::vector* all_programs = ocl_state->GetOpenCLProgramList(); cl::Context* context = ocl_state->GetOpenCLContext(); std::vector* devices = ocl_state->GetOpenCLDeviceList(); // Compile OpenCL program for found device // TODO(ahmad): create more convenient way to compile all OpenCL kernels, by // going through a list of header files. Also, create a stringifier that // goes // from .cl --> .h, since OpenCL kernels must be input as a string here std::string bdm_src_dir = std::getenv("BDM_SRC_DIR"); std::ifstream cl_file(bdm_src_dir + "/core/gpu/displacement_op_opencl_kernel.cl"); if (cl_file.fail()) { Log::Error("CompileOpenCLKernels", "Kernel file does not exists!"); } std::stringstream buffer; buffer << cl_file.rdbuf(); cl::Program displacement_op_program( *context, cl::Program::Sources( 1, std::make_pair(buffer.str().c_str(), buffer.str().length()))); all_programs->push_back(displacement_op_program); Log::Info("", "Compiling OpenCL kernels..."); std::string options; if (param->opencl_debug_) { Log::Info("", "Building OpenCL kernels with debugging symbols"); options = "-g -O0"; } else { Log::Info("", "Building OpenCL kernels without debugging symbols"); } for (auto& prog : *all_programs) { try { prog.build(*devices, options.c_str()); } catch (const cl::Error&) { Log::Error("CompileOpenCLKernels", "OpenCL compilation error: ", prog.getBuildInfo((*devices)[0])); } } } void FindGpuDevicesOpenCL() { try { // We keep the context and device list in the resource manager to be // accessible elsewhere to create command queues and buffers from auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); auto* ocl_state = OpenCLState::GetInstance(); cl::Context* context = ocl_state->GetOpenCLContext(); cl::CommandQueue* queue = ocl_state->GetOpenCLCommandQueue(); std::vector* devices = ocl_state->GetOpenCLDeviceList(); // Get list of OpenCL platforms. std::vector platform; // If we get stuck here, it might be because the DISPLAY envar is not set. // Set it to 0 to avoid getting stuck. It's an AMD specific issue cl::Platform::get(&platform); if (platform.empty()) { Log::Error("FindGpuDevicesOpenCL", "No OpenCL platforms found"); } // Go over all available platforms and devices until first device is found for (auto p = platform.begin(); p != platform.end(); p++) { std::vector pldev; try { p->getDevices(CL_DEVICE_TYPE_GPU, &pldev); for (auto d = pldev.begin(); d != pldev.end(); d++) { if (!d->getInfo()) // NOLINT continue; // The OpenCL extension available on this device std::string ext = d->getInfo(); devices->push_back(*d); } } catch (...) { Log::Error("FindGpuDevicesOpenCL", "Found bad OpenCL platform! Continuing to next one"); devices->clear(); continue; } } if (devices->empty()) { Log::Fatal("FindGpuDevicesCuda", "No CUDA-compatible GPU found on this machine!"); return; } *context = cl::Context(*devices); Log::Info("", "Found ", devices->size(), " OpenCL-compatible GPU device(s): "); for (size_t i = 0; i < devices->size(); i++) { Log::Info("", " [", i, "] ", (*devices)[i].getInfo()); } int selected_gpu = param->preferred_gpu_; Log::Info("", "Selected GPU [", selected_gpu, "]: ", (*devices)[selected_gpu].getInfo()); // Create command queue for that GPU cl_int queue_err; *queue = cl::CommandQueue(*context, (*devices)[param->preferred_gpu_], CL_QUEUE_PROFILING_ENABLE, &queue_err); ClOk(queue_err); // Compile the OpenCL kernels CompileOpenCLKernels(); } catch (const cl::Error& err) { Log::Error("FindGpuDevicesOpenCL", "OpenCL error: ", err.what(), "(", err.err(), ")"); } } #endif // defined(USE_OPENCL) && !defined(__ROOTCLING__) #if (defined(USE_CUDA) || defined(USE_OPENCL)) && !defined(__ROOTCLING__) void InitializeGPUEnvironment() { auto* param = Simulation::GetActive()->GetParam(); if (param->use_opencl_) { #ifdef USE_OPENCL FindGpuDevicesOpenCL(); #else Log::Fatal( "InitializeGPUEnvironment", "You tried to use the GPU (OpenCL) version of BioDynaMo, but no " "OpenCL installation was detected on this machine. Switching to " "the CPU version..."); #endif // USE_OPENCL } else { #ifdef USE_CUDA FindGpuDevicesCuda(); #else Log::Fatal("InitializeGPUEnvironment", "You tried to use the GPU (CUDA) version of BioDynaMo, but no " "CUDA installation was detected on this machine. Switching to " "the CPU version..."); #endif // USE_CUDA } } #endif // defined(USE_CUDA) || defined(USE_OPENCL) && !defined(__ROOTCLING__) }; } // namespace bdm #endif // __ROOTCLING__ #endif // CORE_GPU_GPU_HELPER_H_ #ifndef CORE_GPU_OPENCL_STATE_H_ #define CORE_GPU_OPENCL_STATE_H_ #if defined(USE_OPENCL) && !defined(__ROOTCLING__) class OpenCLState { public: static OpenCLState* GetInstance() { static OpenCLState kInstance; return &kInstance; } /// Returns the OpenCL Context cl::Context* GetOpenCLContext() { return &opencl_context_; } /// Returns the OpenCL command queue cl::CommandQueue* GetOpenCLCommandQueue() { return &opencl_command_queue_; } /// Returns the OpenCL device (GPU) list std::vector* GetOpenCLDeviceList() { return &opencl_devices_; } /// Returns the OpenCL program (kernel) list std::vector* GetOpenCLProgramList() { return &opencl_programs_; } const char* GetErrorString(cl_int error) { switch (error) { // run-time and JIT compiler errors case 0: return "CL_SUCCESS"; case -1: return "CL_DEVICE_NOT_FOUND"; case -2: return "CL_DEVICE_NOT_AVAILABLE"; case -3: return "CL_COMPILER_NOT_AVAILABLE"; case -4: return "CL_MEM_OBJECT_ALLOCATION_FAILURE"; case -5: return "CL_OUT_OF_RESOURCES"; case -6: return "CL_OUT_OF_HOST_MEMORY"; case -7: return "CL_PROFILING_INFO_NOT_AVAILABLE"; case -8: return "CL_MEM_COPY_OVERLAP"; case -9: return "CL_IMAGE_FORMAT_MISMATCH"; case -10: return "CL_IMAGE_FORMAT_NOT_SUPPORTED"; case -11: return "CL_BUILD_PROGRAM_FAILURE"; case -12: return "CL_MAP_FAILURE"; case -13: return "CL_MISALIGNED_SUB_BUFFER_OFFSET"; case -14: return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST"; case -15: return "CL_COMPILE_PROGRAM_FAILURE"; case -16: return "CL_LINKER_NOT_AVAILABLE"; case -17: return "CL_LINK_PROGRAM_FAILURE"; case -18: return "CL_DEVICE_PARTITION_FAILED"; case -19: return "CL_KERNEL_ARG_INFO_NOT_AVAILABLE"; // compile-time errors case -30: return "CL_INVALID_VALUE"; case -31: return "CL_INVALID_DEVICE_TYPE"; case -32: return "CL_INVALID_PLATFORM"; case -33: return "CL_INVALID_DEVICE"; case -34: return "CL_INVALID_CONTEXT"; case -35: return "CL_INVALID_QUEUE_PROPERTIES"; case -36: return "CL_INVALID_COMMAND_QUEUE"; case -37: return "CL_INVALID_HOST_PTR"; case -38: return "CL_INVALID_MEM_OBJECT"; case -39: return "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"; case -40: return "CL_INVALID_IMAGE_SIZE"; case -41: return "CL_INVALID_SAMPLER"; case -42: return "CL_INVALID_BINARY"; case -43: return "CL_INVALID_BUILD_OPTIONS"; case -44: return "CL_INVALID_PROGRAM"; case -45: return "CL_INVALID_PROGRAM_EXECUTABLE"; case -46: return "CL_INVALID_KERNEL_NAME"; case -47: return "CL_INVALID_KERNEL_DEFINITION"; case -48: return "CL_INVALID_KERNEL"; case -49: return "CL_INVALID_ARG_INDEX"; case -50: return "CL_INVALID_ARG_VALUE"; case -51: return "CL_INVALID_ARG_SIZE"; case -52: return "CL_INVALID_KERNEL_ARGS"; case -53: return "CL_INVALID_WORK_DIMENSION"; case -54: return "CL_INVALID_WORK_GROUP_SIZE"; case -55: return "CL_INVALID_WORK_ITEM_SIZE"; case -56: return "CL_INVALID_GLOBAL_OFFSET"; case -57: return "CL_INVALID_EVENT_WAIT_LIST"; case -58: return "CL_INVALID_EVENT"; case -59: return "CL_INVALID_OPERATION"; case -60: return "CL_INVALID_GL_OBJECT"; case -61: return "CL_INVALID_BUFFER_SIZE"; case -62: return "CL_INVALID_MIP_LEVEL"; case -63: return "CL_INVALID_GLOBAL_WORK_SIZE"; case -64: return "CL_INVALID_PROPERTY"; case -65: return "CL_INVALID_IMAGE_DESCRIPTOR"; case -66: return "CL_INVALID_COMPILER_OPTIONS"; case -67: return "CL_INVALID_LINKER_OPTIONS"; case -68: return "CL_INVALID_DEVICE_PARTITION_COUNT"; // extension errors case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR"; case -1001: return "CL_PLATFORM_NOT_FOUND_KHR"; case -1002: return "CL_INVALID_D3D10_DEVICE_KHR"; case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR"; case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR"; case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR"; default: return "Unknown OpenCL error"; } } cl_int ClAssert(cl_int const code, char const* const file, int const line, bool const abort) { if (code != CL_SUCCESS) { char const* const err_str = GetErrorString(code); fprintf(stderr, "\"%s\", line %d: ClAssert (%d) = \"%s\"", file, line, code, err_str); if (abort) { // stop profiling and reset device here if necessary exit(code); } } return code; } private: cl::Context opencl_context_; //! cl::CommandQueue opencl_command_queue_; //! // Currently only support for one GPU device std::vector opencl_devices_; //! std::vector opencl_programs_; //! }; #endif // defined(USE_OPENCL) && !defined(__ROOTCLING__) #endif // CORE_GPU_OPENCL_STATE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_GRID_H_ #define CORE_GRID_H_ #include #include #include #include #include #include #include #include #include #include #ifdef LINUX #include #endif // LINUX #include #include #include #include "core/container/fixed_size_vector.h" #include "core/container/inline_vector.h" #include "core/container/math_array.h" #include "core/container/parallel_resize_vector.h" #include "core/container/sim_object_vector.h" #include "core/param/param.h" #include "core/resource_manager.h" #include "core/util/log.h" #include "core/util/spinlock.h" namespace bdm { /// FIFO data structure. Stores max N number of objects. /// Adding more elements will overwrite the oldest ones. template class CircularBuffer { public: CircularBuffer() { for (uint64_t i = 0; i < N; i++) { data_[i] = T(); } } void clear() { // NOLINT position_ = 0; for (uint64_t i = 0; i < N; i++) { data_[i].clear(); } } void push_back(const T& data) { // NOLINT data_[position_] = data; Increment(); } T& operator[](uint64_t idx) { return data_[(idx + position_) % N]; } const T& operator[](uint64_t idx) const { return data_[(idx + position_) % N]; } /// Calling function `End` and afterwards `Increment` is equivalent to /// `push_back`, but avoids copying data T* End() { return &(data_[position_]); } void Increment() { position_++; position_ %= N; } private: T data_[N]; uint64_t position_ = 0; }; /// A class that represents Cartesian 3D grid class Grid { // DisplacementOpCuda needs access to some Grid private members to reconstruct // the grid on GPU (same for DisplacementOpOpenCL) friend class DisplacementOpCuda; friend class DisplacementOpOpenCL; public: /// A single unit cube of the grid struct Box { Spinlock lock_; // std::atomic timestamp_; uint32_t timestamp_; /// start value of the linked list of simulation objects inside this box. /// Next element can be found at `successors_[start_]` SoHandle start_; /// length of the linked list (i.e. number of simulation objects) /// uint64_t, because sizeof(Box) = 16, for uint16_t and uint64_t uint16_t length_; Box() : timestamp_(0), start_(SoHandle()), length_(0) {} /// Copy Constructor required for boxes_.resize() /// Since box values will be overwritten afterwards it forwards to the /// default ctor Box(const Box& other) : Box() {} Box& operator=(const Box& other) { // start_ = other.start_.load(std::memory_order_relaxed); // length_ = other.length_.load(std::memory_order_relaxed); start_ = other.start_; length_ = other.length_; return *this; } bool IsEmpty(uint64_t grid_timestamp) const { return grid_timestamp != timestamp_; } /// @brief Adds a simulation object to this box /// /// @param[in] so The object's identifier /// @param AddObject successors The successors void AddObject(SoHandle so, SimObjectVector* successors, Grid* grid) { std::lock_guard lock_guard(lock_); if (timestamp_ != grid->timestamp_) { timestamp_ = grid->timestamp_; length_ = 1; start_ = so; } else { length_++; (*successors)[so] = start_; start_ = so; } } /// An iterator that iterates over the cells in this box struct Iterator { Iterator(Grid* grid, const Box* box) : grid_(grid), current_value_(box->start_), countdown_(box->length_) { if (grid->timestamp_ != box->timestamp_) { countdown_ = 0; } } bool IsAtEnd() { return countdown_ <= 0; } Iterator& operator++() { countdown_--; if (countdown_ > 0) { current_value_ = grid_->successors_[current_value_]; } return *this; } SoHandle operator*() const { return current_value_; } /// Pointer to the neighbor grid; for accessing the successor_ list Grid* grid_; /// The current simulation object to be considered SoHandle current_value_; /// The remain number of simulation objects to consider int countdown_ = 0; }; Iterator begin() const { // NOLINT return Iterator(Simulation::GetActive()->GetGrid(), this); } }; /// An iterator that iterates over the boxes in this grid struct NeighborIterator { explicit NeighborIterator( const FixedSizeVector& neighbor_boxes, uint64_t grid_timestamp) : neighbor_boxes_(neighbor_boxes), // start iterator from box 0 box_iterator_(neighbor_boxes_[0]->begin()), grid_timestamp_(grid_timestamp) { // if first box is empty if (neighbor_boxes_[0]->IsEmpty(grid_timestamp)) { ForwardToNonEmptyBox(grid_timestamp); } } bool IsAtEnd() const { return is_end_; } SoHandle operator*() const { return *box_iterator_; } /// Version where empty neighbor boxes are allowed NeighborIterator& operator++() { ++box_iterator_; // if iterator of current box has come to an end, continue with next box if (box_iterator_.IsAtEnd()) { return ForwardToNonEmptyBox(grid_timestamp_); } return *this; } private: /// The 27 neighbor boxes that will be searched for simulation objects const FixedSizeVector& neighbor_boxes_; /// The box that shall be considered to iterate over for finding simulation /// objects typename Box::Iterator box_iterator_; uint64_t grid_timestamp_; /// The id of the box to be considered (i.e. value between 0 - 26) uint16_t box_idx_ = 0; /// Flag to indicate that all the neighbor boxes have been searched through bool is_end_ = false; /// Forwards the iterator to the next non empty box and returns itself /// If there are no non empty boxes is_end_ is set to true NeighborIterator& ForwardToNonEmptyBox(uint64_t grid_timestamp) { // increment box id until non empty box has been found while (++box_idx_ < neighbor_boxes_.size()) { // box is empty or uninitialized (padding box) -> continue if (neighbor_boxes_[box_idx_]->IsEmpty(grid_timestamp)) { continue; } // a non-empty box has been found box_iterator_ = neighbor_boxes_[box_idx_]->begin(); return *this; } // all remaining boxes have been empty; reached end is_end_ = true; return *this; } }; /// Enum that determines the degree of adjacency in search neighbor boxes // todo(ahmad): currently only kHigh is supported (hardcoded 26 several // places) enum Adjacency { kLow, /**< The closest 8 neighboring boxes */ kMedium, /**< The closest 18 neighboring boxes */ kHigh /**< The closest 26 neighboring boxes */ }; Grid() {} Grid(Grid const&) = delete; void operator=(Grid const&) = delete; /// @brief Initialize the grid with the given simulation objects /// @param[in] adjacency The adjacency (see #Adjacency) void Initialize(Adjacency adjacency = kHigh) { adjacency_ = adjacency; UpdateGrid(); initialized_ = true; } virtual ~Grid() {} /// Clears the grid void ClearGrid() { box_length_ = 1; largest_object_size_ = 0; num_boxes_axis_ = {{0}}; num_boxes_xy_ = 0; int32_t inf = std::numeric_limits::max(); grid_dimensions_ = {inf, -inf, inf, -inf, inf, -inf}; threshold_dimensions_ = {inf, -inf}; successors_.clear(); has_grown_ = false; } /// Updates the grid, as simulation objects may have moved, added or deleted void UpdateGrid() { auto* rm = Simulation::GetActive()->GetResourceManager(); if (rm->GetNumSimObjects() != 0) { ClearGrid(); timestamp_++; auto inf = Math::kInfinity; std::array tmp_dim = {{inf, -inf, inf, -inf, inf, -inf}}; CalculateGridDimensions(&tmp_dim); RoundOffGridDimensions(tmp_dim); auto los = ceil(largest_object_size_); assert(los > 0 && "The largest object size was found to be 0. Please check if your " "cells are correctly initialized."); box_length_ = los; for (int i = 0; i < 3; i++) { int dimension_length = grid_dimensions_[2 * i + 1] - grid_dimensions_[2 * i]; int r = dimension_length % box_length_; // If the grid is not perfectly divisible along each dimension by the // resolution, extend the grid so that it is if (r != 0) { // std::abs for the case that box_length_ > dimension_length grid_dimensions_[2 * i + 1] += (box_length_ - r); } else { // Else extend the grid dimension with one row, because the outmost // object lies exactly on the border grid_dimensions_[2 * i + 1] += box_length_; } } // Pad the grid to avoid out of bounds check when search neighbors for (int i = 0; i < 3; i++) { grid_dimensions_[2 * i] -= box_length_; grid_dimensions_[2 * i + 1] += box_length_; } // Calculate how many boxes fit along each dimension for (int i = 0; i < 3; i++) { int dimension_length = grid_dimensions_[2 * i + 1] - grid_dimensions_[2 * i]; assert((dimension_length % box_length_ == 0) && "The grid dimensions are not a multiple of its box length"); num_boxes_axis_[i] = dimension_length / box_length_; } num_boxes_xy_ = num_boxes_axis_[0] * num_boxes_axis_[1]; auto total_num_boxes = num_boxes_xy_ * num_boxes_axis_[2]; CheckGridGrowth(); // resize boxes_ if (boxes_.size() != total_num_boxes) { if (boxes_.capacity() < total_num_boxes) { boxes_.reserve(total_num_boxes * 2); } boxes_.resize(total_num_boxes); } successors_.reserve(); // Assign simulation objects to boxes rm->ApplyOnAllElementsParallelDynamic( 1000, [this](SimObject* sim_object, SoHandle soh) { const auto& position = sim_object->GetPosition(); auto idx = this->GetBoxIndex(position); auto box = this->GetBoxPointer(idx); box->AddObject(soh, &successors_, this); sim_object->SetBoxIdx(idx); }); auto* param = Simulation::GetActive()->GetParam(); if (param->bound_space_) { int min = param->min_bound_; int max = param->max_bound_; threshold_dimensions_ = {min, max}; } if (nb_mutex_builder_ != nullptr) { nb_mutex_builder_->Update(); } } else { // There are no sim objects in this simulation auto* param = Simulation::GetActive()->GetParam(); bool uninitialized = boxes_.size() == 0; if (uninitialized && param->bound_space_) { // Simulation has never had any simulation objects // Initialize grid dimensions with `Param::min_bound_` and // `Param::max_bound_` // This is required for the DiffusionGrid int min = param->min_bound_; int max = param->max_bound_; grid_dimensions_ = {min, max, min, max, min, max}; threshold_dimensions_ = {min, max}; has_grown_ = true; } else if (!uninitialized) { // all simulation objects have been removed in the last iteration // grid state remains the same, but we have to set has_grown_ to false // otherwise the DiffusionGrid will attempt to resize has_grown_ = false; } else { Log::Fatal( "Grid", "You tried to initialize an empty simulation without bound space. " "Therefore we cannot determine the size of the simulation space. " "Please add simulation objects, or set Param::bound_space_, " "Param::min_bound_, and Param::max_bound_."); } } } /// @brief Calculates the squared euclidian distance between two points /// in 3D /// /// @param[in] pos1 Position of the first point /// @param[in] pos2 Position of the second point /// /// @return The distance between the two points /// inline double SquaredEuclideanDistance(const Double3& pos1, const Double3& pos2) const { const double dx = pos2[0] - pos1[0]; const double dy = pos2[1] - pos1[1]; const double dz = pos2[2] - pos1[2]; return (dx * dx + dy * dy + dz * dz); } inline bool WithinSquaredEuclideanDistance(double squared_radius, const Double3& pos1, const Double3& pos2) const { const double dx = pos2[0] - pos1[0]; const double dx2 = dx * dx; if (dx2 > squared_radius) { return false; } const double dy = pos2[1] - pos1[1]; const double dy2_plus_dx2 = dy * dy + dx2; if (dy2_plus_dx2 > squared_radius) { return false; } const double dz = pos2[2] - pos1[2]; const double distance = dz * dz + dy2_plus_dx2; return distance < squared_radius; } void UpdateBoxZOrder() { // iterate boxes in Z-order / morton order // TODO(lukas) this is a very quick attempt to test an idea // improve performance of this brute force solution zorder_sorted_boxes_.resize(boxes_.size()); #pragma omp parallel for collapse(3) for (uint32_t x = 0; x < num_boxes_axis_[0]; x++) { for (uint32_t y = 0; y < num_boxes_axis_[1]; y++) { for (uint32_t z = 0; z < num_boxes_axis_[2]; z++) { auto box_idx = GetBoxIndex(std::array{x, y, z}); auto morton = libmorton::morton3D_64_encode(x, y, z); zorder_sorted_boxes_[box_idx] = std::pair{morton, &boxes_[box_idx]}; } } } #ifdef LINUX __gnu_parallel::sort( zorder_sorted_boxes_.begin(), zorder_sorted_boxes_.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); #else std::sort( zorder_sorted_boxes_.begin(), zorder_sorted_boxes_.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); #endif // LINUX } /// This method iterates over all elements. Iteration is performed in /// Z-order of boxes. There is no particular order for elements inside a box. template void IterateZOrder(const Lambda& lambda) { UpdateBoxZOrder(); for (uint64_t i = 0; i < zorder_sorted_boxes_.size(); i++) { auto it = zorder_sorted_boxes_[i].second->begin(); while (!it.IsAtEnd()) { lambda(*it); ++it; } } } /// @brief Applies the given lambda to each neighbor /// /// @param[in] lambda The operation as a lambda /// @param query The query object void ForEachNeighbor(const std::function& lambda, const SimObject& query) const { auto idx = query.GetBoxIdx(); FixedSizeVector neighbor_boxes; GetMooreBoxes(&neighbor_boxes, idx); auto* rm = Simulation::GetActive()->GetResourceManager(); NeighborIterator ni(neighbor_boxes, timestamp_); while (!ni.IsAtEnd()) { auto* sim_object = rm->GetSimObjectWithSoHandle(*ni); if (sim_object != &query) { lambda(sim_object); } ++ni; } } /// @brief Applies the given lambda to each neighbor or the specified /// simulation object. /// /// In simulation code do not use this function directly. Use the same /// function from the exeuction context (e.g. `InPlaceExecutionContext`) /// /// @param[in] lambda The operation as a lambda /// @param query The query object /// void ForEachNeighbor( const std::function& lambda, const SimObject& query) { const auto& position = query.GetPosition(); auto idx = query.GetBoxIdx(); FixedSizeVector neighbor_boxes; GetMooreBoxes(&neighbor_boxes, idx); auto* rm = Simulation::GetActive()->GetResourceManager(); NeighborIterator ni(neighbor_boxes, timestamp_); const unsigned batch_size = 64; uint64_t size = 0; SimObject* sim_objects[batch_size] __attribute__((aligned(64))); double x[batch_size] __attribute__((aligned(64))); double y[batch_size] __attribute__((aligned(64))); double z[batch_size] __attribute__((aligned(64))); double squared_distance[batch_size] __attribute__((aligned(64))); auto process_batch = [&]() { #pragma omp simd for (uint64_t i = 0; i < size; ++i) { const double dx = x[i] - position[0]; const double dy = y[i] - position[1]; const double dz = z[i] - position[2]; squared_distance[i] = dx * dx + dy * dy + dz * dz; } for (uint64_t i = 0; i < size; ++i) { lambda(sim_objects[i], squared_distance[i]); } size = 0; }; while (!ni.IsAtEnd()) { auto soh = *ni; // increment iterator already here to hide memory latency ++ni; auto* sim_object = rm->GetSimObjectWithSoHandle(soh); if (sim_object != &query) { sim_objects[size] = sim_object; const auto& pos = sim_object->GetPosition(); x[size] = pos[0]; y[size] = pos[1]; z[size] = pos[2]; size++; if (size == batch_size) { process_batch(); } } } process_batch(); } /// @brief Applies the given lambda to each neighbor or the specified /// simulation object. /// /// In simulation code do not use this function directly. Use the same /// function from the exeuction context (e.g. `InPlaceExecutionContext`) /// /// @param[in] lambda The operation as a lambda /// @param query The query object /// @param[in] squared_radius The search radius squared /// void ForEachNeighborWithinRadius( const std::function& lambda, const SimObject& query, double squared_radius) { const auto& position = query.GetPosition(); auto idx = query.GetBoxIdx(); FixedSizeVector neighbor_boxes; GetMooreBoxes(&neighbor_boxes, idx); auto* rm = Simulation::GetActive()->GetResourceManager(); NeighborIterator ni(neighbor_boxes, timestamp_); while (!ni.IsAtEnd()) { // Do something with neighbor object auto* sim_object = rm->GetSimObjectWithSoHandle(*ni); if (sim_object != &query) { const auto& neighbor_position = sim_object->GetPosition(); if (this->WithinSquaredEuclideanDistance(squared_radius, position, neighbor_position)) { lambda(sim_object); } } ++ni; } } /// @brief Return the box index in the one dimensional array of the box /// that contains the position /// /// @param[in] position The position of the object /// /// @return The box index. /// size_t GetBoxIndex(const Double3& position) const { std::array box_coord; box_coord[0] = (floor(position[0]) - grid_dimensions_[0]) / box_length_; box_coord[1] = (floor(position[1]) - grid_dimensions_[2]) / box_length_; box_coord[2] = (floor(position[2]) - grid_dimensions_[4]) / box_length_; return GetBoxIndex(box_coord); } /// Gets the size of the largest object in the grid double GetLargestObjectSize() const { return largest_object_size_; } const std::array& GetDimensions() const { return grid_dimensions_; } const std::array& GetDimensionThresholds() const { return threshold_dimensions_; } uint64_t GetNumBoxes() const { return boxes_.size(); } uint32_t GetBoxLength() { return box_length_; } bool HasGrown() { return has_grown_; } std::array GetBoxCoordinates(size_t box_idx) const { std::array box_coord; box_coord[2] = box_idx / num_boxes_xy_; auto remainder = box_idx % num_boxes_xy_; box_coord[1] = remainder / num_boxes_axis_[0]; box_coord[0] = remainder % num_boxes_axis_[0]; return box_coord; } bool IsInitialized() { return initialized_; } /// @brief Gets the information about the grid /// /// @param box_length The grid's box length /// @param num_boxes_axis The number boxes along each axis of the grid /// @param grid_dimensions The grid's dimensions /// /// @tparam TUint32 A uint32 type (could also be cl_uint) /// @tparam TInt32 A int32 type (could be cl_int) /// template void GetGridInfo(TUint32* box_length, std::array* num_boxes_axis, std::array* grid_dimensions) { box_length[0] = box_length_; (*num_boxes_axis)[0] = num_boxes_axis_[0]; (*num_boxes_axis)[1] = num_boxes_axis_[1]; (*num_boxes_axis)[2] = num_boxes_axis_[2]; (*grid_dimensions)[0] = grid_dimensions_[0]; (*grid_dimensions)[1] = grid_dimensions_[2]; (*grid_dimensions)[2] = grid_dimensions_[4]; } // NeighborMutex --------------------------------------------------------- /// This class ensures thread-safety for the InPlaceExecutionContext for the /// case /// that a simulation object modifies its neighbors. class NeighborMutexBuilder { public: /// The NeighborMutex class is a synchronization primitive that can be /// used to protect sim_objects data from being simultaneously accessed by /// multiple threads. class NeighborMutex { public: NeighborMutex(const FixedSizeVector& mutex_indices, NeighborMutexBuilder* mutex_builder) : mutex_indices_(mutex_indices), mutex_builder_(mutex_builder) { // Deadlocks occur if mutliple threads try to acquire the same locks, // but in different order. // -> sort to avoid deadlocks - see lock ordering std::sort(mutex_indices_.begin(), mutex_indices_.end()); } void lock() { // NOLINT for (auto idx : mutex_indices_) { auto& mutex = mutex_builder_->mutexes_[idx].mutex_; // acquire lock (and spin if another thread is holding it) while (mutex.test_and_set(std::memory_order_acquire)) { } } } void unlock() { // NOLINT for (auto idx : mutex_indices_) { auto& mutex = mutex_builder_->mutexes_[idx].mutex_; mutex.clear(std::memory_order_release); } } private: FixedSizeVector mutex_indices_; NeighborMutexBuilder* mutex_builder_; }; /// Used to store mutexes in a vector. /// Always creates a new mutex (even for the copy constructor) struct MutexWrapper { MutexWrapper() {} MutexWrapper(const MutexWrapper&) {} std::atomic_flag mutex_ = ATOMIC_FLAG_INIT; }; void Update() { auto* grid = Simulation::GetActive()->GetGrid(); mutexes_.resize(grid->GetNumBoxes()); } NeighborMutex GetMutex(uint64_t box_idx) { auto* grid = Simulation::GetActive()->GetGrid(); FixedSizeVector box_indices; grid->GetMooreBoxIndices(&box_indices, box_idx); return NeighborMutex(box_indices, this); } private: /// one mutex for each box in `Grid::boxes_` std::vector mutexes_; }; /// Disable neighbor mutexes management. `GetNeighborMutexBuilder()` will /// return a nullptr. void DisableNeighborMutexes() { nb_mutex_builder_ = nullptr; } /// Returns the `NeighborMutexBuilder`. The client use it to create a /// `NeighborMutex`. If neighbor mutexes has been disabled by calling /// `DisableNeighborMutexes`, this function will return a nullptr. NeighborMutexBuilder* GetNeighborMutexBuilder() { return nb_mutex_builder_.get(); } private: /// The vector containing all the boxes in the grid /// Using parallel resize vector to enable parallel initialization and thus /// better scalability. ParallelResizeVector boxes_; /// is incremented at each call to UpdateGrid /// This is used to decide if boxes should be reinitialized uint32_t timestamp_ = 0; /// Length of a Box uint32_t box_length_ = 1; /// Stores the number of boxes for each axis std::array num_boxes_axis_ = {{0}}; /// Number of boxes in the xy plane (=num_boxes_axis_[0] * num_boxes_axis_[1]) size_t num_boxes_xy_ = 0; /// Implements linked list - array index = key, value: next element /// /// // Usage /// SoHandle current_element = ...; /// SoHandle next_element = successors_[current_element]; SimObjectVector successors_; /// Determines which boxes to search neighbors in (see enum Adjacency) Adjacency adjacency_; /// The size of the largest object in the simulation double largest_object_size_ = 0; /// Cube which contains all simulation objects /// {x_min, x_max, y_min, y_max, z_min, z_max} std::array grid_dimensions_; /// Stores the min / max dimension value that need to be surpassed in order /// to trigger a diffusion grid change std::array threshold_dimensions_; /// Flag to indicate that the grid dimensions have increased bool has_grown_ = false; /// Flag to indicate if the grid has been initialized or not bool initialized_ = false; /// stores pairs of sorted by morton code. ParallelResizeVector> zorder_sorted_boxes_; /// Holds instance of NeighborMutexBuilder if it is enabled. /// If `DisableNeighborMutexes` has been called this member set to nullptr. std::unique_ptr nb_mutex_builder_ = std::make_unique(); void CheckGridGrowth() { // Determine if the grid dimensions have changed (changed in the sense that // the grid has grown outwards) auto min_gd = *std::min_element(grid_dimensions_.begin(), grid_dimensions_.end()); auto max_gd = *std::max_element(grid_dimensions_.begin(), grid_dimensions_.end()); if (min_gd < threshold_dimensions_[0]) { threshold_dimensions_[0] = min_gd; has_grown_ = true; } if (max_gd > threshold_dimensions_[1]) { Log::Info("Grid", "Your simulation objects are getting near the edge of " "the simulation space. Be aware of boundary conditions that " "may come into play!"); threshold_dimensions_[1] = max_gd; has_grown_ = true; } } /// Calculates what the grid dimensions need to be in order to contain all the /// simulation objects void CalculateGridDimensions(std::array* ret_grid_dimensions) { auto* rm = Simulation::GetActive()->GetResourceManager(); const auto max_threads = omp_get_max_threads(); // allocate version for each thread - avoid false sharing by padding them // assumes 64 byte cache lines (8 * sizeof(double)) std::vector> xmin(max_threads, {{Math::kInfinity}}); std::vector> ymin(max_threads, {{Math::kInfinity}}); std::vector> zmin(max_threads, {{Math::kInfinity}}); std::vector> xmax(max_threads, {{-Math::kInfinity}}); std::vector> ymax(max_threads, {{-Math::kInfinity}}); std::vector> zmax(max_threads, {{-Math::kInfinity}}); std::vector> largest(max_threads, {{0}}); rm->ApplyOnAllElementsParallelDynamic(1000, [&](SimObject* so, SoHandle) { auto tid = omp_get_thread_num(); const auto& position = so->GetPosition(); // x if (position[0] < xmin[tid][0]) { xmin[tid][0] = position[0]; } if (position[0] > xmax[tid][0]) { xmax[tid][0] = position[0]; } // y if (position[1] < ymin[tid][0]) { ymin[tid][0] = position[1]; } if (position[1] > ymax[tid][0]) { ymax[tid][0] = position[1]; } // z if (position[2] < zmin[tid][0]) { zmin[tid][0] = position[2]; } if (position[2] > zmax[tid][0]) { zmax[tid][0] = position[2]; } // larget object auto diameter = so->GetDiameter(); if (diameter > largest[tid][0]) { largest[tid][0] = diameter; } }); // reduce partial results into global one double& gxmin = (*ret_grid_dimensions)[0]; double& gxmax = (*ret_grid_dimensions)[1]; double& gymin = (*ret_grid_dimensions)[2]; double& gymax = (*ret_grid_dimensions)[3]; double& gzmin = (*ret_grid_dimensions)[4]; double& gzmax = (*ret_grid_dimensions)[5]; for (int tid = 0; tid < max_threads; tid++) { // x if (xmin[tid][0] < gxmin) { gxmin = xmin[tid][0]; } if (xmax[tid][0] > gxmax) { gxmax = xmax[tid][0]; } // y if (ymin[tid][0] < gymin) { gymin = ymin[tid][0]; } if (ymax[tid][0] > gymax) { gymax = ymax[tid][0]; } // z if (zmin[tid][0] < gzmin) { gzmin = zmin[tid][0]; } if (zmax[tid][0] > gzmax) { gzmax = zmax[tid][0]; } // larget object if (largest[tid][0] > largest_object_size_) { largest_object_size_ = largest[tid][0]; } } } void RoundOffGridDimensions(const std::array& grid_dimensions) { assert(grid_dimensions_[0] > -9.999999999); assert(grid_dimensions_[2] > -9.999999999); assert(grid_dimensions_[4] > -9.999999999); assert(grid_dimensions_[1] < 80); assert(grid_dimensions_[3] < 80); assert(grid_dimensions_[5] < 80); grid_dimensions_[0] = floor(grid_dimensions[0]); grid_dimensions_[2] = floor(grid_dimensions[2]); grid_dimensions_[4] = floor(grid_dimensions[4]); grid_dimensions_[1] = ceil(grid_dimensions[1]); grid_dimensions_[3] = ceil(grid_dimensions[3]); grid_dimensions_[5] = ceil(grid_dimensions[5]); } /// @brief Gets the Moore (i.e adjacent) boxes of the query boxAlso adds /// the /// query box. /// /// @param[out] neighbor_boxes The neighbor boxes /// @param[in] box_idx The query box /// void GetMooreBoxes(FixedSizeVector* neighbor_boxes, size_t box_idx) const { neighbor_boxes->push_back(GetBoxPointer(box_idx)); // Adjacent 6 (top, down, left, right, front and back) if (adjacency_ >= kLow) { neighbor_boxes->push_back(GetBoxPointer(box_idx - num_boxes_xy_)); neighbor_boxes->push_back(GetBoxPointer(box_idx + num_boxes_xy_)); neighbor_boxes->push_back(GetBoxPointer(box_idx - num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx + num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx - 1)); neighbor_boxes->push_back(GetBoxPointer(box_idx + 1)); } // Adjacent 12 if (adjacency_ >= kMedium) { neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ - num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx - num_boxes_xy_ - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ - num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx + num_boxes_xy_ - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ + num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx - num_boxes_xy_ + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_axis_[0] + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ + num_boxes_axis_[0])); neighbor_boxes->push_back(GetBoxPointer(box_idx + num_boxes_xy_ + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_axis_[0] + 1)); } // Adjacent 8 if (adjacency_ >= kHigh) { neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ - num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ - num_boxes_axis_[0] + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ + num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx - num_boxes_xy_ + num_boxes_axis_[0] + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ - num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ - num_boxes_axis_[0] + 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ + num_boxes_axis_[0] - 1)); neighbor_boxes->push_back( GetBoxPointer(box_idx + num_boxes_xy_ + num_boxes_axis_[0] + 1)); } } /// @brief Gets the box indices of all adjacent boxes. Also adds the /// query box index. /// /// @param[out] box_indices Result containing all box indices /// @param[in] box_idx The query box /// void GetMooreBoxIndices(FixedSizeVector* box_indices, size_t box_idx) const { box_indices->push_back(box_idx); // Adjacent 6 (top, down, left, right, front and back) if (adjacency_ >= kLow) { box_indices->push_back(box_idx - num_boxes_xy_); box_indices->push_back(box_idx + num_boxes_xy_); box_indices->push_back(box_idx - num_boxes_axis_[0]); box_indices->push_back(box_idx + num_boxes_axis_[0]); box_indices->push_back(box_idx - 1); box_indices->push_back(box_idx + 1); } // Adjacent 12 if (adjacency_ >= kMedium) { box_indices->push_back(box_idx - num_boxes_xy_ - num_boxes_axis_[0]); box_indices->push_back(box_idx - num_boxes_xy_ - 1); box_indices->push_back(box_idx - num_boxes_axis_[0] - 1); box_indices->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0]); box_indices->push_back(box_idx + num_boxes_xy_ - 1); box_indices->push_back(box_idx + num_boxes_axis_[0] - 1); box_indices->push_back(box_idx - num_boxes_xy_ + num_boxes_axis_[0]); box_indices->push_back(box_idx - num_boxes_xy_ + 1); box_indices->push_back(box_idx - num_boxes_axis_[0] + 1); box_indices->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0]); box_indices->push_back(box_idx + num_boxes_xy_ + 1); box_indices->push_back(box_idx + num_boxes_axis_[0] + 1); } // Adjacent 8 if (adjacency_ >= kHigh) { box_indices->push_back(box_idx - num_boxes_xy_ - num_boxes_axis_[0] - 1); box_indices->push_back(box_idx - num_boxes_xy_ - num_boxes_axis_[0] + 1); box_indices->push_back(box_idx - num_boxes_xy_ + num_boxes_axis_[0] - 1); box_indices->push_back(box_idx - num_boxes_xy_ + num_boxes_axis_[0] + 1); box_indices->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0] - 1); box_indices->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0] + 1); box_indices->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0] - 1); box_indices->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0] + 1); } } /// Determines current box based on parameter box_idx and adds it together /// with half of the surrounding boxes to the vector. /// Legend: C = center, N = north, E = east, S = south, W = west, F = front, /// B = back /// For each box pair which is centro-symmetric only one box is taken -- /// e.g. E-W: E, or BNW-FSE: BNW /// NB: for the update mechanism using a CircularBuffer the order is /// important. /// /// (x-axis to the right \ y-axis up) /// z=1 /// +-----+----+-----+ /// | BNW | BN | BNE | /// +-----+----+-----+ /// | NW | N | NE | /// +-----+----+-----+ /// | FNW | FN | FNE | /// +-----+----+-----+ /// /// z = 0 /// +-----+----+-----+ /// | BW | B | BE | /// +-----+----+-----+ /// | W | C | E | /// +-----+----+-----+ /// | FW | F | FE | /// +-----+----+-----+ /// /// z = -1 /// +-----+----+-----+ /// | BSW | BS | BSE | /// +-----+----+-----+ /// | SW | S | SE | /// +-----+----+-----+ /// | FSW | FS | FSE | /// +-----+----+-----+ /// void GetHalfMooreBoxIndices(FixedSizeVector* neighbor_boxes, size_t box_idx) const { // C neighbor_boxes->push_back(box_idx); // BW neighbor_boxes->push_back(box_idx + num_boxes_axis_[0] - 1); // FNW neighbor_boxes->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0] - 1); // NW neighbor_boxes->push_back(box_idx + num_boxes_xy_ - 1); // BNW neighbor_boxes->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0] - 1); // B neighbor_boxes->push_back(box_idx + num_boxes_axis_[0]); // FN neighbor_boxes->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0]); // N neighbor_boxes->push_back(box_idx + num_boxes_xy_); // BN neighbor_boxes->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0]); // E neighbor_boxes->push_back(box_idx + 1); // BE neighbor_boxes->push_back(box_idx + num_boxes_axis_[0] + 1); // FNE neighbor_boxes->push_back(box_idx + num_boxes_xy_ - num_boxes_axis_[0] + 1); // NE neighbor_boxes->push_back(box_idx + num_boxes_xy_ + 1); // BNE neighbor_boxes->push_back(box_idx + num_boxes_xy_ + num_boxes_axis_[0] + 1); } /// @brief Gets the pointer to the box with the given index /// /// @param[in] index The index of the box /// /// @return The pointer to the box /// const Box* GetBoxPointer(size_t index) const { return &(boxes_[index]); } /// @brief Gets the pointer to the box with the given index /// /// @param[in] index The index of the box /// /// @return The pointer to the box /// Box* GetBoxPointer(size_t index) { return &(boxes_[index]); } /// Returns the box index in the one dimensional array based on box /// coordinates in space /// /// @param box_coord box coordinates in space (x, y, z) /// /// @return The box index. /// size_t GetBoxIndex(const std::array& box_coord) const { return box_coord[2] * num_boxes_xy_ + box_coord[1] * num_boxes_axis_[0] + box_coord[0]; } }; } // namespace bdm #endif // CORE_GRID_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_MODEL_INITIALIZER_H_ #define CORE_MODEL_INITIALIZER_H_ #include #include #include #include "core/container/math_array.h" #include "core/diffusion_grid.h" #include "core/resource_manager.h" #include "core/simulation.h" #include "core/util/random.h" namespace bdm { struct ModelInitializer { /// Creates a 3D cubic grid of simulation objects and adds them to the /// ResourceManager. Type of the simulation object is determined by the return /// type of parameter cell_builder. /// /// ModelInitializer::Grid3D(8, 10, [](const Double3& pos){ /// return Cell(pos); }); /// @param cells_per_dim number of simulation objects on each axis. /// Number of generated simulation objects = /// `cells_per_dim ^ 3` /// @param space space between the positions - e.g space = 10: /// positions = `{(0, 0, 0), (0, 0, 10), (0, 0, /// 20), ... }` /// @param cell_builder function containing the logic to instantiate a /// new simulation object. Takes `const /// Double3&` as input parameter /// template static void Grid3D(size_t cells_per_dim, double space, Function cell_builder) { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); rm->Reserve(cells_per_dim * cells_per_dim * cells_per_dim); for (size_t x = 0; x < cells_per_dim; x++) { auto x_pos = x * space; for (size_t y = 0; y < cells_per_dim; y++) { auto y_pos = y * space; for (size_t z = 0; z < cells_per_dim; z++) { auto* new_simulation_object = cell_builder({x_pos, y_pos, z * space}); rm->push_back(new_simulation_object); } } } } /// Creates a 3D grid of simulation objects and adds them to the /// ResourceManager. Type of the simulation object is determined by the return /// type of parameter cell_builder. /// /// ModelInitializer::Grid3D({8,6,4}, 10, [](const Double3& /// pos){ return Cell(pos); }); /// @param cells_per_dim number of simulation objects on each axis. /// Number of generated simulation objects = /// `cells_per_dim[0] * cells_per_dim[1] * /// cells_per_dim[2]` /// @param space space between the positions - e.g space = 10: /// positions = `{(0, 0, 0), (0, 0, 10), (0, 0, /// 20), ... }` /// @param cell_builder function containing the logic to instantiate a /// new simulation object. Takes `const /// Double3&` as input parameter /// template static void Grid3D(const std::array& cells_per_dim, double space, Function cell_builder) { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); rm->Reserve(cells_per_dim[0] * cells_per_dim[1] * cells_per_dim[2]); for (size_t x = 0; x < cells_per_dim[0]; x++) { auto x_pos = x * space; for (size_t y = 0; y < cells_per_dim[1]; y++) { auto y_pos = y * space; for (size_t z = 0; z < cells_per_dim[2]; z++) { auto* new_simulation_object = cell_builder({x_pos, y_pos, z * space}); rm->push_back(new_simulation_object); } } } } /// Adds simulation objects to the ResourceManager. Type of the simulation /// object is determined by the return type of parameter cell_builder. /// /// @param positions positions of the simulation objects to be /// @param cell_builder function containing the logic to instantiate a /// new simulation object. Takes `const /// Double3&` as input parameter /// template static void CreateCells(const std::vector& positions, Function cell_builder) { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); rm->Reserve(positions.size()); for (size_t i = 0; i < positions.size(); i++) { auto* new_simulation_object = cell_builder({positions[i][0], positions[i][1], positions[i][2]}); rm->push_back(new_simulation_object); } } /// Adds simulation objects with random positions to the ResourceManager. /// Type of the simulation object is determined by the return type of /// parameter cell_builder. /// /// @param[in] min The minimum position value /// @param[in] max The maximum position value /// @param[in] num_cells The number cells /// @param[in] cell_builder function containing the logic to instantiate a /// new simulation object. Takes `const /// Double3&` as input parameter /// template static void CreateCellsRandom(double min, double max, int num_cells, Function cell_builder) { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); rm->Reserve(num_cells); // TODO(ahmad): throughout simulation only one random number generator // should be used, so this should go someplace accessible for other // classes / functions auto* random = sim->GetRandom(); for (int i = 0; i < num_cells; i++) { double x = random->Uniform(min, max); double y = random->Uniform(min, max); double z = random->Uniform(min, max); auto* new_simulation_object = cell_builder({x, y, z}); rm->push_back(new_simulation_object); } } /// Allows cells to secrete the specified substance. Diffusion throughout the /// simulation space is automatically taken care of by the DiffusionGrid class /// /// @param[in] substance_id The substance identifier /// @param[in] substance_name The substance name /// @param[in] diffusion_coeff The diffusion coefficient /// @param[in] decay_constant The decay constant /// @param[in] resolution The resolution of the diffusion grid /// static void DefineSubstance(size_t substance_id, std::string substance_name, double diffusion_coeff, double decay_constant, int resolution = 10) { assert(resolution > 0 && "Resolution needs to be a positive integer value"); auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); DiffusionGrid* d_grid = new DiffusionGrid(substance_id, substance_name, diffusion_coeff, decay_constant, resolution); rm->AddDiffusionGrid(d_grid); } template static void InitializeSubstance(size_t substance_id, std::string substance_name, F function) { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); auto diffusion_grid = rm->GetDiffusionGrid(substance_id); diffusion_grid->AddInitializer(function); } }; } // namespace bdm #endif // CORE_MODEL_INITIALIZER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_BOUND_SPACE_OP_H_ #define CORE_OPERATION_BOUND_SPACE_OP_H_ #include "core/param/param.h" #include "core/sim_object/sim_object.h" #include "core/simulation.h" namespace bdm { inline void ApplyBoundingBox(SimObject* sim_object, double lb, double rb) { // Need to create a small distance from the positive edge of each dimension; // otherwise it will fall out of the boundary of the simulation space double eps = 1e-10; auto pos = sim_object->GetPosition(); bool updated = false; for (int i = 0; i < 3; i++) { if (pos[i] < lb) { pos[i] = lb; updated = true; } else if (pos[i] >= rb) { pos[i] = rb - eps; updated = true; } } if (updated) { sim_object->SetPosition(pos); } } /// Keeps the simulation objects contained within the bounds as defined in /// param.h class BoundSpace { public: BoundSpace() {} ~BoundSpace() {} void operator()(SimObject* sim_object) const { auto* param = Simulation::GetActive()->GetParam(); if (param->bound_space_) { ApplyBoundingBox(sim_object, param->min_bound_, param->max_bound_); } } }; } // namespace bdm #endif // CORE_OPERATION_BOUND_SPACE_OP_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DIFFUSION_OP_H_ #define CORE_OPERATION_DIFFUSION_OP_H_ #include #include #include #include "core/container/inline_vector.h" #include "core/diffusion_grid.h" #include "core/grid.h" #include "core/param/param.h" #include "core/resource_manager.h" #include "core/simulation.h" namespace bdm { /// A class that sets up diffusion grids of the substances in this simulation class DiffusionOp { public: DiffusionOp() {} virtual ~DiffusionOp() {} void operator()() { auto* sim = Simulation::GetActive(); auto* rm = sim->GetResourceManager(); auto* grid = sim->GetGrid(); auto* param = sim->GetParam(); rm->ApplyOnAllDiffusionGrids([&](DiffusionGrid* dg) { // Update the diffusion grid dimension if the neighbor grid dimensions // have changed. If the space is bound, we do not need to update the // dimensions, because these should not be changing anyway if (grid->HasGrown() && !param->bound_space_) { Log::Info("DiffusionOp", "Your simulation objects are getting near the edge of the " "simulation space. Be aware of boundary conditions that may " "come into play!"); dg->Update(grid->GetDimensionThresholds()); } if (param->leaking_edges_) { dg->DiffuseEulerLeakingEdge(); } else { dg->DiffuseEuler(); } if (param->calculate_gradients_) { dg->CalculateGradient(); } }); } }; } // namespace bdm #endif // CORE_OPERATION_DIFFUSION_OP_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DISPLACEMENT_OP_H_ #define CORE_OPERATION_DISPLACEMENT_OP_H_ #include #include "core/operation/displacement_op_cpu.h" #ifdef USE_CUDA #include "core/operation/displacement_op_cuda.h" #endif #if defined(USE_OPENCL) && !defined(__ROOTCLING__) #include "core/operation/displacement_op_opencl.h" #endif #include "core/grid.h" #include "core/param/param.h" #include "core/scheduler.h" #include "core/shape.h" #include "core/simulation.h" #include "core/util/log.h" namespace bdm { /// Defines the 3D physical interactions between physical objects class DisplacementOp { public: DisplacementOp() { // NB: check if there are non spherical shapes is not easily possible in the // dynamic solution. } ~DisplacementOp() {} bool UseCpu() const { auto* param = Simulation::GetActive()->GetParam(); return force_cpu_implementation_ || (!param->use_gpu_ && !param->use_opencl_); } void operator()() { auto* param = Simulation::GetActive()->GetParam(); if (param->use_gpu_ && !force_cpu_implementation_) { #if defined(USE_OPENCL) && !defined(__ROOTCLING__) if (param->use_opencl_) { opencl_(); } #endif #ifdef USE_CUDA if (!param->use_opencl_) { cuda_(); } #endif } } void operator()(SimObject* sim_object) { cpu_(sim_object); } private: /// Currently the gpu implementation only supports Spheres. /// If a simulation contains simulation objects with different shapes with /// GPU turned on, this wouldn't work. The CPU implementation should be used /// if this condition is detected. In this case `force_cpu_implementation_` /// will be set to true. bool force_cpu_implementation_ = false; DisplacementOpCpu cpu_; #ifdef USE_CUDA DisplacementOpCuda cuda_; // NOLINT #endif #if defined(USE_OPENCL) && !defined(__ROOTCLING__) DisplacementOpOpenCL opencl_; #endif }; } // namespace bdm #endif // CORE_OPERATION_DISPLACEMENT_OP_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DISPLACEMENT_OP_CPU_H_ #define CORE_OPERATION_DISPLACEMENT_OP_CPU_H_ #include #include #include #include #include "core/grid.h" #include "core/operation/bound_space_op.h" #include "core/param/param.h" #include "core/scheduler.h" #include "core/sim_object/sim_object.h" #include "core/simulation.h" #include "core/util/math.h" namespace bdm { class DisplacementOpCpu { public: DisplacementOpCpu() {} ~DisplacementOpCpu() {} void operator()(SimObject* sim_object) { auto* sim = Simulation::GetActive(); auto* scheduler = sim->GetScheduler(); auto* param = sim->GetParam(); if (!sim_object->RunDisplacement()) { return; } // update search radius and delta_time_ at beginning of each iteration auto current_iteration = scheduler->GetSimulatedSteps(); if (last_iteration_ != current_iteration) { last_iteration_ = current_iteration; auto* grid = sim->GetGrid(); auto search_radius = grid->GetLargestObjectSize(); squared_radius_ = search_radius * search_radius; auto current_time = (current_iteration + 1) * param->simulation_time_step_; delta_time_ = current_time - last_time_run_; last_time_run_ = current_time; } const auto& displacement = sim_object->CalculateDisplacement(squared_radius_, delta_time_); sim_object->ApplyDisplacement(displacement); if (param->bound_space_) { ApplyBoundingBox(sim_object, param->min_bound_, param->max_bound_); } } private: double squared_radius_ = 0; double last_time_run_ = 0; double delta_time_ = 0; uint64_t last_iteration_ = std::numeric_limits::max(); }; } // namespace bdm #endif // CORE_OPERATION_DISPLACEMENT_OP_CPU_H_ // // // ----------------------------------------------------------------------------- // // // // Copyright (C) The BioDynaMo Project. // // All Rights Reserved. // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // // // // See the LICENSE file distributed with this work for details. // // See the NOTICE file distributed with this work for additional information // // regarding copyright ownership. // // // // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DISPLACEMENT_OP_CUDA_H_ #define CORE_OPERATION_DISPLACEMENT_OP_CUDA_H_ #include #include "core/gpu/displacement_op_cuda_kernel.h" #include "core/operation/bound_space_op.h" #include "core/resource_manager.h" #include "core/shape.h" #include "core/sim_object/cell.h" #include "core/simulation.h" #include "core/util/log.h" #include "core/util/thread_info.h" #include "core/util/type.h" namespace bdm { /// Defines the 3D physical interactions between physical objects class DisplacementOpCuda { public: DisplacementOpCuda() {} ~DisplacementOpCuda() {} void IsNonSphericalObjectPresent(const SimObject* so, bool* answer) { if (so->GetShape() != Shape::kSphere) { *answer = true; } } void operator()() { auto* sim = Simulation::GetActive(); auto* grid = sim->GetGrid(); auto* param = sim->GetParam(); auto* rm = sim->GetResourceManager(); // Check the number of NUMA domains on the system. Currently only 1 is // supported for GPU execution. if (ThreadInfo::GetInstance()->GetNumaNodes() > 1) { Log::Fatal( "DisplacementOpCuda", "\nThe GPU execution only supports systems with 1 NUMA domain."); return; } uint32_t num_objects = rm->GetNumSimObjects(); // Cannot use Double3 here, because the `data()` function returns a const // pointer to the underlying array, whereas the CUDA kernal will cast it to // a void pointer. The conversion of `const double *` to `void *` is // illegal. std::vector> cell_movements(num_objects); std::vector cell_positions(num_objects); std::vector cell_diameters(num_objects); std::vector cell_adherence(num_objects); std::vector cell_tractor_force(num_objects); std::vector cell_boxid(num_objects); std::vector mass(num_objects); std::vector starts; std::vector lengths; std::vector successors(num_objects); uint32_t box_length; std::array num_boxes_axis; std::array grid_dimensions; double squared_radius = grid->GetLargestObjectSize() * grid->GetLargestObjectSize(); bool is_non_spherical_object = false; rm->ApplyOnAllElements([&](SimObject* so, SoHandle soh) { // Check if there are any non-spherical objects in our simulation, because // GPU accelerations currently supports only sphere-sphere interactions IsNonSphericalObjectPresent(so, &is_non_spherical_object); if (is_non_spherical_object) { Log::Fatal("DisplacementOpCuda", "\nWe detected a non-spherical object during the GPU " "execution. This is currently not supported."); return; } auto* cell = bdm_static_cast(so); auto idx = soh.GetElementIdx(); mass[idx] = cell->GetMass(); cell_diameters[idx] = cell->GetDiameter(); cell_adherence[idx] = cell->GetAdherence(); cell_tractor_force[idx] = cell->GetTractorForce(); cell_positions[idx] = cell->GetPosition(); cell_boxid[idx] = cell->GetBoxIdx(); }); uint16_t numa_node = 0; // GPU code only supports 1 NUMA domain currently for (size_t i = 0; i < grid->successors_.size(numa_node); i++) { auto sh = SoHandle(numa_node, i); successors[i] = grid->successors_[sh].GetElementIdx(); } starts.resize(grid->boxes_.size()); lengths.resize(grid->boxes_.size()); size_t i = 0; for (auto& box : grid->boxes_) { starts[i] = box.start_.GetElementIdx(); lengths[i] = box.length_; i++; } grid->GetGridInfo(&box_length, &num_boxes_axis, &grid_dimensions); // If this is the first time we perform physics on GPU using CUDA if (cdo_ == nullptr) { // Allocate 25% more memory so we don't need to reallocate GPU memory // for every (small) change uint32_t new_num_objects = static_cast(1.25 * num_objects); uint32_t new_num_boxes = static_cast(1.25 * starts.size()); // Store these extended buffer sizes for future reference num_objects_ = new_num_objects; num_boxes_ = new_num_boxes; // Allocate required GPU memory cdo_ = new DisplacementOpCudaKernel(new_num_objects, new_num_boxes); } else { // If the number of simulation objects increased if (num_objects >= num_objects_) { Log::Info("DisplacementOpCuda", "\nThe number of cells increased signficantly (from ", num_objects_, " to ", num_objects, "), so we allocate bigger GPU buffers\n"); uint32_t new_num_objects = static_cast(1.25 * num_objects); num_objects_ = new_num_objects; cdo_->ResizeCellBuffers(new_num_objects); } // If the neighbor grid size increased if (starts.size() >= num_boxes_) { Log::Info("DisplacementOpCuda", "\nThe number of boxes increased signficantly (from ", num_boxes_, " to ", "), so we allocate bigger GPU buffers\n"); uint32_t new_num_boxes = static_cast(1.25 * starts.size()); num_boxes_ = new_num_boxes; cdo_->ResizeGridBuffers(new_num_boxes); } } cdo_->LaunchDisplacementKernel( cell_positions.data()->data(), cell_diameters.data(), cell_tractor_force.data()->data(), cell_adherence.data(), cell_boxid.data(), mass.data(), &(param->simulation_time_step_), &(param->simulation_max_displacement_), &squared_radius, &num_objects, starts.data(), lengths.data(), successors.data(), &box_length, num_boxes_axis.data(), grid_dimensions.data(), cell_movements.data()->data()); // set new positions after all updates have been calculated // otherwise some cells would see neighbors with already updated positions // which would lead to inconsistencies rm->ApplyOnAllElements([&](SimObject* so, SoHandle soh) { auto* cell = dynamic_cast(so); auto idx = soh.GetElementIdx(); Double3 new_pos; new_pos[0] = cell_movements[idx][0]; new_pos[1] = cell_movements[idx][1]; new_pos[2] = cell_movements[idx][2]; cell->UpdatePosition(new_pos); if (param->bound_space_) { ApplyBoundingBox(so, param->min_bound_, param->max_bound_); } }); } private: DisplacementOpCudaKernel* cdo_ = nullptr; uint32_t num_boxes_ = 0; uint32_t num_objects_ = 0; }; } // namespace bdm #endif // CORE_OPERATION_DISPLACEMENT_OP_CUDA_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DISPLACEMENT_OP_OPENCL_H_ #define CORE_OPERATION_DISPLACEMENT_OP_OPENCL_H_ #if defined(USE_OPENCL) && !defined(__ROOTCLING__) #include #include "core/gpu/opencl_state.h" #include "core/grid.h" #include "core/operation/bound_space_op.h" #include "core/shape.h" #include "core/sim_object/cell.h" #include "core/util/thread_info.h" #include "core/util/type.h" namespace bdm { /// Defines the 3D physical interactions between physical objects class DisplacementOpOpenCL { public: DisplacementOpOpenCL() {} ~DisplacementOpOpenCL() {} void IsNonSphericalObjectPresent(const SimObject* so, bool* answer) { if (so->GetShape() != Shape::kSphere) { *answer = true; } } void operator()() { auto* sim = Simulation::GetActive(); auto* grid = sim->GetGrid(); auto* param = sim->GetParam(); auto* rm = sim->GetResourceManager(); // Check the number of NUMA domains on the system. Currently only 1 is // supported for GPU execution. if (ThreadInfo::GetInstance()->GetNumaNodes() > 1) { Log::Fatal( "DisplacementOpOpenCL", "\nThe GPU execution only supports systems with 1 NUMA domain."); return; } uint32_t num_objects = rm->GetNumSimObjects(); auto* ocl_state = OpenCLState::GetInstance(); auto context = ocl_state->GetOpenCLContext(); auto queue = ocl_state->GetOpenCLCommandQueue(); auto programs = ocl_state->GetOpenCLProgramList(); // Cannot use Double3 here, because the `data()` function returns a const // pointer to the underlying array, whereas the CUDA kernal will cast it to // a void pointer. The conversion of `const double *` to `void *` is // illegal. std::vector> cell_movements(num_objects); std::vector> cell_positions(num_objects); std::vector cell_diameters(num_objects); std::vector cell_adherence(num_objects); std::vector> cell_tractor_force(num_objects); std::vector cell_boxid(num_objects); std::vector mass(num_objects); std::vector starts; std::vector lengths; std::vector successors(num_objects); cl_uint box_length; std::array num_boxes_axis; std::array grid_dimensions; cl_double squared_radius = grid->GetLargestObjectSize() * grid->GetLargestObjectSize(); bool is_non_spherical_object = false; rm->ApplyOnAllElements([&](SimObject* so, SoHandle soh) { // Check if there are any non-spherical objects in our simulation, because // GPU accelerations currently supports only sphere-sphere interactions IsNonSphericalObjectPresent(so, &is_non_spherical_object); if (is_non_spherical_object) { Log::Fatal("DisplacementOpOpenCL", "\nWe detected a non-spherical object during the GPU " "execution. This is currently not supported."); return; } auto* cell = bdm_static_cast(so); auto idx = soh.GetElementIdx(); mass[idx] = cell->GetMass(); cell_diameters[idx] = cell->GetDiameter(); cell_adherence[idx] = cell->GetAdherence(); cell_boxid[idx] = cell->GetBoxIdx(); cell_tractor_force[idx][0] = cell->GetTractorForce()[0]; cell_tractor_force[idx][1] = cell->GetTractorForce()[1]; cell_tractor_force[idx][2] = cell->GetTractorForce()[2]; cell_positions[idx][0] = cell->GetPosition()[0]; cell_positions[idx][1] = cell->GetPosition()[1]; cell_positions[idx][2] = cell->GetPosition()[2]; }); uint16_t numa_node = 0; // GPU code only supports 1 NUMA domain currently for (size_t i = 0; i < grid->successors_.size(numa_node); i++) { auto sh = SoHandle(numa_node, i); successors[i] = grid->successors_[sh].GetElementIdx(); } starts.resize(grid->boxes_.size()); lengths.resize(grid->boxes_.size()); size_t i = 0; for (auto& box : grid->boxes_) { starts[i] = box.start_.GetElementIdx(); lengths[i] = box.length_; i++; } grid->GetGridInfo(&box_length, &num_boxes_axis, &grid_dimensions); // Allocate GPU buffers cl::Buffer positions_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * 3 * sizeof(cl_double), cell_positions.data()->data()); cl::Buffer diameters_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * sizeof(cl_double), cell_diameters.data()); cl::Buffer tractor_force_arg( *context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * 3 * sizeof(cl_double), cell_tractor_force.data()->data()); cl::Buffer adherence_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * sizeof(cl_double), cell_adherence.data()); cl::Buffer box_id_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * sizeof(cl_uint), cell_boxid.data()); cl::Buffer mass_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, num_objects * sizeof(cl_double), mass.data()); cl::Buffer cell_movements_arg( *context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, num_objects * 3 * sizeof(cl_double), cell_movements.data()->data()); cl::Buffer starts_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, starts.size() * sizeof(cl_uint), starts.data()); cl::Buffer lengths_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, lengths.size() * sizeof(cl_short), lengths.data()); cl::Buffer successors_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, successors.size() * sizeof(cl_uint), successors.data()); cl::Buffer nba_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, 3 * sizeof(cl_uint), num_boxes_axis.data()); cl::Buffer gd_arg(*context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, 3 * sizeof(cl_int), grid_dimensions.data()); // Create the kernel object from our program // TODO(ahmad): generalize the program selection, in case we have more than // one. We can maintain an unordered map of programs maybe cl::Kernel collide((*programs)[0], "collide"); // Set kernel parameters collide.setArg(0, positions_arg); collide.setArg(1, diameters_arg); collide.setArg(2, tractor_force_arg); collide.setArg(3, adherence_arg); collide.setArg(4, box_id_arg); collide.setArg(5, mass_arg); collide.setArg(6, param->simulation_time_step_); collide.setArg(7, param->simulation_max_displacement_); collide.setArg(8, squared_radius); collide.setArg(9, static_cast(num_objects)); collide.setArg(10, starts_arg); collide.setArg(11, lengths_arg); collide.setArg(12, successors_arg); collide.setArg(13, box_length); collide.setArg(14, nba_arg); collide.setArg(15, gd_arg); collide.setArg(16, cell_movements_arg); // The amount of threads for each work group (similar to CUDA thread block) int block_size = 256; try { // The global size determines the total number of threads that will be // spawned on the GPU, in groups of local_size cl::NDRange global_size = cl::NDRange(num_objects + (block_size - (num_objects % block_size))); cl::NDRange local_size = cl::NDRange(block_size); queue->enqueueNDRangeKernel(collide, cl::NullRange, global_size, local_size); } catch (const cl::Error& err) { Log::Error("DisplacementOpOpenCL", err.what(), "(", err.err(), ") = ", ocl_state->GetErrorString(err.err())); throw; } try { queue->enqueueReadBuffer(cell_movements_arg, CL_TRUE, 0, num_objects * 3 * sizeof(cl_double), cell_movements.data()->data()); } catch (const cl::Error& err) { Log::Error("DisplacementOpOpenCL", err.what(), "(", err.err(), ") = ", ocl_state->GetErrorString(err.err())); throw; } // set new positions after all updates have been calculated // otherwise some cells would see neighbors with already updated positions // which would lead to inconsistencies rm->ApplyOnAllElements([&](SimObject* so, SoHandle soh) { auto* cell = dynamic_cast(so); auto idx = soh.GetElementIdx(); Double3 new_pos; new_pos[0] = cell_movements[idx][0]; new_pos[1] = cell_movements[idx][1]; new_pos[2] = cell_movements[idx][2]; cell->UpdatePosition(new_pos); if (param->bound_space_) { ApplyBoundingBox(so, param->min_bound_, param->max_bound_); } }); } }; } // namespace bdm #endif // defined(USE_OPENCL) && !defined(__ROOTCLING__) #endif // CORE_OPERATION_DISPLACEMENT_OP_OPENCL_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_DIVIDING_CELL_OP_H_ #define CORE_OPERATION_DIVIDING_CELL_OP_H_ #include #include "core/sim_object/cell.h" #include "core/sim_object/sim_object.h" #include "core/simulation.h" namespace bdm { class DividingCellOp { public: DividingCellOp() {} ~DividingCellOp() {} void operator()(SimObject* sim_object) const { if (Cell* cell = dynamic_cast(sim_object)) { if (cell->GetDiameter() <= 40) { cell->ChangeVolume(300); } else { cell->Divide(); } } } }; } // namespace bdm #endif // CORE_OPERATION_DIVIDING_CELL_OP_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_OP_TIMER_H_ #define CORE_OPERATION_OP_TIMER_H_ #include #include "core/simulation.h" #include "core/util/timing.h" namespace bdm { /// \brief Decorator for `Operations` to measure runtime template struct OpTimer { explicit OpTimer(std::string timer_msg) : timer_msg_(timer_msg) {} explicit OpTimer(std::string timer_msg, const TOp& op) : timer_msg_(timer_msg), operation_(op) {} template void operator()(Container* cells, uint16_t numa_node, uint16_t type_idx) { auto* param = Simulation::GetActive()->GetParam(); if (param->statistics_) { Timing timer(timer_msg_, &gStatistics); operation_(cells, numa_node, type_idx); } else { operation_(cells, numa_node, type_idx); } } TOp* operator->() { return &operation_; } const TOp* operator->() const { return &operation_; } private: std::string timer_msg_; TOp operation_; }; } // namespace bdm #endif // CORE_OPERATION_OP_TIMER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_OPERATION_H_ #define CORE_OPERATION_OPERATION_H_ #include #include namespace bdm { class SimObject; /// An Operation contains a function that will be executed for each simulation /// object. It's data member `frequency_` specifies how often it will be /// executed (every simulation step, every second, ...). struct Operation { using FunctionType = std::function; Operation(); Operation(const std::string& name, const FunctionType& f); Operation(const std::string& name, uint32_t frequency, const FunctionType& f); void operator()(SimObject* so) const; /// Specifies how often this operation will be executed.\n /// 1: every timestep\n /// 2: every second timestep\n /// ... uint32_t frequency_ = 1; /// Operation name / unique identifier std::string name_; private: FunctionType function_; }; } // namespace bdm #endif // CORE_OPERATION_OPERATION_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_OPERATION_VTUNE_OP_WRAPPER_H_ #define CORE_OPERATION_VTUNE_OP_WRAPPER_H_ #include "core/util/vtune.h" namespace bdm { template class VTuneOpWrapper : public Op { public: /// perfect forwarding ctor template explicit VTuneOpWrapper(Args&&... args) : Op{std::forward(args)...} { domain_ = __itt_domain_create("MyTraces.MyDomain"); task_ = __itt_string_handle_create(typeid(Op).name()); } template void Compute(daosoa* cells) const { __itt_task_begin(domain_, __itt_null, __itt_null, task_); Op::Compute(cells); __itt_task_end(domain_); } private: __itt_domain* domain_; __itt_string_handle* task_; }; } // namespace bdm #endif // CORE_OPERATION_VTUNE_OP_WRAPPER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_PARAM_COMMAND_LINE_OPTIONS_H_ #define CORE_PARAM_COMMAND_LINE_OPTIONS_H_ #include "cxxopts.h" #include "TError.h" #include "core/simulation.h" #include "version.h" #include #include #include namespace bdm { /// Class to contain and parse command line options class CommandLineOptions { public: CommandLineOptions(int argc, const char** argv); ~CommandLineOptions(); /// Add an extra command line option cxxopts::OptionAdder AddOption(std::string group = "Simulation"); /// Add an extra command line option template void AddOption(std::string opt, std::string def, std::string description = "", std::string group = "Simulation") { AddOption(group)(opt, description, cxxopts::value()->default_value(def)); } /// Return the simulation name that was parsed from argv[0] std::string GetSimulationName(); template T Get(std::string val) { if (parser_ == nullptr) { this->Parse(); } return parser_->Get(val); } private: void AddCoreOptions(); /// Parse the options with the given command line arguments void Parse(); /// Return only the executable name given the path /// @param path path and filename of the executable /// e.g. `executable`, `./executable`, './build/executable' /// @return `executable` void ExtractSimulationName(const char* path); /// Takes care of core options void HandleCoreOptions(); int argc_; const char** argv_; // The name of the simulation std::string sim_name_; // Flag to determine if new options were added bool first_parse_ = true; cxxopts::Options options_; cxxopts::ParseResult* parser_ = nullptr; }; } // namespace bdm #endif // CORE_PARAM_COMMAND_LINE_OPTIONS_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_PARAM_MODULE_PARAM_H_ #define CORE_PARAM_MODULE_PARAM_H_ #include "core/util/root.h" #include "cpptoml/cpptoml.h" namespace bdm { struct Param; using ModuleParamUid = uint64_t; // TODO(lukas) code duplication with `UniqueEventIdFactory` /// This class generates unique ids for module parameters. Thread safe. class ModuleParamUidGenerator { public: ModuleParamUidGenerator(const ModuleParamUidGenerator&) = delete; static ModuleParamUidGenerator* Get(); ModuleParamUid NewUid(); private: ModuleParamUidGenerator(); std::atomic counter_; }; /// Interface for module parameters. struct ModuleParam { virtual ~ModuleParam(); virtual ModuleParam* GetCopy() const = 0; virtual ModuleParamUid GetUid() const = 0; protected: /// Assign values from config file to variables virtual void AssignFromConfig(const std::shared_ptr&) = 0; private: friend struct Param; BDM_CLASS_DEF(ModuleParam, 1); }; } // namespace bdm #endif // CORE_PARAM_MODULE_PARAM_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_PARAM_PARAM_H_ #define CORE_PARAM_PARAM_H_ #include #include #include #include #include #include #include "core/param/module_param.h" #include "core/util/root.h" #include "core/util/type.h" #include "cpptoml/cpptoml.h" namespace bdm { class Simulation; struct Param { static void RegisterModuleParam(ModuleParam* param); Param(); ~Param(); void Restore(Param&& other); template const TModuleParam* GetModuleParam() const { assert(modules_.find(TModuleParam::kUid) != modules_.end() && "Couldn't find the requested module parameter."); return bdm_static_cast( modules_.at(TModuleParam::kUid)); } template TModuleParam* GetModuleParam() { assert(modules_.find(TModuleParam::kUid) != modules_.end() && "Couldn't find the requested module parameter."); return bdm_static_cast(modules_.at(TModuleParam::kUid)); } // simulation values --------------------------------------------------------- /// Variable which specifies method using for solving differential equation /// {"Euler", "RK4"}. enum NumericalODESolver { kEuler = 1, kRK4 = 2 }; NumericalODESolver numerical_ode_solver_ = NumericalODESolver::kEuler; /// Ouput Directory name used to store visualization and other files.\n /// Path is relative to working directory.\n /// Default value: `"output"`\n /// TOML config file: /// /// [simulation] /// ouput_dir = "output" std::string output_dir_ = "output"; /// Backup file name for full simulation backups\n /// Path is relative to working directory.\n /// Default value: `""` (no backups will be made)\n /// TOML config file: /// /// [simulation] /// backup_file = /.root /// Command line argument: `-b, --backup` std::string backup_file_ = ""; /// File name to restore simulation from\n /// Path is relative to working directory.\n /// Default value: `""` (no restore will be made)\n /// TOML config file: /// /// [simulation] /// restore_file = /.root /// Command line argument: `-r, --restore` std::string restore_file_ = ""; /// Specifies the interval (in seconds) in which backups will be performed.\n /// Default Value: `1800` (every half an hour)\n /// TOML config file: /// /// [simulation] /// backup_interval = 1800 # backup every half an hour uint32_t backup_interval_ = 1800; /// Time between two simulation steps, in hours. /// Default value: `0.01`\n /// TOML config file: /// /// [simulation] /// time_step = 0.0125 double simulation_time_step_ = 0.01; /// Maximum jump that a point mass can do in one time step. Useful to /// stabilize the simulation\n /// Default value: `3.0`\n /// TOML config file: /// /// [simulation] /// max_displacement = 3.0 double simulation_max_displacement_ = 3.0; /// Calculate mechanical interactions between simulation objects.\n /// Default value: `true`\n /// TOML config file: /// /// [simulation] /// run_mechanical_interactions = true bool run_mechanical_interactions_ = true; /// Enforce an artificial cubic bounds around the simulation space. /// Simulation objects cannot move outside this cube. Dimensions of this cube /// are determined by parameter `lbound` and `rbound`.\n /// Default value: `false` (simulation space is "infinite")\n /// TOML config file: /// /// [simulation] /// bound_space = false bool bound_space_ = false; /// Minimum allowed value for x-, y- and z-position if simulation space is /// bound (@see `bound_space_`).\n /// Default value: `0`\n /// TOML config file: /// /// [simulation] /// min_bound = 0 double min_bound_ = 0; /// Maximum allowed value for x-, y- and z-position if simulation space is /// bound (@see `bound_space_`).\n /// Default value: `100`\n /// TOML config file: /// /// [simulation] /// max_bound = 100 double max_bound_ = 100; /// Allow substances to leak out of the simulation space. In this way /// the substance concentration will not be blocked by an artificial border\n /// Default value: `true`\n /// TOML config file: /// /// [simulation] /// leaking_edges = true bool leaking_edges_ = true; /// Calculate the diffusion gradient for each substance.\n /// TOML config file: /// /// [simulation] /// calculate_gradients = true bool calculate_gradients_ = true; // visualization values ------------------------------------------------------ /// Use ParaView Catalyst for live visualization.\n /// Default value: `false`\n /// TOML config file: /// /// [visualization] /// live = false bool live_visualization_ = false; /// Write data to file for post-simulation visualization /// Default value: `false`\n /// TOML config file: /// /// [visualization] /// export = false bool export_visualization_ = false; /// Use ROOT for enable visualization.\n /// Default value: `false`\n /// TOML config file: /// /// [visualization] /// root = false bool root_visualization_ = false; /// If `export_visualization_` is set to true, this parameter specifies /// how often it should be exported. 1 = every timestep, 10: every 10 /// time steps.\n /// Default value: `1`\n /// TOML config file: /// /// [visualization] /// export_interval = 1 uint32_t visualization_export_interval_ = 1; /// If `export_visualization_` is set to true, this parameter specifies /// if the ParaView pvsm file will be generated!\n /// Default value: `true`\n /// TOML config file: /// /// [visualization] /// export_generate_pvsm = true bool visualization_export_generate_pvsm_ = true; /// Specifies which simulation objects should be visualized. \n /// Every simulation object defines the minimum set of data members which /// are required to visualize it. (e.g. Cell: `position_` and `diameter_`).\n /// With this parameter it is also possible to extend the number of data /// members that are sent to the visualization engine. /// Default value: empty (no simulation object will be visualized)\n /// NB: This data member is not backed up, due to a ROOT error. /// TOML config file: /// /// [visualization] /// # turn on live or export /// export = true /// /// [[visualize_sim_object]] /// name = "Cell" /// # the following entry is optional /// additional_data_members = [ "density_" ] /// /// # The former block can be repeated for further simulation objects /// [[visualize_sim_object]] /// name = "Neurite" std::unordered_map> visualize_sim_objects_; //! struct VisualizeDiffusion { std::string name_; bool concentration_ = true; bool gradient_ = false; }; /// Spceifies for which substances extracellular diffusion should be /// visualized.\n /// Default value: empty (no diffusion will be visualized)\n /// TOML config file: /// /// [visualization] /// # turn on live or export /// export = true /// /// [[visualize_diffusion]] /// # Name of the substance /// name = "Na" /// # the following two entries are optional /// # default value for concentration is true /// concentration = true /// # default value for gradient is false /// gradient = false /// /// # The former block can be repeated for further substances /// [[visualize_diffusion]] /// name = "K" /// # default values: concentration = true and gradient = false std::vector visualize_diffusion_; // performance values -------------------------------------------------------- /// Batch size used by the `Scheduler` to iterate over simulation objects\n /// Default value: `1000`\n /// TOML config file: /// /// [performance] /// scheduling_batch_size = 1000 uint64_t scheduling_batch_size_ = 1000; /// Calculation of the displacement (mechanical interaction) is an /// expensive operation. If simulation objects do not move or grow, /// displacement calculation is ommited if detect_static_sim_objects is turned /// on. However, the detection mechanism introduces an overhead. For dynamic /// simulations where sim objects move and grow, the overhead outweighs the /// benefits.\n /// Default value: `false`\n /// TOML config file: /// /// [performance] /// detect_static_sim_objects = false bool detect_static_sim_objects_ = false; /// Neighbors of a simulation object can be cached so to avoid consecutive /// searches. This of course only makes sense if there is more than one /// `ForEachNeighbor*` operation.\n /// Default value: `false`\n /// TOML config file: /// /// [performance] /// cache_neighbors = false bool cache_neighbors_ = false; // development values -------------------------------------------------------- /// Statistics of profiling data; keeps track of the execution time of each /// operation at every timestep.\n /// Default Value: `false`\n /// TOML config file: /// /// [development] /// statistics = false bool statistics_ = false; /// Output debugging info related to running on NUMA architecture.\n /// \see `ThreadInfo`, `ResourceManager::DebugNuma` /// Default Value: `false`\n /// TOML config file: /// /// [development] /// debug_numa = false bool debug_numa_ = false; /// Use the python script (simple_pipeline.py) to do Live Visualization with /// ParaView. If false, we use the C++ pipeline /// Default value: `false`\n /// TOML config file: /// [development] /// python_catalyst_pipeline_ = false bool python_catalyst_pipeline_ = false; /// Display the current simulation step in the terminal output /// Default value: `true`\n /// TOML config file: /// [development] /// show_simulation_step = true bool show_simulation_step_ = true; /// Sets the frequency at which the current simulation step is displayed. /// Display every `simulation_step_freq_` steps. /// Default value: `10`\n /// TOML config file: /// [development] /// simulation_step_freq = false uint32_t simulation_step_freq_ = 10; // --------------------------------------------------------------------------- // experimental group /// Run the simulation partially on the GPU for improved performance. /// Default value: `false`\n /// TOML config file: /// [experimental] /// use_gpu = false bool use_gpu_ = false; /// When both CUDA and OpenCL are available on a machine, the preference to /// OpenCL can be set with this flag, as per default CUDA is used. /// Default value: `false`\n /// TOML config file: /// [experimental] /// use_opencl = false bool use_opencl_ = false; /// Compile OpenCL kernels with debugging symbols, for debugging on CPU /// targets with GNU gdb. /// Default value: `false`\n /// TOML config file: /// [experimental] /// opencl_debug_ = false bool opencl_debug_ = false; /// Set the index of the preferred GPU you wish to use. /// Default value: `0`\n /// TOML config file: /// [experimental] /// preferred_gpu = 0 int preferred_gpu_ = 0; protected: /// Assign values from config file to variables void AssignFromConfig(const std::shared_ptr&); private: friend class Simulation; static std::unordered_map> registered_modules_; std::unordered_map modules_; BDM_CLASS_DEF_NV(Param, 1); }; } // namespace bdm #endif // CORE_PARAM_PARAM_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_RESOURCE_MANAGER_H_ #define CORE_RESOURCE_MANAGER_H_ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_OPENCL #ifdef __APPLE__ #define CL_HPP_ENABLE_EXCEPTIONS #define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY #define CL_HPP_MINIMUM_OPENCL_VERSION 120 #define CL_HPP_TARGET_OPENCL_VERSION 120 #include "cl2.hpp" #else #define __CL_ENABLE_EXCEPTIONS #include #endif #endif #include "core/diffusion_grid.h" #include "core/sim_object/sim_object.h" #include "core/sim_object/so_uid.h" #include "core/simulation.h" #include "core/util/numa.h" #include "core/util/root.h" #include "core/util/thread_info.h" #include "core/util/type.h" namespace bdm { /// Unique identifier of a simulation object. Acts as a type erased pointer. /// Has the same type for every simulation object. \n /// Points to the storage location of a sim object inside ResourceManager.\n /// The id is split into two parts: Numa node, element index. /// The first one is used to obtain the numa storage, and the second specifies /// the element within this vector. class SoHandle { public: using NumaNode_t = uint16_t; using ElementIdx_t = uint32_t; constexpr SoHandle() noexcept : numa_node_(std::numeric_limits::max()), element_idx_(std::numeric_limits::max()) {} explicit SoHandle(ElementIdx_t element_idx) : numa_node_(0), element_idx_(element_idx) {} SoHandle(NumaNode_t numa_node, ElementIdx_t element_idx) : numa_node_(numa_node), element_idx_(element_idx) {} NumaNode_t GetNumaNode() const { return numa_node_; } ElementIdx_t GetElementIdx() const { return element_idx_; } void SetElementIdx(ElementIdx_t element_idx) { element_idx_ = element_idx; } bool operator==(const SoHandle& other) const { return numa_node_ == other.numa_node_ && element_idx_ == other.element_idx_; } bool operator!=(const SoHandle& other) const { return !(*this == other); } bool operator<(const SoHandle& other) const { if (numa_node_ == other.numa_node_) { return element_idx_ < other.element_idx_; } else { return numa_node_ < other.numa_node_; } } friend std::ostream& operator<<(std::ostream& stream, const SoHandle& handle) { stream << "Numa node: " << handle.numa_node_ << " element idx: " << handle.element_idx_; return stream; } private: NumaNode_t numa_node_; /// changed element index to uint32_t after issues with std::atomic with /// size 16 -> max element_idx: 4.294.967.296 ElementIdx_t element_idx_; BDM_CLASS_DEF_NV(SoHandle, 1); }; /// ResourceManager stores simulation objects and diffusion grids and provides /// methods to add, remove, and access them. Sim objects are uniquely identified /// by their SoUid, and SoHandle. A SoHandle might change during the simulation. class ResourceManager { public: explicit ResourceManager(TRootIOCtor* r) {} /// Default constructor. Unfortunately needs to be public although it is /// a singleton to be able to use ROOT I/O ResourceManager() { // Must be called prior any other function call to libnuma if (auto ret = numa_available() == -1) { Log::Fatal("ResourceManager", "Call to numa_available failed with return code: ", ret); } sim_objects_.resize(numa_num_configured_nodes()); } virtual ~ResourceManager() { for (auto& el : diffusion_grids_) { delete el.second; } for (auto& numa_sos : sim_objects_) { for (auto* so : numa_sos) { delete so; } } } ResourceManager& operator=(ResourceManager&& other) { if (sim_objects_.size() != other.sim_objects_.size()) { Log::Fatal( "Restored ResourceManager has different number of NUMA nodes."); } for (auto& el : diffusion_grids_) { delete el.second; } for (auto& numa_sos : sim_objects_) { for (auto* so : numa_sos) { delete so; } } sim_objects_ = std::move(other.sim_objects_); diffusion_grids_ = std::move(other.diffusion_grids_); RestoreUidSoMap(); return *this; } void RestoreUidSoMap() { // rebuild uid_soh_map_ uid_soh_map_.clear(); for (unsigned n = 0; n < sim_objects_.size(); ++n) { for (unsigned i = 0; i < sim_objects_[n].size(); ++i) { auto* so = sim_objects_[n][i]; this->uid_soh_map_[so->GetUid()] = SoHandle(n, i); } } } SimObject* GetSimObject(SoUid uid) { auto search_it = uid_soh_map_.find(uid); if (search_it == uid_soh_map_.end()) { return nullptr; } SoHandle soh = search_it->second; return sim_objects_[soh.GetNumaNode()][soh.GetElementIdx()]; } SimObject* GetSimObjectWithSoHandle(SoHandle soh) { return sim_objects_[soh.GetNumaNode()][soh.GetElementIdx()]; } SoHandle GetSoHandle(SoUid uid) { return uid_soh_map_[uid]; } void AddDiffusionGrid(DiffusionGrid* dgrid) { uint64_t substance_id = dgrid->GetSubstanceId(); auto search = diffusion_grids_.find(substance_id); if (search != diffusion_grids_.end()) { Log::Fatal("ResourceManager::AddDiffusionGrid", "You tried to add a diffusion grid with an already existing " "substance id. Please choose a different substance id."); } else { diffusion_grids_[substance_id] = dgrid; } } void RemoveDiffusionGrid(size_t substance_id) { auto search = diffusion_grids_.find(substance_id); if (search != diffusion_grids_.end()) { delete search->second; diffusion_grids_.erase(search); } else { Log::Fatal("ResourceManager::AddDiffusionGrid", "You tried to remove a diffusion grid that does not exist."); } } /// Return the diffusion grid which holds the substance of specified id DiffusionGrid* GetDiffusionGrid(size_t substance_id) const { assert(substance_id < diffusion_grids_.size() && "You tried to access a diffusion grid that does not exist!"); return diffusion_grids_.at(substance_id); } /// Return the diffusion grid which holds the substance of specified name /// Caution: using this function in a tight loop will result in a slow /// simulation. Use `GetDiffusionGrid(size_t)` in those cases. DiffusionGrid* GetDiffusionGrid(std::string substance_name) const { for (auto& el : diffusion_grids_) { auto& dg = el.second; if (dg->GetSubstanceName() == substance_name) { return dg; } } assert(false && "You tried to access a diffusion grid that does not exist! " "Did you specify the correct substance name?"); return nullptr; } /// Execute the given functor for all diffusion grids /// rm->ApplyOnAllDiffusionGrids([](DiffusionGrid* dgrid) { /// ... /// }); template void ApplyOnAllDiffusionGrids(TFunctor&& f) const { for (auto& el : diffusion_grids_) { f(el.second); } } /// Returns the total number of simulation objects if numa_node == -1 /// Otherwise the number of sim_objects in the specific numa node size_t GetNumSimObjects(int numa_node = -1) const { if (numa_node == -1) { size_t num_so = 0; for (auto& numa_sos : sim_objects_) { num_so += numa_sos.size(); } return num_so; } else { return sim_objects_[numa_node].size(); } } /// Apply a function on all elements in every container /// @param function that will be called with each container as a parameter /// /// rm->ApplyOnAllElements([](SimObject* element) { /// std::cout << *element << std::endl; /// }); void ApplyOnAllElements(const std::function& function) { for (auto& numa_sos : sim_objects_) { for (auto* so : numa_sos) { function(so); } } } void ApplyOnAllElements( const std::function& function) { for (uint64_t n = 0; n < sim_objects_.size(); ++n) { auto& numa_sos = sim_objects_[n]; for (uint64_t i = 0; i < numa_sos.size(); ++i) { function(numa_sos[i], SoHandle(n, i)); } } } /// Apply a function on all elements.\n /// Function invocations are parallelized.\n /// Uses static scheduling. /// \see ApplyOnAllElements void ApplyOnAllElementsParallel( const std::function& function); /// Apply a function on all elements.\n /// Function invocations are parallelized.\n /// Uses dynamic scheduling and work stealing. Batch size controlled by /// `chunk`. /// \param chunk number of sim objects that are assigned to a thread (batch /// size) /// \see ApplyOnAllElements void ApplyOnAllElementsParallelDynamic( uint64_t chunk, const std::function& function); /// Reserves enough memory to hold `capacity` number of simulation objects for /// each numa domain. void Reserve(size_t capacity) { for (auto& numa_sos : sim_objects_) { numa_sos.reserve(capacity); } } /// Resize `sim_objects_[numa_node]` such that it holds `current + additional` /// elements after this call. /// Returns the size after uint64_t GrowSoContainer(size_t additional, size_t numa_node) { if (additional == 0) { return sim_objects_[numa_node].size(); } auto current = sim_objects_[numa_node].size(); sim_objects_[numa_node].resize(current + additional); return current; } /// Returns true if a sim object with the given uid is stored in this /// ResourceManager. bool Contains(SoUid uid) const { return uid_soh_map_.find(uid) != uid_soh_map_.end(); } /// Remove all simulation objects /// NB: This method is not thread-safe! This function invalidates /// sim_object references pointing into the ResourceManager. SoPointer are /// not affected. void Clear() { uid_soh_map_.clear(); for (auto& numa_sos : sim_objects_) { for (auto* so : numa_sos) { delete so; } numa_sos.clear(); } } /// Reorder simulation objects such that, sim objects are distributed to NUMA /// nodes. Nearby sim objects will be moved to the same NUMA node. void SortAndBalanceNumaNodes(); void DebugNuma() const; /// NB: This method is not thread-safe! This function might invalidate /// sim_object references pointing into the ResourceManager. SoPointer are /// not affected. void push_back(SimObject* so, // NOLINT typename SoHandle::NumaNode_t numa_node = 0) { sim_objects_[numa_node].push_back(so); uid_soh_map_[so->GetUid()] = SoHandle(numa_node, sim_objects_[numa_node].size() - 1); } /// Adds `new_sim_objects` to `sim_objects_[numa_node]`. `offset` specifies /// the index at which the first element is inserted. Sim objects are inserted /// consecutively. This methos is thread safe only if insertion intervals do /// not overlap! virtual void AddNewSimObjects( typename SoHandle::NumaNode_t numa_node, uint64_t offset, const tbb::concurrent_unordered_map& new_sim_objects) { uint64_t i = 0; for (auto& pair : new_sim_objects) { auto uid = pair.first; uid_soh_map_[uid] = SoHandle(numa_node, offset + i); sim_objects_[numa_node][offset + i] = pair.second; i++; } } /// Removes the simulation object with the given uid.\n /// NB: This method is not thread-safe! This function invalidates /// sim_object references pointing into the ResourceManager. SoPointer are /// not affected. void Remove(SoUid uid) { // remove from map auto it = uid_soh_map_.find(uid); if (it != uid_soh_map_.end()) { SoHandle soh = it->second; uid_soh_map_.unsafe_erase(it); // remove from vector auto& numa_sos = sim_objects_[soh.GetNumaNode()]; if (soh.GetElementIdx() == numa_sos.size() - 1) { delete numa_sos.back(); numa_sos.pop_back(); } else { // swap delete numa_sos[soh.GetElementIdx()]; auto* reordered = numa_sos.back(); numa_sos[soh.GetElementIdx()] = reordered; numa_sos.pop_back(); uid_soh_map_[reordered->GetUid()] = soh; } } } protected: /// Maps an SoUid to its storage location in `sim_objects_` \n tbb::concurrent_unordered_map uid_soh_map_; //! /// Pointer container for all simulation objects std::vector> sim_objects_; /// Maps a diffusion grid ID to the pointer to the diffusion grid std::unordered_map diffusion_grids_; ThreadInfo* thread_info_ = ThreadInfo::GetInstance(); //! friend class SimulationBackup; BDM_CLASS_DEF_NV(ResourceManager, 1); }; } // namespace bdm #endif // CORE_RESOURCE_MANAGER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SCHEDULER_H_ #define CORE_SCHEDULER_H_ #include #include #include #include #include #include "core/operation/operation.h" namespace bdm { class SimObject; class SimulationBackup; class CatalystAdaptor; class RootAdaptor; class BoundSpace; class DisplacementOp; class DiffusionOp; class Scheduler { public: using Clock = std::chrono::high_resolution_clock; Scheduler(); virtual ~Scheduler(); void Simulate(uint64_t steps); /// This function returns the numer of simulated steps (=iterations). uint64_t GetSimulatedSteps() const; void AddOperation(const Operation& operation); /// Remove an operation. However, some operations are protected and cannot /// be removed. \see protected_operations_ /// A request to remove a proteced operation is ignored. void RemoveOperation(const std::string& op_name); /// Returns a reference to an operation. However, some operations are /// protected and will not be returned. \see protected_operations_ /// If the operation does not exist or is protected, a nullptr will be /// returned. Operation* GetOperation(const std::string& op_name); RootAdaptor* GetRootVisualization() { return root_visualization_; } protected: uint64_t total_steps_ = 0; /// Executes one step. /// This design makes testing more convenient virtual void Execute(bool last_iteration); private: SimulationBackup* backup_ = nullptr; uint64_t restore_point_; std::chrono::time_point last_backup_ = Clock::now(); CatalystAdaptor* visualization_ = nullptr; //! RootAdaptor* root_visualization_ = nullptr; //! bool is_gpu_environment_initialized_ = false; BoundSpace* bound_space_; DisplacementOp* displacement_; DiffusionOp* diffusion_; std::vector operations_; //! std::set protected_operations_; /// Backup the simulation. Backup interval based on `Param::backup_interval_` void Backup(); /// Restore the simulation if requested at the right time /// @param steps number of simulation steps for a `Simulate` call /// @return if `Simulate` should return early bool Restore(uint64_t* steps); // TODO(lukas, ahmad) After https://trello.com/c/0D6sHCK4 has been resolved // think about a better solution, because some operations are executed twice // if Simulate is called with one timestep. void Initialize(); // Decide which operations should be executed std::vector GetScheduleOps(); }; } // namespace bdm #endif // CORE_SCHEDULER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SHAPE_H_ #define CORE_SHAPE_H_ namespace bdm { enum Shape { kSphere, kCylinder }; } // namespace bdm #endif // CORE_SHAPE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIM_OBJECT_CELL_H_ #define CORE_SIM_OBJECT_CELL_H_ #include #include #include #include #include #include #include #include "core/container/inline_vector.h" #include "core/container/math_array.h" #include "core/default_force.h" #include "core/event/cell_division_event.h" #include "core/event/event.h" #include "core/execution_context/in_place_exec_ctxt.h" #include "core/param/param.h" #include "core/shape.h" #include "core/sim_object/sim_object.h" #include "core/util/math.h" namespace bdm { class Cell : public SimObject { BDM_SIM_OBJECT_HEADER(Cell, SimObject, 1, position_, tractor_force_, diameter_, volume_, adherence_, density_); public: /// First axis of the local coordinate system. static const Double3 kXAxis; /// Second axis of the local coordinate system. static const Double3 kYAxis; /// Third axis of the local coordinate system. static const Double3 kZAxis; Cell() : density_(1.0) {} explicit Cell(double diameter) : diameter_(diameter), density_(1.0) { UpdateVolume(); } explicit Cell(const Double3& position) : position_(position), density_{1.0} {} /// \brief This constructor is used to initialise the values of daughter /// 2 for a cell division event. /// /// \see CellDivisionEvent Cell(const Event& event, SimObject* mother, uint64_t new_oid = 0) : Base(event, mother, new_oid) { const CellDivisionEvent* cdevent = dynamic_cast(&event); Cell* mother_cell = dynamic_cast(mother); if (cdevent && mother_cell) { auto* daughter = this; // FIXME // A) Defining some values // .................................................................. // defining the two radii s.t total volume is conserved // * radius^3 = r1^3 + r2^3 ; // * volume_ratio = r2^3 / r1^3 double radius = mother_cell->GetDiameter() * 0.5; // define an axis for division (along which the nuclei will move) double x_coord = std::cos(cdevent->theta_) * std::sin(cdevent->phi_); double y_coord = std::sin(cdevent->theta_) * std::sin(cdevent->phi_); double z_coord = std::cos(cdevent->phi_); Double3 coords = {x_coord, y_coord, z_coord}; double total_length_of_displacement = radius / 4.0; const auto x_axis = mother_cell->kXAxis; const auto y_axis = mother_cell->kYAxis; const auto z_axis = mother_cell->kZAxis; Double3 axis_of_division = (coords.EntryWiseProduct(x_axis) + coords.EntryWiseProduct(y_axis) + coords.EntryWiseProduct(z_axis)) * total_length_of_displacement; // two equations for the center displacement : // 1) d2/d1= v2/v1 = volume_ratio (each sphere is shifted inver. // proportionally to its volume) // 2) d1 + d2 = TOTAL_LENGTH_OF_DISPLACEMENT double d_2 = total_length_of_displacement / (cdevent->volume_ratio_ + 1); double d_1 = total_length_of_displacement - d_2; double mother_volume = mother_cell->GetVolume(); double new_volume = mother_volume / (cdevent->volume_ratio_ + 1); daughter->SetVolume(mother_volume - new_volume); // position auto mother_pos = mother_cell->GetPosition(); auto new_position = mother_pos + (axis_of_division * d_2); daughter->SetPosition(new_position); // E) This sphere becomes the 1st daughter // move these cells on opposite direction mother_pos -= axis_of_division * d_1; // update mother here and not in EventHandler to avoid recomputation mother_cell->SetPosition(mother_pos); mother_cell->SetVolume(new_volume); daughter->SetAdherence(mother_cell->GetAdherence()); daughter->SetDensity(mother_cell->GetDensity()); // G) TODO(lukas) Copy the intracellular and membrane bound Substances } } virtual ~Cell() {} /// \brief EventHandler to modify the data members of this cell /// after a cell division. /// /// Performs the transition mother to daughter 1 /// \param event contains parameters for cell division /// \param daughter_2 pointer to new cell (=daughter 2) /// \see Event, CellDivisionEvent void EventHandler(const Event& event, SimObject* other1, SimObject* other2 = nullptr) override { Base::EventHandler(event, other1, other2); } Shape GetShape() const override { return Shape::kSphere; } /// \brief Divide this cell. /// /// CellDivisionEvent::volume_ratio_ will be between 0.9 and 1.1\n /// The axis of division is random. /// \see CellDivisionEvent virtual Cell* Divide() { auto* random = Simulation::GetActive()->GetRandom(); return Divide(random->Uniform(0.9, 1.1)); } /// \brief Divide this cell. /// /// The axis of division is random. /// \see CellDivisionEvent virtual Cell* Divide(double volume_ratio) { // find random point on sphere (based on : // http://mathworld.wolfram.com/SpherePointPicking.html) auto* random = Simulation::GetActive()->GetRandom(); double theta = 2 * Math::kPi * random->Uniform(0, 1); double phi = std::acos(2 * random->Uniform(0, 1) - 1); return Divide(volume_ratio, phi, theta); } /// \brief Divide this cell. /// /// CellDivisionEvent::volume_ratio_ will be between 0.9 and 1.1\n /// \see CellDivisionEvent virtual Cell* Divide(const Double3& axis) { auto* random = Simulation::GetActive()->GetRandom(); auto polarcoord = TransformCoordinatesGlobalToPolar(axis + position_); return Divide(random->Uniform(0.9, 1.1), polarcoord[1], polarcoord[2]); } /// \brief Divide this cell. /// /// \see CellDivisionEvent virtual Cell* Divide(double volume_ratio, const Double3& axis) { auto polarcoord = TransformCoordinatesGlobalToPolar(axis + position_); return Divide(volume_ratio, polarcoord[1], polarcoord[2]); } /// \brief Divide this cell. /// /// \see CellDivisionEvent virtual Cell* Divide(double volume_ratio, double phi, double theta) { auto* ctxt = Simulation::GetActive()->GetExecutionContext(); CellDivisionEvent event(volume_ratio, phi, theta); auto* daughter = static_cast(GetInstance(event, this)); ctxt->push_back(daughter); EventHandler(event, daughter); return daughter; } double GetAdherence() const { return adherence_; } double GetDiameter() const override { return diameter_; } double GetMass() const { return density_ * volume_; } double GetDensity() const { return density_; } const Double3& GetPosition() const override { return position_; } const Double3& GetTractorForce() const { return tractor_force_; } double GetVolume() const { return volume_; } void SetAdherence(double adherence) { adherence_ = adherence; } void SetDiameter(double diameter) override { if (diameter > diameter_) { SetRunDisplacementForAllNextTs(); } diameter_ = diameter; UpdateVolume(); } void SetVolume(double volume) { volume_ = volume; UpdateDiameter(); } void SetMass(double mass) { density_ = mass / volume_; } void SetDensity(double density) { density_ = density; } void SetPosition(const Double3& position) override { position_ = position; SetRunDisplacementForAllNextTs(); } void SetTractorForce(const Double3& tractor_force) { tractor_force_ = tractor_force; } void ChangeVolume(double speed) { // scaling for integration step auto* param = Simulation::GetActive()->GetParam(); double delta = speed * param->simulation_time_step_; volume_ += delta; if (volume_ < 5.2359877E-7) { volume_ = 5.2359877E-7; } UpdateDiameter(); } void UpdateDiameter() { // V = (4/3)*pi*r^3 = (pi/6)*diameter^3 double diameter = std::cbrt(volume_ * 6 / Math::kPi); if (diameter > diameter_) { Base::SetRunDisplacementForAllNextTs(); } diameter_ = diameter; } void UpdateVolume() { // V = (4/3)*pi*r^3 = (pi/6)*diameter^3 volume_ = Math::kPi / 6 * std::pow(diameter_, 3); } void UpdatePosition(const Double3& delta) { position_ += delta; SetRunDisplacementForAllNextTs(); } Double3 CalculateDisplacement(double squared_radius, double dt) override { // Basically, the idea is to make the sum of all the forces acting // on the Point mass. It is stored in translationForceOnPointMass. // There is also a computation of the torque (only applied // by the daughter neurites), stored in rotationForce. // TODO(roman) : There might be a problem, in the sense that the biology // is not applied if the total Force is smaller than adherence. // Once, I should look at this more carefully. // fixme why? copying const auto& tf = GetTractorForce(); // the 3 types of movement that can occur // bool biological_translation = false; bool physical_translation = false; // bool physical_rotation = false; double h = dt; Double3 movement_at_next_step{0, 0, 0}; // BIOLOGY : // 0) Start with tractor force : What the biology defined as active // movement------------ movement_at_next_step += tf * h; // PHYSICS // the physics force to move the point mass Double3 translation_force_on_point_mass{0, 0, 0}; // the physics force to rotate the cell // Double3 rotation_force { 0, 0, 0 }; // 1) "artificial force" to maintain the sphere in the ecm simulation // boundaries-------- // 2) Spring force from my neurites (translation and // rotation)-------------------------- // 3) Object avoidance force // ----------------------------------------------------------- // (We check for every neighbor object if they touch us, i.e. push us // away) auto calculate_neighbor_forces = [&, this](const auto* neighbor) { DefaultForce default_force; auto neighbor_force = default_force.GetForce(this, neighbor); translation_force_on_point_mass[0] += neighbor_force[0]; translation_force_on_point_mass[1] += neighbor_force[1]; translation_force_on_point_mass[2] += neighbor_force[2]; }; auto* ctxt = Simulation::GetActive()->GetExecutionContext(); ctxt->ForEachNeighborWithinRadius(calculate_neighbor_forces, *this, squared_radius); // 4) PhysicalBonds // How the physics influences the next displacement double norm_of_force = std::sqrt(translation_force_on_point_mass * translation_force_on_point_mass); // is there enough force to : // - make us biologically move (Tractor) : // - break adherence and make us translate ? physical_translation = norm_of_force > GetAdherence(); assert(GetMass() != 0 && "The mass of a cell was found to be zero!"); double mh = h / GetMass(); // adding the physics translation (scale by weight) if important enough if (physical_translation) { // We scale the move with mass and time step movement_at_next_step += translation_force_on_point_mass * mh; // Performing the translation itself : // but we want to avoid huge jumps in the simulation, so there are // maximum distances possible auto* param = Simulation::GetActive()->GetParam(); if (norm_of_force * mh > param->simulation_max_displacement_) { movement_at_next_step.Normalize(); movement_at_next_step *= param->simulation_max_displacement_; } } return movement_at_next_step; } void ApplyDisplacement(const Double3& displacement) override; protected: /// Returns the position in the polar coordinate system (cylindrical or /// spherical) of a point expressed in global cartesian coordinates /// ([1,0,0],[0,1,0],[0,0,1]). /// @param coord: position in absolute coordinates - [x,y,z] cartesian values /// @return the position in local coordinates Double3 TransformCoordinatesGlobalToPolar(const Double3& coord) const; /// NB: Use setter and don't assign values directly Double3 position_ = {{0, 0, 0}}; Double3 tractor_force_ = {{0, 0, 0}}; /// NB: Use setter and don't assign values directly double diameter_ = 0; double volume_ = 0; double adherence_ = 0; double density_ = 0; private: BDM_CLASS_DEF(Cell, 1); }; } // namespace bdm #endif // CORE_SIM_OBJECT_CELL_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIM_OBJECT_SIM_OBJECT_H_ #define CORE_SIM_OBJECT_SIM_OBJECT_H_ #include #include #include #include #include #include #include #include #include #include #include "core/container/math_array.h" #include "core/shape.h" #include "core/sim_object/so_pointer.h" #include "core/sim_object/so_uid.h" #include "core/sim_object/so_visitor.h" #include "core/util/macros.h" #include "core/util/root.h" namespace bdm { // ----------------------------------------------------------------------------- // Helper macros used to generate code for all data members of a class #define BDM_SIM_OBJECT_FOREACHDM_BODY(...) \ EVAL(LOOP(BDM_SIM_OBJECT_FOREACHDM_BODY_ITERATOR, __VA_ARGS__)) #define BDM_SIM_OBJECT_FOREACHDM_BODY_ITERATOR(data_member) \ if (is_so_ptr::value) { \ visitor->Visit(#data_member, typeid(uint64_t).hash_code(), \ static_cast( \ detail::ExtractUidPtr::GetUidPtr(data_member))); \ } else { \ visitor->Visit(#data_member, typeid(decltype(data_member)).hash_code(), \ static_cast(&data_member)); \ } #define BDM_SIM_OBJECT_FOREACHDMIN_BODY(...) \ EVAL(LOOP(BDM_SIM_OBJECT_FOREACHDMIN_BODY_ITERATOR, __VA_ARGS__)) #define BDM_SIM_OBJECT_FOREACHDMIN_BODY_ITERATOR(data_member) \ { \ auto it = dm_selector.find(#data_member); \ if (it != dm_selector.end()) { \ if (is_so_ptr::value) { \ visitor->Visit(#data_member, typeid(uint64_t).hash_code(), \ static_cast( \ detail::ExtractUidPtr::GetUidPtr(data_member))); \ } else { \ visitor->Visit(#data_member, \ typeid(decltype(data_member)).hash_code(), \ static_cast(&data_member)); \ } \ } \ } /// Macro to insert required boilerplate code into simulation object /// @param class_name scalar class name of the simulation object /// @param base_class scalar class name of the base simulation object /// @param class_version_id required for ROOT I/O (see ROOT BDM_CLASS_DEF /// Macro). /// Every time the layout of the class is changed, class_version_id /// must be incremented by one. The class_version_id should be greater /// or equal to 1. /// @param ...: List of all data members of this class #define BDM_SIM_OBJECT_HEADER(class_name, base_class, class_version_id, ...) \ public: \ using Base = base_class; \ \ explicit class_name(TRootIOCtor* io_ctor) {} \ \ /** Create a new instance of this object using the default constructor. */ \ SimObject* GetInstance(const Event& event, SimObject* other, \ uint64_t new_oid = 0) const override { \ return new class_name(event, other, new_oid); \ } \ \ /** Create a copy of this object. */ \ SimObject* GetCopy() const override { return new class_name(*this); } \ \ const char* GetTypeName() const override { return #class_name; } \ \ /** Executes the given function for all data members */ \ void ForEachDataMember(SoVisitor* visitor) const override { \ BDM_SIM_OBJECT_FOREACHDM_BODY(__VA_ARGS__) \ Base::ForEachDataMember(visitor); \ } \ \ /** Executes the given function for the specified data members */ \ void ForEachDataMemberIn(const std::set& dm_selector, \ SoVisitor* visitor) const override { \ BDM_SIM_OBJECT_FOREACHDMIN_BODY(__VA_ARGS__) \ Base::ForEachDataMemberIn(dm_selector, visitor); \ } \ \ protected: \ /** Cast `this` to the base class pointer (one level up) */ \ Base* UpCast() { return static_cast(this); } \ \ /** Cast `this` to the base class pointer (one level up) */ \ const Base* UpCast() const { return static_cast(this); } \ \ // BDM_CLASS_DEF(class_name, class_version_id) // ----------------------------------------------------------------------------- struct Event; struct BaseBiologyModule; /// Contains code required by all simulation objects class SimObject { public: SimObject(); SimObject(const Event& event, SimObject* other, uint64_t new_oid = 0); explicit SimObject(TRootIOCtor* io_ctor); SimObject(const SimObject& other); virtual ~SimObject(); /// Executes the given function for all data members /// \see `SoVisitor` virtual void ForEachDataMember(SoVisitor* visitor) const { BDM_SIM_OBJECT_FOREACHDM_BODY(uid_, box_idx_, biology_modules_, run_bm_loop_idx_) } /// Executes the given visitor for the specified data members /// \see `SoVisitor` virtual void ForEachDataMemberIn(const std::set& dm_selector, SoVisitor* visitor) const { BDM_SIM_OBJECT_FOREACHDMIN_BODY(uid_, box_idx_, biology_modules_, run_bm_loop_idx_) } // --------------------------------------------------------------------------- /// Create a new instance of this object using the default constructor. virtual SimObject* GetInstance(const Event& event, SimObject* other, uint64_t new_oid = 0) const = 0; /// Create a copy of this object. virtual SimObject* GetCopy() const = 0; virtual const char* GetTypeName() const { return "SimObject"; } virtual Shape GetShape() const = 0; /// Returns the data members that are required to visualize this simulation /// object. virtual std::set GetRequiredVisDataMembers() const { return {"position_", "diameter_"}; } virtual void RunDiscretization(); void AssignNewUid(); SoUid GetUid() const; uint32_t GetBoxIdx() const; void SetBoxIdx(uint32_t idx); void SetRunDisplacementNextTimestep(bool run) const { run_displacement_next_ts_ = run; } bool GetRunDisplacementForAllNextTs() const { return run_displacement_for_all_next_ts_; } void SetRunDisplacementForAllNextTs(bool value = true) { run_displacement_for_all_next_ts_ = value; } void ApplyRunDisplacementForAllNextTs(); void UpdateRunDisplacement() { run_displacement_ = run_displacement_next_ts_; run_displacement_next_ts_ = false; } bool RunDisplacement() const { return run_displacement_; } /// Return simulation object pointer template SoPointer GetSoPtr() const { return SoPointer(uid_); } // --------------------------------------------------------------------------- // Biology modules /// Add a biology module to this sim object void AddBiologyModule(BaseBiologyModule* module); /// Remove a biology module from this sim object void RemoveBiologyModule(const BaseBiologyModule* remove_module); /// Execute all biology modulesq void RunBiologyModules(); /// Return all biology modules const std::vector& GetAllBiologyModules() const; // --------------------------------------------------------------------------- virtual Double3 CalculateDisplacement(double squared_radius, double dt) = 0; virtual void ApplyDisplacement(const Double3& displacement) = 0; virtual const Double3& GetPosition() const = 0; virtual void SetPosition(const Double3& pos) = 0; virtual double GetDiameter() const = 0; virtual void SetDiameter(double diameter) = 0; void RemoveFromSimulation() const; virtual void EventHandler(const Event& event, SimObject* other1, SimObject* other2 = nullptr); protected: /// unique id SoUid uid_; /// Grid box index uint32_t box_idx_; /// collection of biology modules which define the internal behavior std::vector biology_modules_; private: /// Helper variable used to support removal of biology modules while /// `RunBiologyModules` iterates over them. uint32_t run_bm_loop_idx_ = 0; bool run_displacement_ = true; //! bool run_displacement_for_all_next_ts_ = false; //! mutable bool run_displacement_next_ts_ = true; //! /// @brief Function to copy biology modules from one structure to another /// @param event event will be passed on to biology module to determine /// whether it should be copied to destination /// @param src source vector of biology modules /// @param dest destination vector of biology modules void CopyBiologyModules(const Event& event, decltype(biology_modules_) * dest); /// @brief Function to invoke the EventHandler of the biology module or remove /// it from `current`. /// Forwards the event handler call to each biology modules of the triggered /// simulation object and removes biology modules if they are flagged. void BiologyModuleEventHandler(const Event& event, decltype(biology_modules_) * other1, decltype(biology_modules_) * other2); // BDM_CLASS_DEF(SimObject, 1) }; } // namespace bdm #endif // CORE_SIM_OBJECT_SIM_OBJECT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIM_OBJECT_SO_POINTER_H_ #define CORE_SIM_OBJECT_SO_POINTER_H_ #include #include #include #include "core/execution_context/in_place_exec_ctxt.h" #include "core/sim_object/so_uid.h" #include "core/simulation.h" #include "core/util/root.h" namespace bdm { class SimObject; /// Simulation object pointer. Required to point to a simulation object with /// throughout the whole simulation. Raw pointers cannot be used, because /// a sim object might be copied to a different NUMA domain, or if it resides /// on a different address space in case of a distributed runtime. /// Benefit compared to SoHandle is, that the compiler knows /// the type returned by `Get` and can therefore inline the code from the callee /// and perform optimizations. /// @tparam TSimObject simulation object type template class SoPointer { public: explicit SoPointer(SoUid uid) : uid_(uid) {} /// constructs an SoPointer object representing a nullptr SoPointer() {} virtual ~SoPointer() {} uint64_t GetUid() const { return uid_; } const uint64_t* GetUidPtr() const { return &uid_; } /// Equals operator that enables the following statement `so_ptr == nullptr;` bool operator==(std::nullptr_t) const { return uid_ == std::numeric_limits::max(); } /// Not equal operator that enables the following statement `so_ptr != /// nullptr;` bool operator!=(std::nullptr_t) const { return !this->operator==(nullptr); } bool operator==(const SoPointer& other) const { return uid_ == other.uid_; } template bool operator==(const TSo& other) const { return uid_ == other.GetUid(); } bool operator!=(const TSimObject& other) const { return !this->operator==(other); } /// Assignment operator that changes the internal representation to nullptr. /// Makes the following statement possible `so_ptr = nullptr;` SoPointer& operator=(std::nullptr_t) { uid_ = std::numeric_limits::max(); return *this; } TSimObject* operator->() { assert(*this != nullptr); auto* ctxt = Simulation::GetActive()->GetExecutionContext(); return dynamic_cast(ctxt->GetSimObject(uid_)); } const TSimObject* operator->() const { assert(*this != nullptr); auto* ctxt = Simulation::GetActive()->GetExecutionContext(); return dynamic_cast(ctxt->GetConstSimObject(uid_)); } friend std::ostream& operator<<(std::ostream& str, const SoPointer& so_ptr) { str << "{ uid: " << so_ptr.uid_ << "}"; return str; } // TODO(lukas) add test TSimObject& operator*() { return *(this->operator->()); } // TODO(lukas) add test const TSimObject& operator*() const { return *(this->operator->()); } // TODO(lukas) add test TSimObject* Get() { return this->operator->(); } // TODO(lukas) add test const TSimObject* Get() const { return this->operator->(); } private: SoUid uid_ = std::numeric_limits::max(); BDM_TEMPLATE_CLASS_DEF(SoPointer, 2); }; template struct is_so_ptr { static constexpr bool value = false; // NOLINT }; template struct is_so_ptr> { static constexpr bool value = true; // NOLINT }; namespace detail { struct ExtractUidPtr { template static typename std::enable_if::value, const uint64_t*>::type GetUidPtr(const T& t) { return t.GetUidPtr(); } template static typename std::enable_if::value, const uint64_t*>::type GetUidPtr(const T& t) { return nullptr; } }; } // namespace detail } // namespace bdm #endif // CORE_SIM_OBJECT_SO_POINTER_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIM_OBJECT_SO_UID_H_ #define CORE_SIM_OBJECT_SO_UID_H_ #include namespace bdm { /// SoUid is a unique id for simulation objects that remains unchanged /// throughout the whole simulation. using SoUid = uint64_t; /// This class generates unique ids for simulation objects events satisfying the /// EventId invariant. Thread safe. class SoUidGenerator { public: SoUidGenerator(const SoUidGenerator&) = delete; static SoUidGenerator* Get() { static SoUidGenerator kInstance; return &kInstance; } SoUid NewSoUid() { return counter_++; } SoUid GetLastId() const { return counter_; } private: SoUidGenerator() : counter_(0) {} std::atomic counter_; }; } // namespace bdm #endif // CORE_SIM_OBJECT_SO_UID_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIM_OBJECT_SO_VISITOR_H_ #define CORE_SIM_OBJECT_SO_VISITOR_H_ #include #include namespace bdm { // Interface for simulation object visitors. struct SoVisitor { virtual ~SoVisitor() {} virtual void Visit(const std::string& name, size_t type_hash_code, const void* data) = 0; }; } // namespace bdm #endif // CORE_SIM_OBJECT_SO_VISITOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIMULATION_H_ #define CORE_SIMULATION_H_ #include #include #include #include "core/util/random.h" #include "core/util/root.h" namespace bdm { // forward declarations class ResourceManager; class Grid; class Scheduler; struct Param; class InPlaceExecutionContext; class CommandLineOptions; class SimulationTest; class CatalystAdaptorTest; /// This is the central BioDynaMo object. It containes pointers to e.g. the /// ResourceManager, the scheduler, parameters, ... \n /// It is possible to create multiple simulations, but only one can be active at /// the same time. Creating a new simulation object automatically activates it. class Simulation { public: /// This function returns the currently active Simulation simulation. static Simulation* GetActive(); explicit Simulation(TRootIOCtor* p); /// Constructor that takes the arguments from `main` to parse command line /// arguments. The simulation name is extracted from the executable name. /// Creation of a new simulation automatically activates it. Simulation(int argc, const char** argv, const std::string& config_file = ""); Simulation(CommandLineOptions* clo, const std::string& config_file = ""); /// Alternative constructor, if the arguments from function `main` are not /// available, or if a different simulation name should be chosen. \n /// Command line arguments are not parsed!\n /// Creation of a new simulation automatically activates it. /// \param config_file Use a different config file than the default bdm.toml explicit Simulation(const std::string& simulation_name, const std::string& config_file = ""); Simulation(int argc, const char** argv, const std::function& set_param, const std::string& config_file = ""); Simulation(CommandLineOptions* clo, const std::function& set_param, const std::string& config_file = ""); Simulation(const std::string& simulation_name, const std::function& set_param, const std::string& config_file = ""); ~Simulation(); /// Copies / moves values from a restored simulation into this object. /// Thus, pointers to `rm_`, `param_`, ... are not invalidated. void Restore(Simulation&& restored); /// Activates this simulation. void Activate(); ResourceManager* GetResourceManager(); void SetResourceManager(ResourceManager* rm); const Param* GetParam() const; Grid* GetGrid(); Scheduler* GetScheduler(); void Simulate(uint64_t steps); /// Returns a thread local random number generator. Random* GetRandom(); /// Returns all thread local random number generator. std::vector& GetAllRandom(); /// Returns a thread local execution context. InPlaceExecutionContext* GetExecutionContext(); /// Returns all thread local execution contexts. std::vector& GetAllExecCtxts(); /// @see `unique_name_` const std::string& GetUniqueName() const; /// Returns the output directory for this specific simulation const std::string& GetOutputDir() const; /// Replaces the scheduler for this simulation. /// Existing scheduler will be deleted! Therefore, pointers to the old /// scheduler (obtained with `GetScheduler()`) will be invalidated. \n /// Simulation will take ownership of the passed pointer void ReplaceScheduler(Scheduler* scheduler); private: /// Currently active simulation static Simulation* active_; /// Number of simulations in this process static std::atomic counter_; /// random number generator for each thread std::vector random_; /// Execution Context for each thread std::vector exec_ctxt_; //! ResourceManager* rm_ = nullptr; Param* param_ = nullptr; std::string name_; Grid* grid_ = nullptr; //! Scheduler* scheduler_ = nullptr; //! /// This id is unique for each simulation within the same process uint64_t id_ = 0; //! /// cached value where `id_` is appended to `name_` if `id_` is /// not zero.\n /// e.g. `name_ = "my-sim"` and `id_ = 0` -> "my-sim"\n /// e.g. `name_ = "my-sim"` and `id_ = 4` -> "my-sim4" std::string unique_name_; //! /// cached value where `unique_name_` is appended to `Param::output_dir_` std::string output_dir_; //! /// Initialize Simulation void Initialize(CommandLineOptions* clo, const std::function& set_param, const std::string& ctor_config); /// Initialize data members that have a dependency on Simulation void InitializeMembers(); /// This function parses command line parameters and the configuration file. void InitializeRuntimeParams(CommandLineOptions* clo, const std::function& set_param, const std::string& ctor_config); void LoadConfigFile(const std::string& ctor_config, const std::string& cli_config); /// This function initialzes `unique_name_` void InitializeUniqueName(const std::string& simulation_name); /// Initializes `output_dir_` and creates dir if it does not exist. void InitializeOutputDir(); friend SimulationTest; friend CatalystAdaptorTest; BDM_CLASS_DEF_NV(Simulation, 1); }; } // namespace bdm #endif // CORE_SIMULATION_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SIMULATION_BACKUP_H_ #define CORE_SIMULATION_BACKUP_H_ #include #include #include #include #include "core/simulation.h" #include "core/util/io.h" #include "core/util/log.h" namespace bdm { /// SimulationBackup is responsible for backing up and restoring all relevant /// simulation information. class SimulationBackup { public: // object names for root file static const std::string kSimulationName; static const std::string kSimulationStepName; static const std::string kRuntimeVariableName; /// If a whole simulation is restored from a ROOT file, the new /// ResourceManager is not updated before the end. Consequently, during /// restore, a custom streamer cannot access the new RessourceManager. /// However, they can delay actions that rely on the new RessourceManager /// by registering it to `after_restore_event_` and will be executed /// after the restore has been finished and the ResourceManager has been /// updated. static std::vector> after_restore_event_; /// If `backup_file` is an empty string no backups will be made /// If `restore_file` is an empty string no restore will be made SimulationBackup(const std::string& backup_file, const std::string& restore_file); void Backup(size_t completed_simulation_steps) { if (!backup_) { Log::Fatal("SimulationBackup", "Requested to backup data, but no backup file given."); } // create temporary file // if application crashes during backup; last backup is not corrupted std::stringstream tmp_file; tmp_file << "tmp_" << backup_file_; // Backup { TFileRaii f(tmp_file.str(), "UPDATE"); auto* simulation = Simulation::GetActive(); f.Get()->WriteObject(simulation, kSimulationName.c_str()); IntegralTypeWrapper wrapper(completed_simulation_steps); f.Get()->WriteObject(&wrapper, kSimulationStepName.c_str()); RuntimeVariables rv; f.Get()->WriteObject(&rv, kRuntimeVariableName.c_str()); // TODO(lukas) random number generator; all statics (e.g. Param) } // remove last backup file remove(backup_file_.c_str()); // rename temporary file rename(tmp_file.str().c_str(), backup_file_.c_str()); } void Restore() { if (!restore_) { Log::Fatal("SimulationBackup", "Requested to restore data, but no restore file given."); } after_restore_event_.clear(); TFileRaii file(TFile::Open(restore_file_.c_str())); RuntimeVariables* restored_rv; file.Get()->GetObject(kRuntimeVariableName.c_str(), restored_rv); // check if runtime variables are the same if (!(RuntimeVariables() == *restored_rv)) { Log::Warning("SimulationBackup", "Restoring simulation executed on a different system!"); } Simulation* restored_simulation = nullptr; file.Get()->GetObject(kSimulationName.c_str(), restored_simulation); Simulation::GetActive()->Restore(std::move(*restored_simulation)); Log::Info("Scheduler", "Restored simulation from ", restore_file_); delete restored_simulation; // call all after restore events for (auto&& event : after_restore_event_) { event(); } after_restore_event_.clear(); } size_t GetSimulationStepsFromBackup(); bool BackupEnabled(); bool RestoreEnabled(); private: bool backup_ = false; bool restore_ = true; std::string backup_file_; std::string restore_file_; }; } // namespace bdm #endif // CORE_SIMULATION_BACKUP_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_SUBSTANCE_INITIALIZERS_H_ #define CORE_SUBSTANCE_INITIALIZERS_H_ #include #include #include "Math/DistFunc.h" #include "core/diffusion_grid.h" namespace bdm { // ----------------------------------------------------------------------------- // A substance initializer is a function that can be used to initialize the // concentration values of a particular substance in space. // ----------------------------------------------------------------------------- // Use this enum to express the axis you are interested in enum Axis { kXAxis, kYAxis, kZAxis }; /// An initializer that uniformly initializes the concentration of a diffusion /// grid based on the input value and the range (along the specified axis). struct Uniform { double min_; double max_; double value_; uint8_t axis_; Uniform(double min, double max, double value, uint8_t axis) { min_ = min; max_ = max; value_ = value; axis_ = axis; } double operator()(double x, double y, double z) { switch (axis_) { case Axis::kXAxis: { if (x >= min_ && x <= max_) { return value_; } break; } case Axis::kYAxis: { if (y >= min_ && y <= max_) { return value_; } break; } case Axis::kZAxis: { if (z >= min_ && z <= max_) { return value_; } break; } default: throw std::logic_error("You have chosen an non-existing axis!"); } return 0; } }; /// An initializer that follows a Gaussian (normal) distribution along one axis /// We use ROOT's built-in statistics function `normal_pdf(X, sigma, mean)`, /// that follows the normal probability density function: /// ( 1/( sigma * sqrt(2*pi) ))*e^( (-(x - mean )^2) / (2*sigma^2)) struct GaussianBand { double mean_; double sigma_; uint8_t axis_; /// @brief The constructor /// /// @param[in] mean The mean of the Gaussian distribution (should be a /// value within the range of the chosen axis) /// @param[in] sigma The sigma of the Gaussian distribution /// @param[in] axis The axis along which you want the Gaussian distribution /// to be oriented to /// GaussianBand(double mean, double sigma, uint8_t axis) { mean_ = mean; sigma_ = sigma; axis_ = axis; } /// @brief The model that we want to apply for substance initialization. /// The operator is called for the entire space /// /// @param[in] x The x coordinate /// @param[in] y The y coordinate /// @param[in] z The z coordinate /// double operator()(double x, double y, double z) { switch (axis_) { case Axis::kXAxis: return ROOT::Math::normal_pdf(x, sigma_, mean_); case Axis::kYAxis: return ROOT::Math::normal_pdf(y, sigma_, mean_); case Axis::kZAxis: return ROOT::Math::normal_pdf(z, sigma_, mean_); default: throw std::logic_error("You have chosen an non-existing axis!"); } } }; /// An initializer that follows a Poisson (normal) distribution along one axis /// The function ROOT::Math::poisson_pdfd(X, lambda) follows the normal /// probability density function: /// {e^( - lambda ) * lambda ^x )} / x! struct PoissonBand { double lambda_; uint8_t axis_; /// @brief The constructor /// /// @param[in] lambda The lambda of the Poisson distribution /// @param[in] axis The axis along which you want the Poisson distribution /// to be oriented to /// PoissonBand(double lambda, uint8_t axis) { lambda_ = lambda; axis_ = axis; } /// @brief The model that we want to apply for substance initialization. /// The operator is called for the entire space /// /// @param[in] x The x coordinate /// @param[in] y The y coordinate /// @param[in] z The z coordinate /// double operator()(double x, double y, double z) { switch (axis_) { case Axis::kXAxis: return ROOT::Math::poisson_pdf(x, lambda_); case Axis::kYAxis: return ROOT::Math::poisson_pdf(y, lambda_); case Axis::kZAxis: return ROOT::Math::poisson_pdf(z, lambda_); default: throw std::logic_error("You have chosen an non-existing axis!"); } } }; } // namespace bdm #endif // CORE_SUBSTANCE_INITIALIZERS_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_CPPTOML_H_ #define CORE_UTIL_CPPTOML_H_ #define BDM_ASSIGN_CONFIG_VALUE(variable, config_key) \ { \ if (config->contains_qualified(config_key)) { \ auto value = config->get_qualified_as(config_key); \ if (value) { \ variable = *value; \ } \ } \ } #endif // CORE_UTIL_CPPTOML_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_DEBUG_H_ #define CORE_UTIL_DEBUG_H_ #include #include #include #include #include "core/container/math_array.h" inline void Print(const bdm::Double3& a, int precision = 10) { std::cout << std::setprecision(precision) << a[0] << ", " << a[1] << ", " << a[2] << std::endl; } #endif // CORE_UTIL_DEBUG_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_IO_H_ #define CORE_UTIL_IO_H_ #include #include #include #include "core/util/root.h" namespace bdm { /// This class stores the runtime variables of the system. This is useful for /// check if the same system is used for continuing a simulation for example. class RuntimeVariables { public: RuntimeVariables(); // Constructor for ROOT I/O explicit RuntimeVariables(TRootIOCtor* io_ctor); SysInfo_t GetSystemInfo() const; void SetSystemInfo(const SysInfo_t& other); void PrintSystemInfo(); bool operator==(const RuntimeVariables& other) const; bool operator!=(const RuntimeVariables& other) const; private: SysInfo_t sysinfo_; BDM_CLASS_DEF_NV(RuntimeVariables, 1); // NOLINT }; /// Automatically close a TFile object using RAII pattern class TFileRaii { public: TFileRaii(const std::string& filename, const char* mode); explicit TFileRaii(TFile* file); ~TFileRaii(); TFile* Get(); private: TFile* file_; }; /// ROOT cannot write a single integral type (int, double, ...). Therefore, /// this wrapper is needed template class IntegralTypeWrapper { public: explicit IntegralTypeWrapper(const T& data) : data_(data) {} explicit IntegralTypeWrapper(TRootIOCtor* io_ctor) {} const T& Get() const { return data_; } private: T data_; BDM_CLASS_DEF_NV(IntegralTypeWrapper, 1); }; bool FileExists(const std::string& file_name); /// @brief Gets the persistent object from the specified ROOT file. /// /// @param[in] root_file The root file /// @param[in] obj_name The object name /// @param empty_obj The empty object /// /// @tparam T { The object class type } /// /// @return The persistent object. /// template bool GetPersistentObject(const char* root_file, const char* obj_name, T*& empty_obj) { // NOLINT if (FileExists(root_file)) { TFileRaii file(TFile::Open(root_file)); file.Get()->GetObject(obj_name, empty_obj); return true; } return false; } // clang-format off /// /// @brief Writes a persistent object to the specified ROOT file. /// /// @param[in] root_file The root file /// @param[in] obj_name The object name /// @param pst_object The persistent object /// @param[in] mode The mode /// /// @tparam T { The object class type } /// /// Option | Details /// -------|-------- /// new (default) | A new root file `root_file` is created. If file already exists, an error message is printed and the function returns. /// recreate | If file does not exist, it is created (like in "new"). If file already exist, the existing file is deleted before creating the new file. /// update | New classes are added to the existing directory. Existing classes with the same name are replaced by the new definition. If the directory dirname doest not exist, same effect as "new". /// // clang-format on template void WritePersistentObject(const char* root_file, const char* obj_name, const T& pst_object, const char* mode = "new") { TFileRaii file(root_file, mode); file.Get()->WriteObject(&pst_object, obj_name); } } // namespace bdm #endif // CORE_UTIL_IO_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_LOG_H_ #define CORE_UTIL_LOG_H_ #include #include #include #include #include #include "core/util/string.h" namespace bdm { /// @brief Wrapper class over ROOT logging module /// class Log { public: /// @brief Prints debug message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Debug(const std::string& location, const Args&... parts) { // kPrint has the highest level of verbosity if (gErrorIgnoreLevel <= kPrint) { std::string message = Concat(parts...); // Mimic ROOT logging output fprintf(stderr, "Debug in <%s>: %s\n", location.c_str(), message.c_str()); } } /// @brief Prints information message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Info(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::Info(location.c_str(), "%s", message.c_str()); } /// @brief Prints warning message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Warning(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::Warning(location.c_str(), "%s", message.c_str()); } /// @brief Prints error message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Error(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::Error(location.c_str(), "%s", message.c_str()); } /// @brief Prints break message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Break(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::Break(location.c_str(), "%s", message.c_str()); } /// @brief Prints system error message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void SysError(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::SysError(location.c_str(), "%s", message.c_str()); } /// @brief Prints fatal error message /// /// @param[in] location The location of the message /// @param[in] parts objects that compose the entire message /// template static void Fatal(const std::string& location, const Args&... parts) { std::string message = Concat(parts...); // ROOT function ::Error(location.c_str(), "%s", message.c_str()); exit(1); } }; } // namespace bdm #endif // CORE_UTIL_LOG_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_MACROS_H_ #define CORE_UTIL_MACROS_H_ #include "cpp_magic.h" /// TODO #define LOOP(operation, ...) \ IF(HAS_ARGS(__VA_ARGS__))(DEFER2(_LOOP_NE)()(operation, __VA_ARGS__)) /// loops over variadic macro arguments and calls the specified operation /// processes one argument in each iteration /// e.g. LOOP(OP, a, b) will lead to: /// OP(a) /// OP(b) /// For a more detailed explanation see `MAP` macro in `third_party/cpp_magic.h` // clang-format off #define LOOP_NE(operation, first, ...) \ operation(first) \ IF(HAS_ARGS(__VA_ARGS__))( \ DEFER2(_LOOP_NE)()(operation, __VA_ARGS__)) #define _LOOP_NE() LOOP_NE // clang-format on /// loops over variadic macro arguments and calls the specified operation /// removes the first two parameters in each iteration, but adds the first one /// again for the next call /// e.g. LOOP_3_1(OP, a, b, c) will lead to: /// OP(a, b) /// OP(a, c) /// For a more detailed explanation see `MAP` macro in `third_party/cpp_magic.h` // clang-format off #define LOOP_2_1(operation, first, second, ...) \ operation(first, second) \ IF(HAS_ARGS(__VA_ARGS__))( \ DEFER2(_LOOP_2_1)()(operation, first, __VA_ARGS__)) #define _LOOP_2_1() LOOP_2_1 // clang-format on /// loops over variadic macro arguments and calls the specified operation /// removes the first three parameters in each iteration, but adds the first one /// again for the next call /// e.g. LOOP_3_1(OP, a, b, c, d, e) will lead to: /// OP(a, b, c) /// OP(a, d, e) /// For a more detailed explanation see `MAP` macro in `third_party/cpp_magic.h` // clang-format off #define LOOP_3_1(operation, first, second, third, ...) \ operation(first, second, third) \ IF(HAS_ARGS(__VA_ARGS__))( \ DEFER2(_LOOP_3_1)()(operation, first, __VA_ARGS__)) #define _LOOP_3_1() LOOP_3_1 // clang-format on /// Macro to force compiler to inline a function #define BDM_FORCE_INLINE inline __attribute__((always_inline)) #endif // CORE_UTIL_MACROS_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_MATH_H_ #define CORE_UTIL_MATH_H_ #include #include #include #include #include #include "core/container/math_array.h" #include "core/util/random.h" namespace bdm { struct Math { /// value of pi static constexpr double kPi = TMath::Pi(); /// Helpful constant to identify 'infinity' static constexpr double kInfinity = 1e20; static double ToDegree(double rad) { return rad * (180 / kPi); } static double ToRadian(double deg) { return deg * (kPi / 180); } // Helper function that returns distance (L2 norm) between two positions in 3D static double GetL2Distance(const Double3& pos1, const Double3& pos2) { Double3 dist_array; dist_array[0] = pos2[0] - pos1[0]; dist_array[1] = pos2[1] - pos1[1]; dist_array[2] = pos2[2] - pos1[2]; return dist_array.Norm(); } /// Returns the cross product of two vectors. /// @param a /// @param b /// @return result the cross product of a and b (a x b) template static MathArray CrossProduct(const MathArray& a, const MathArray& b) { MathArray result; result[0] = a[1] * b[2] - a[2] * b[1]; result[1] = a[2] * b[0] - a[0] * b[2]; result[2] = a[0] * b[1] - a[1] * b[0]; return result; } /// Returns a vector of norm 1 perpendicular to a 3D vector. As usual there is /// no length check. /// @param a vector /// @param random /// @return a perpendicular vector static Double3 Perp3(const Double3& a, double random) { Double3 vect_perp; if (a[0] == 0.0) { vect_perp[0] = 1.0; vect_perp[1] = 0.0; vect_perp[2] = 0.0; vect_perp = RotAroundAxis(vect_perp, 6.35 * random, a); } else { vect_perp[0] = a[1]; vect_perp[1] = -a[0]; vect_perp[2] = 0.0; vect_perp.Normalize(); vect_perp = RotAroundAxis(vect_perp, 6.35 * random, a); } return vect_perp; } /// Performs a rotation of a 3D vector `vector` around a given axis `axis`, /// in the positive mathematical sens. /// /// @param[in] vector the vector we want to rotate /// @param[in] theta the amplitude of rotation (in radian) /// @param[in] axis (also a vector) /// @return the vector after rotation static Double3 RotAroundAxis(const Double3& vector, double theta, const Double3& axis) { auto naxis = axis; naxis.Normalize(); auto temp_1 = naxis * (vector * naxis); auto temp_2 = (vector - temp_1) * std::cos(-theta); auto temp_3 = CrossProduct(vector, naxis) * std::sin(-theta); return temp_1 + temp_2 + temp_3; } /// Returns the angle (in radian) between two vectors. /// @param a the first vector /// @param b the second vector /// @return the angle between them. static double AngleRadian(const Double3& a, const Double3& b) { return std::acos(a * b / (a.Norm() * b.Norm())); } /// Returns the projection of the first vector onto the second one. /// @param a /// @param b /// @return the projection of a onto b static Double3 ProjectionOnto(const Double3& a, const Double3& b) { double k = (a * b) / (b * b); return b * k; } }; } // namespace bdm #endif // CORE_UTIL_MATH_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_NUMA_H_ #define CORE_UTIL_NUMA_H_ #ifdef USE_NUMA #include #else #include inline int numa_available() { return 0; } inline int numa_num_configured_nodes() { return 1; } inline int numa_num_configured_cpus() { return omp_get_max_threads(); } inline int numa_run_on_node(int) { return 0; } inline int numa_node_of_cpu(int) { return 0; } inline int numa_move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags) { *status = 0; return 0; } // on linux in , but missing on MacOS inline int sched_getcpu() { return 0; } #endif // USE_NUMA #endif // CORE_UTIL_NUMA_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_RANDOM_H_ #define CORE_UTIL_RANDOM_H_ #include #include #include #include "core/container/math_array.h" #include "core/util/root.h" namespace bdm { /// Decorator for ROOT's TRandom3 class Random { public: Random(); Random& operator=(const Random& other); /// Forwards call to TRandom3::Uniform double Uniform(double max = 1.0); /// Forwards call to TRandom3::Uniform double Uniform(double min, double max); /// Returns an array of uniform random numbers in the interval (0, max) template MathArray UniformArray(double max = 1.0) { MathArray ret; for (uint64_t i = 0; i < N; i++) { ret[i] = Uniform(max); } return ret; } /// Returns an array of uniform random numbers in the interval (min, max) template MathArray UniformArray(double min, double max) { MathArray ret; for (uint64_t i = 0; i < N; i++) { ret[i] = Uniform(min, max); } return ret; } /// Forwards call to TRandom3::Gaus double Gaus(double mean = 0.0, double sigma = 1.0); /// Forwards call to TRandom3::SetSeed void SetSeed(double seed); /// Forwards call to TRandom3::GetSeed double GetSeed() const; private: TRandom3 generator_; BDM_CLASS_DEF_NV(Random, 1); }; } // namespace bdm #endif // CORE_UTIL_RANDOM_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_ROOT_H_ #define CORE_UTIL_ROOT_H_ #if defined(USE_DICT) #include #include #define BDM_CLASS_DEF(class_name, class_version_id) \ ClassDef(class_name, class_version_id) #define BDM_CLASS_DEF_NV(class_name, class_version_id) \ ClassDefNV(class_name, class_version_id) #define BDM_CLASS_DEF_OVERRIDE(class_name, class_version_id) \ ClassDefOverride(class_name, class_version_id) #define BDM_TEMPLATE_CLASS_DEF(class_name, class_version_id) \ private: \ static atomic_TClass_ptr fgIsA; \ \ public: \ static TClass* Class() { \ throw "This method should have been replaced by the ROOT dictionary."; \ } \ static const char* Class_Name(); \ static Version_t Class_Version() { return class_version_id; } \ static TClass* Dictionary(); \ virtual TClass* IsA() const { return class_name::Class(); } \ virtual void ShowMembers(TMemberInspector& insp) const { \ ::ROOT::Class_ShowMembers(class_name::Class(), this, insp); \ } \ virtual void Streamer(TBuffer&) { \ throw "This method should have been replaced by the ROOT dictionary."; \ } \ void StreamerNVirtual(TBuffer& ClassDef_StreamerNVirtual_b) { \ class_name::Streamer(ClassDef_StreamerNVirtual_b); \ } \ static const char* DeclFileName() { return __FILE__; } \ static int ImplFileLine(); \ static const char* ImplFileName(); \ static int DeclFileLine() { return __LINE__; } \ \ private: // NOLINT // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- #else #include "core/util/log.h" #define BDM_DICT_ERROR_MSG \ "You tried to use a ROOT dictionary function, but " \ "compiled BioDynaMo without dictionary support. " \ "Please configure with cmake -Ddict=on .. and recompile." /// Macro that inserts empty definitions of each function used for compilations /// without dictionaries. \n /// The compiler won't complain about missing functions. \n /// However, if ROOT functions are used that rely on dictionaries (e.g. backup) /// a runtime error will be thrown. #define BDM_NULL_CLASS_DEF(class_name, class_version_id) \ public: \ static TClass* Class() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static const char* Class_Name() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static Version_t Class_Version() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return class_version_id; \ } \ static TClass* Dictionary() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ virtual TClass* IsA() const { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return class_name::Class(); \ } \ virtual void ShowMembers(TMemberInspector& insp) const { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ virtual void Streamer(TBuffer&) { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ void StreamerNVirtual(TBuffer& ClassDef_StreamerNVirtual_b) { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ static const char* DeclFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int ImplFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return -1; \ } \ static const char* ImplFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int DeclFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return __LINE__; \ } \ \ private: // NOLINT #define BDM_NULL_CLASS_DEF_NV(class_name, class_version_id) \ public: \ static TClass* Class() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static const char* Class_Name() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static Version_t Class_Version() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return class_version_id; \ } \ static TClass* Dictionary() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ TClass* IsA() const { return class_name::Class(); } \ void ShowMembers(TMemberInspector& insp) const { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ void Streamer(TBuffer&) { Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); } \ void StreamerNVirtual(TBuffer& ClassDef_StreamerNVirtual_b) { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ static const char* DeclFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int ImplFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return -1; \ } \ static const char* ImplFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int DeclFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return __LINE__; \ } \ \ private: // NOLINT #define BDM_NULL_CLASS_DEF_OVERRIDE(class_name, class_version_id) \ public: \ static TClass* Class() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static const char* Class_Name() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static Version_t Class_Version() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return class_version_id; \ } \ static TClass* Dictionary() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ TClass* IsA() const override { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return class_name::Class(); \ } \ void ShowMembers(TMemberInspector& insp) const override { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ void Streamer(TBuffer&) override { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ void StreamerNVirtual(TBuffer& ClassDef_StreamerNVirtual_b) { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ } \ static const char* DeclFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int ImplFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return -1; \ } \ static const char* ImplFileName() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return nullptr; \ } \ static int DeclFileLine() { \ Log::Fatal("Dictionary", BDM_DICT_ERROR_MSG); \ return __LINE__; \ } \ \ private: // NOLINT /// Forward all calls to BDM_NULL_CLASS_DEF #define BDM_CLASS_DEF(class_name, class_version_id) \ BDM_NULL_CLASS_DEF(class_name, class_version_id) #define BDM_CLASS_DEF_NV(class_name, class_version_id) \ BDM_NULL_CLASS_DEF_NV(class_name, class_version_id) #define BDM_CLASS_DEF_OVERRIDE(class_name, class_version_id) \ BDM_NULL_CLASS_DEF_OVERRIDE(class_name, class_version_id) #define BDM_TEMPLATE_CLASS_DEF(class_name, class_version_id) \ BDM_NULL_CLASS_DEF(class_name, class_version_id) #endif // defined(USE_DICT) #endif // CORE_UTIL_ROOT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_SPINLOCK_H_ #define CORE_UTIL_SPINLOCK_H_ #include class Spinlock { public: Spinlock() {} void lock() { // NOLINT while (flag_.test_and_set()) { // spin ; } } void unlock() { // NOLINT flag_.clear(); } private: std::atomic_flag flag_ = ATOMIC_FLAG_INIT; }; #endif // CORE_UTIL_SPINLOCK_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_STRING_H_ #define CORE_UTIL_STRING_H_ #include #include namespace bdm { namespace detail { /// @brief Appends the closing string to the message /// /// @param[in] ss the stringstream that holds the message /// inline void ConcatNextPart(std::ostringstream* ss) {} /// @brief Appends the next part of the message /// /// @param[in] ss the stringstream that holds the message /// @param[in] arg the part to be appended next /// @param[in] parts the rest of the parts, waiting to be appended /// template inline void ConcatNextPart(std::ostringstream* ss, const T& arg, const Args&... parts) { *ss << arg; ConcatNextPart(ss, parts...); } } // namespace detail /// @brief Concatenates all arguments into a string. /// Equivalent to streaming all arguments into a stringstream and calling /// `stream.str()` /// /// @param[in] parts objects that compose the entire message /// /// @returns A unique string pointer to the message /// template inline std::string Concat(const Args&... parts) { std::ostringstream message; detail::ConcatNextPart(&message, parts...); return message.str(); } } // namespace bdm #endif // CORE_UTIL_STRING_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_THREAD_INFO_H_ #define CORE_UTIL_THREAD_INFO_H_ #include #include #include #include "core/util/log.h" #include "core/util/numa.h" namespace bdm { /// \brief This class stores information about each thread. (e.g. to which NUMA /// node it belongs to.) /// NB: Threads **must** be bound to CPUs using `OMP_PROC_BIND=true`. class ThreadInfo { public: static ThreadInfo* GetInstance() { static ThreadInfo kInstance; return &kInstance; } /// Returns the number of NUMA nodes on this machine int GetNumaNodes() const { return numa_nodes_; } /// Returns the numa node the given openmp thread is bound to. int GetNumaNode(int omp_thread_id) const { return thread_numa_mapping_[omp_thread_id]; } /// Returns the number of threads in a given NUMA node. int GetThreadsInNumaNode(int numa_node) const { return threads_in_numa_[numa_node]; } /// Return the numa thread id of an openmp thread. int GetNumaThreadId(int omp_thread_id) const { return numa_thread_id_[omp_thread_id]; } /// Return the maximum number of threads. int GetMaxThreads() const { return max_threads_; } /// Renews the metadata.\n /// Whenever a thread is scheduled on a different cpu, e.g. using /// `numa_run_on_node`, `Renew()` must be called to update the thread /// metadata. void Renew() { max_threads_ = omp_get_max_threads(); numa_nodes_ = numa_num_configured_nodes(); thread_numa_mapping_.clear(); numa_thread_id_.clear(); threads_in_numa_.clear(); thread_numa_mapping_.resize(max_threads_, 0); numa_thread_id_.resize(max_threads_, 0); threads_in_numa_.resize(numa_nodes_, 0); // (openmp thread id -> numa node) #pragma omp parallel { int tid = omp_get_thread_num(); thread_numa_mapping_[tid] = numa_node_of_cpu(sched_getcpu()); } // (numa -> number of associated threads), and // (omp_thread_id -> thread id in numa) for (uint16_t n = 0; n < numa_nodes_; n++) { uint64_t cnt = 0; for (uint64_t t = 0; t < max_threads_; t++) { int numa = thread_numa_mapping_[t]; if (n == numa) { numa_thread_id_[t] = cnt; cnt++; } } threads_in_numa_[n] = cnt; } } friend std::ostream& operator<<(std::ostream& str, const ThreadInfo& ti) { str << "max_threads " << ti.max_threads_ << "\nnum_numa nodes " << ti.numa_nodes_; str << "\nthread to numa mapping "; for (auto& el : ti.thread_numa_mapping_) { str << " " << el; } str << "\nthread id in numa node "; for (auto& el : ti.numa_thread_id_) { str << " " << el; } str << "\nnum threads per numa "; for (auto& el : ti.threads_in_numa_) { str << " " << el; } str << "\n"; return str; } private: /// Maximum number of threads for this simulation. uint64_t max_threads_; /// Number of NUMA nodes on this machine. uint16_t numa_nodes_; /// Contains the mapping thread id -> numa node \n /// vector position = omp_thread_id \n /// vector value = numa node std::vector thread_numa_mapping_; /// Contains the mapping omp_thread_id -> numa thread id \n /// each thread in a numa domain has a unique id in the range 0 to number \n /// of threads in this numa domain std::vector numa_thread_id_; /// Contains the mapping numa node -> total number of threads in this numa /// node \n /// vector position: numa node \n /// vector value number of threads std::vector threads_in_numa_; ThreadInfo() { if (omp_get_proc_bind() != 1) { Log::Fatal("ThreadInfo", "The environmental variable OMP_PROC_BIND must be set to " "true. On Linux run 'export OMP_PROC_BIND=true' prior to " "running BioDynaMo"); } Renew(); } }; } // namespace bdm #endif // CORE_UTIL_THREAD_INFO_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_TIMING_H_ #define CORE_UTIL_TIMING_H_ #include #include #include #include "core/param/param.h" #include "core/simulation.h" #include "core/util/timing_aggregator.h" namespace bdm { static TimingAggregator gStatistics; class Timing { public: typedef std::chrono::high_resolution_clock Clock; static int64_t Timestamp() { using std::chrono::milliseconds; using std::chrono::duration_cast; auto time = Clock::now(); auto since_epoch = time.time_since_epoch(); auto millis = duration_cast(since_epoch); return millis.count(); } template static void Time(const std::string& description, TFunctor&& f) { static bool kUseTimer = Simulation::GetActive()->GetParam()->statistics_; if (kUseTimer) { Timing timing(description, &gStatistics); f(); } else { f(); } } explicit Timing(const std::string& description = "") : start_{Timestamp()}, text_{description} {} Timing(const std::string& description, TimingAggregator* aggregator) : start_{Timestamp()}, text_{description}, aggregator_{aggregator} {} ~Timing() { int64_t duration = (Timestamp() - start_); if (aggregator_ == nullptr) { std::cout << text_ << " " << duration << " ms" << std::endl; } else { aggregator_->AddEntry(text_, duration); } } private: int64_t start_; std::string text_; TimingAggregator* aggregator_ = nullptr; }; } // namespace bdm #endif // CORE_UTIL_TIMING_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_TIMING_AGGREGATOR_H_ #define CORE_UTIL_TIMING_AGGREGATOR_H_ #include #include #include #include #include "core/util/math.h" namespace bdm { class TimingAggregator { public: TimingAggregator() {} ~TimingAggregator() {} void AddEntry(const std::string& key, int64_t value) { if (!timings_.count(key)) { std::vector data; data.push_back(value); timings_[key] = data; } else { timings_[key].push_back(value); } } void AddDescription(const std::string text) { descriptions_.push_back(text); } private: std::map> timings_; std::vector descriptions_; friend std::ostream& operator<<(std::ostream& os, const TimingAggregator& p); }; inline std::ostream& operator<<(std::ostream& os, const TimingAggregator& ta) { os << std::endl; if (ta.timings_.size() != 0) { os << "\033[1mTotal execution time per operation:\033[0m" << std::endl; for (auto& timing : ta.timings_) { os << timing.first << ": " << std::accumulate(timing.second.begin(), timing.second.end(), 0) << std::endl; } } else { os << "No statistics were gathered!" << std::endl; } return os; } } // namespace bdm #endif // CORE_UTIL_TIMING_AGGREGATOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_TUPLE_H_ #define CORE_UTIL_TUPLE_H_ #include #include namespace bdm { using std::size_t; namespace detail { // Inspiration taken from: // https://stackoverflow.com/questions/21062864/optimal-way-to-access-stdtuple-element-in-runtime-by-index /// Applies the given function on tuple element TIndex /// This function is called from `detail::Apply`. It has a compile time index /// to be used in `std::get` to obtain the right type within the tuple. /// The obtained type is then passed to `function`. template void ApplyImpl(TTuple* t, TFunction&& function) { function(&std::get(*t)); } /// Does the translation of runtime index to compile time index by using /// a look up table (lut) array of `ApplyImpl` functions. Forwards the call to /// ApplyImpl template void Apply(TTuple* t, size_t index, TFunction&& f, std::index_sequence) { using ApplyImplSignature = void(TTuple*, TFunction &&); // NOLINT // create lookup table that maps runtime index to right ApplyImpl function static constexpr ApplyImplSignature* kLut[] = { &ApplyImpl...}; kLut[index](t, f); } template struct GetIndexImpl; template struct GetIndexImpl { int static constexpr GetValue() { if (std::is_same::value) { return Counter; } else { return -1; } } }; template struct GetIndexImpl { int static constexpr GetValue() { // TODO(lukas) after changing to c++17: change to if constexpr (...) if (std::is_same::value) { return Counter; } else { return GetIndexImpl::GetValue(); } } }; } // namespace detail /// This function applies the given function on tuple element index. /// The peculiarity is that index is a runtime parameter. /// `std::get(tuple)` however, requires a compile time constant. /// Therefore, `Apply` performs the translation to a compile time index. /// @param t std::tuple or similar type that supports std::get /// @param index runtime index specifying the type within t /// @param f function that should be executed on the type template void Apply(TTuple* t, size_t index, TFunction&& f) { detail::Apply(t, index, f, std::make_index_sequence::value>()); } /// Return the index of the first occurence of type T within the variadic /// template parameter Types. template inline constexpr int GetIndex() { return bdm::detail::template GetIndexImpl::GetValue(); } } // namespace bdm #endif // CORE_UTIL_TUPLE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_UTIL_TYPE_H_ #define CORE_UTIL_TYPE_H_ #include #include #include "core/shape.h" #include "core/util/string.h" namespace bdm { /// Type trait which defines a ternary operator for types which can be evaluated /// at compile time template struct type_ternary_operator {}; // NOLINT template struct type_ternary_operator { typedef T type; // NOLINT }; template struct type_ternary_operator { typedef U type; // NOLINT }; /// Type trait that converts `T*`, `T&`, `T&&`, `T*&` to `T` template using raw_type = std::remove_pointer_t>; // NOLINT /// Use this cast if you want to downcast an object to a known type with extra /// safety. The extra safety check will only be performed in Debug mode. template TTo bdm_static_cast(TFrom from) { // NOLINT assert(dynamic_cast(from) && Concat("Could not cast object ", from, " to type ", typeid(TTo).name()) .c_str()); return static_cast(from); } } // namespace bdm #endif // CORE_UTIL_TYPE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- // Define empty function bodies if Vtune wasn't found on your system. // Functions will emit a warning on the first invocation. #ifndef CORE_UTIL_VTUNE_H_ #define CORE_UTIL_VTUNE_H_ #ifdef USE_VTUNE #include #else #include "core/util/log.h" /// This macro should be inserted in the function body of each wrapper. /// It emits a Warning the first time it is called. #define BDM_VTUNE_LOG_WARNING() \ static bool warning_logged = false; \ if (!warning_logged) { \ bdm::Log::Warning( \ "Vtune", \ "Vtune was not found on your system. Call to %s won't have any " \ "effect", \ __FUNCTION__); \ warning_logged = true; \ } struct __itt_domain {}; // NOLINT struct __itt_string_handle {}; // NOLINT struct __itt_null_t {}; // NOLINT static constexpr __itt_null_t __itt_null; // NOLINT inline __itt_domain* __itt_domain_create(const char*) { // NOLINT BDM_VTUNE_LOG_WARNING(); return nullptr; } inline __itt_string_handle* __itt_string_handle_create(const char*) { // NOLINT BDM_VTUNE_LOG_WARNING(); return nullptr; } inline void __itt_task_begin(__itt_domain*, __itt_null_t, // NOLINT __itt_null_t, __itt_string_handle*) { BDM_VTUNE_LOG_WARNING(); } inline void __itt_task_end(__itt_domain*) { // NOLINT BDM_VTUNE_LOG_WARNING(); } // NOLINT inline void __itt_pause() { BDM_VTUNE_LOG_WARNING(); } // NOLINT inline void __itt_resume() { BDM_VTUNE_LOG_WARNING(); } // NOLINT #endif // USE_VTUNE #endif // CORE_UTIL_VTUNE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_VISUALIZATION_CATALYST_ADAPTOR_H_ #define CORE_VISUALIZATION_CATALYST_ADAPTOR_H_ // check for ROOTCLING was necessary, due to ambigous reference to namespace // detail when using ROOT I/O #if defined(USE_CATALYST) && !defined(__ROOTCLING__) #include #include #include #include #include #include #include #include #include "core/param/param.h" #include "core/resource_manager.h" #include "core/scheduler.h" #include "core/shape.h" #include "core/simulation.h" #include "core/util/log.h" #include "core/visualization/catalyst_helper_structs.h" #include "core/visualization/catalyst_so_visitor.h" #include "core/visualization/insitu_pipeline.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace bdm { /// The class that bridges the simulation code with ParaView. class CatalystAdaptor { public: /// Initializes Catalyst with the predefined pipeline and allocates memory /// for the VTK grid structures /// /// @param[in] script The Python script that contains the pipeline /// explicit CatalystAdaptor(const std::string& script) : python_script_(script) { counter_++; } ~CatalystAdaptor() { auto* param = Simulation::GetActive()->GetParam(); counter_--; if (pipeline_) { g_processor_->RemovePipeline(pipeline_); pipeline_->Delete(); pipeline_ = nullptr; } if (counter_ == 0 && g_processor_) { g_processor_->RemoveAllPipelines(); g_processor_->Finalize(); g_processor_->Delete(); g_processor_ = nullptr; } if (param->export_visualization_ && param->visualization_export_generate_pvsm_) { GenerateSimulationInfoJson(vtk_so_grids_, vtk_dgrids_); GenerateParaviewState(); } for (auto& el : vtk_so_grids_) { delete el.second; } for (auto& el : vtk_dgrids_) { delete el.second; } } /// Visualize one timestep based on the configuration in `Param` void Visualize(uint64_t total_steps, bool last_iteration) { if (!initialized_) { Initialize(); initialized_ = true; } auto* param = Simulation::GetActive()->GetParam(); if (total_steps % param->visualization_export_interval_ != 0) { return; } if (param->live_visualization_) { double time = param->simulation_time_step_ * total_steps; LiveVisualization(time, total_steps, last_iteration); } if (param->export_visualization_) { double time = param->simulation_time_step_ * total_steps; ExportVisualization(time, total_steps, last_iteration); } } private: static vtkCPProcessor* g_processor_; static std::atomic counter_; /// only needed for live visualization InSituPipeline* pipeline_ = nullptr; std::string python_script_; bool initialized_ = false; bool exclusive_export_viz_ = false; std::unordered_map vtk_so_grids_; std::unordered_map vtk_dgrids_; static constexpr char const* kSimulationInfoJson = "simulation_info.json"; friend class CatalystAdaptorTest_GenerateSimulationInfoJson_Test; friend class CatalystAdaptorTest_GenerateParaviewState_Test; friend class CatalystAdaptorTest_DISABLED_CheckVisualizationSelection_Test; friend class DISABLED_DiffusionTest_ModelInitializer_Test; /// Parameters might be set after the constructor has been called. /// Therefore, we defer initialization to the first invocation of /// `Visualize`. void Initialize() { auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); exclusive_export_viz_ = param->export_visualization_ && !param->live_visualization_; if (param->live_visualization_ || param->export_visualization_) { if (g_processor_ == nullptr) { g_processor_ = vtkCPProcessor::New(); g_processor_->Initialize(); } if (param->live_visualization_ && g_processor_->GetNumberOfPipelines() != 0) { Log::Fatal("CatalystAdaptor", "Live visualization does not support multiple " "simulations. Turning off live visualization for ", sim->GetUniqueName()); } else if (param->python_catalyst_pipeline_) { vtkNew pipeline; pipeline->Initialize(python_script_.c_str()); g_processor_->AddPipeline(pipeline.GetPointer()); } else if (!exclusive_export_viz_) { pipeline_ = new InSituPipeline(); g_processor_->AddPipeline(pipeline_); } vtkNew data_description; data_description->SetTimeData(0, 0); auto* param = Simulation::GetActive()->GetParam(); for (auto& pair : param->visualize_sim_objects_) { vtk_so_grids_[pair.first.c_str()] = new VtkSoGrid(pair.first.c_str(), data_description); } for (auto& entry : param->visualize_diffusion_) { vtk_dgrids_[entry.name_] = new VtkDiffusionGrid(entry.name_, data_description); } } } /// Applies the pipeline to the simulation objects during live visualization /// /// @param[in] time The simulation time /// @param[in] step The time step duration /// @param[in] last_time_step Last time step or not /// void LiveVisualization(double time, size_t step, bool last_time_step) { vtkNew data_description; data_description->SetTimeData(time, step); CreateVtkObjects(data_description); if (last_time_step == true) { data_description->ForceOutputOn(); } if (pipeline_ != nullptr) { if (!(pipeline_->IsInitialized())) { pipeline_->Initialize(vtk_so_grids_); } } g_processor_->CoProcess(data_description.GetPointer()); } /// Exports the visualized objects to file, so that they can be imported and /// visualized in ParaView at a later point in time /// /// @param[in] time The simulation time /// @param[in] step The time step /// @param[in] last_time_step The last time step /// inline void ExportVisualization(double time, size_t step, bool last_time_step) { vtkNew data_description; data_description->SetTimeData(time, step); CreateVtkObjects(data_description); if (last_time_step == true) { data_description->ForceOutputOn(); } WriteToFile(step); } /// Creates the VTK objects that represent the simulation objects in ParaView. /// /// @param data_description The data description /// void CreateVtkObjects( const vtkNew& data_description) { // NOLINT BuildSimObjectsVTKStructures(data_description); BuildDiffusionGridVTKStructures(data_description); } // --------------------------------------------------------------------------- // simulation objects // Process a single simulation object void ProcessSimObject(const SimObject* so, const vtkNew& data_description) { auto* param = Simulation::GetActive()->GetParam(); auto so_name = so->GetTypeName(); if (param->visualize_sim_objects_.find(so_name) != param->visualize_sim_objects_.end()) { // assert(vtk_so_grids_.find(so->GetTypeName()) != vtk_so_grids_.end() && // Concat("VtkSoGrid for ", so->GetTypeName(), " not found!"); auto* vsg = vtk_so_grids_[so->GetTypeName()]; if (!vsg->initialized_) { vsg->Init(so); } // If we segfault at here it probably means that there is a problem // with the pipeline (either the C++ pipeline or Python pipeline) // We do not need to RequestDataDescription in Export Mode, because // we do not make use of Catalyst CoProcessing capabilities if (exclusive_export_viz_ || (g_processor_->RequestDataDescription( data_description.GetPointer())) != 0) { CatalystSoVisitor visitor(vsg); so->ForEachDataMemberIn(vsg->vis_data_members_, &visitor); } } } /// Create the required vtk objects to visualize simulation objects. void BuildSimObjectsVTKStructures( const vtkNew& data_description) { auto* rm = Simulation::GetActive()->GetResourceManager(); rm->ApplyOnAllElements( [&](SimObject* so) { ProcessSimObject(so, data_description); }); } // --------------------------------------------------------------------------- // diffusion grids /// Sets the properties of the diffusion VTK grid structures void ProcessDiffusionGrid( const DiffusionGrid* grid, const vtkNew& data_description) { auto* param = Simulation::GetActive()->GetParam(); auto name = grid->GetSubstanceName(); // get visualization config const Param::VisualizeDiffusion* vd = nullptr; for (auto& entry : param->visualize_diffusion_) { if (entry.name_ == name) { vd = &entry; } } if (vd != nullptr) { auto* vdg = vtk_dgrids_[grid->GetSubstanceName()]; if (!vdg->used_) { vdg->Init(); } // If we segfault at here it probably means that there is a problem // with the pipeline (either the C++ pipeline or Python pipeline) // We do not need to RequestDataDescription in Export Mode, because // we do not make use of Catalyst CoProcessing capabilities if (exclusive_export_viz_ || (g_processor_->RequestDataDescription( data_description.GetPointer())) != 0) { auto num_boxes = grid->GetNumBoxesArray(); auto grid_dimensions = grid->GetDimensions(); auto box_length = grid->GetBoxLength(); auto total_boxes = grid->GetNumBoxes(); double origin_x = grid_dimensions[0]; double origin_y = grid_dimensions[2]; double origin_z = grid_dimensions[4]; vdg->data_->SetOrigin(origin_x, origin_y, origin_z); vdg->data_->SetDimensions(num_boxes[0], num_boxes[1], num_boxes[2]); vdg->data_->SetSpacing(box_length, box_length, box_length); if (vdg->concentration_) { auto* co_ptr = const_cast(grid->GetAllConcentrations()); vdg->concentration_->SetArray(co_ptr, static_cast(total_boxes), 1); } if (vdg->gradient_) { auto gr_ptr = const_cast(grid->GetAllGradients()); vdg->gradient_->SetArray(gr_ptr, static_cast(total_boxes * 3), 1); } } } } /// Create the required vtk objects to visualize diffusion grids. void BuildDiffusionGridVTKStructures( const vtkNew& data_description) { auto* rm = Simulation::GetActive()->GetResourceManager(); rm->ApplyOnAllDiffusionGrids([&](DiffusionGrid* grid) { ProcessDiffusionGrid(grid, data_description); }); } // --------------------------------------------------------------------------- // generate files /// Helper function to write simulation objects to file. It loops through the /// vectors of VTK grid structures and calls the internal VTK writer methods /// /// @param[in] step The step /// void WriteToFile(size_t step) { auto* sim = Simulation::GetActive(); for (auto& el : vtk_so_grids_) { vtkNew cells_writer; auto filename = Concat(sim->GetOutputDir(), "/", el.second->name_, "-", step, ".pvtu"); cells_writer->SetFileName(filename.c_str()); cells_writer->SetInputData(el.second->data_); cells_writer->Update(); } for (auto& entry : vtk_dgrids_) { vtkNew dgrid_writer; const auto& substance_name = entry.second->name_; auto filename = Concat(sim->GetOutputDir(), "/", substance_name, "-", step, ".pvti"); dgrid_writer->SetFileName(filename.c_str()); dgrid_writer->SetInputData(entry.second->data_); dgrid_writer->Update(); } } /// If the user selects the visualiation option export, we need to pass the /// information on the C++ side to a python script which generates the /// ParaView state file. The Json file is generated inside this function /// \see GenerateParaviewState static void GenerateSimulationInfoJson( const std::unordered_map& vtk_so_grids, const std::unordered_map& vtk_dgrids) { auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); // simulation objects std::stringstream sim_objects; uint64_t num_sim_objects = param->visualize_sim_objects_.size(); uint64_t counter = 0; for (const auto& entry : param->visualize_sim_objects_) { std::string so_name = entry.first; auto search = vtk_so_grids.find(so_name); if (search == vtk_so_grids.end()) { continue; } auto* so_grid = search->second; // user wanted to export this simulation object type, but not a single // instance has been created during the entire simulation if (!so_grid->initialized_) { Log::Warning("Visualize Simulation Objects", "You are trying to visualize simulation object type ", so_name, ", but not a single instance has been created during the " "entire simulation. Please make sure the names in the " "configuration file match the ones in the simulation."); num_sim_objects--; continue; } sim_objects << " {" << std::endl << " \"name\":\"" << so_name << "\"," << std::endl; if (so_grid->shape_ == Shape::kSphere) { sim_objects << " \"glyph\":\"Glyph\"," << std::endl << " \"shape\":\"Sphere\"," << std::endl << " \"scaling_attribute\":\"diameter_\"" << std::endl; } else if (so_grid->shape_ == kCylinder) { sim_objects << " \"glyph\":\"BDMGlyph\"," << std::endl << " \"shape\":\"Cylinder\"," << std::endl << " \"x_scaling_attribute\":\"diameter_\"," << std::endl << " \"y_scaling_attribute\":\"actual_length_\"," << std::endl << " \"z_scaling_attribute\":\"diameter_\"," << std::endl << " \"Vectors\":\"spring_axis_\"," << std::endl << " \"MassLocation\":\"mass_location_\"" << std::endl; } sim_objects << " }"; if (counter != num_sim_objects - 1) { sim_objects << ","; } sim_objects << std::endl; counter++; } // extracellular substances std::stringstream substances; uint64_t num_substances = param->visualize_diffusion_.size(); for (uint64_t i = 0; i < num_substances; i++) { auto& name = param->visualize_diffusion_[i].name_; auto search = vtk_dgrids.find(name); if (search == vtk_dgrids.end()) { continue; } auto* dgrid = search->second; // user wanted to export this substance, but it did not exist during // the entire simulation if (!dgrid->used_) { Log::Warning( "Visualize Diffusion Grids", "You are trying to visualize diffusion grid ", name, ", but it has not been created during the entire simulation. " "Please make sure the names in the " "configuration file match the ones in the simulation."); num_substances--; continue; } substances << " { \"name\":\"" << name << "\", "; std::string has_gradient = param->visualize_diffusion_[i].gradient_ ? "true" : "false"; substances << "\"has_gradient\":\"" << has_gradient << "\" }"; if (i != num_substances - 1) { substances << "," << std::endl; } } // write to file std::ofstream ofstr; ofstr.open(Concat(sim->GetOutputDir(), "/", kSimulationInfoJson)); ofstr << "{" << std::endl << " \"simulation\": {" << std::endl << " \"name\":\"" << sim->GetUniqueName() << "\"," << std::endl << " \"result_dir\":\"" << sim->GetOutputDir() << "\"" << std::endl << " }," << std::endl << " \"sim_objects\": [" << std::endl << sim_objects.str() << " ]," << std::endl << " \"extracellular_substances\": [" << std::endl << substances.str() << std::endl << " ]" << std::endl << "}" << std::endl; ofstr.close(); } /// This function generates the Paraview state based on the exported files /// Therefore, the user can load the visualization simply by opening the pvsm /// file and does not have to perform a lot of manual steps. static void GenerateParaviewState() { auto* sim = Simulation::GetActive(); std::stringstream python_cmd; std::string bdm_src_dir = std::getenv("BDM_SRC_DIR"); python_cmd << bdm_src_dir << "/../third_party/paraview/bin/pvpython " << bdm_src_dir << "/core/visualization/generate_pv_state.py " << sim->GetOutputDir() << "/" << kSimulationInfoJson; int ret_code = system(python_cmd.str().c_str()); if (ret_code) { Log::Fatal("CatalystAdaptor::GenerateParaviewState", "Error during generation of ParaView state\n", "Command\n", python_cmd.str()); } } }; } // namespace bdm #else #include #include #include "core/shape.h" namespace bdm { struct VtkSoGrid; struct VtkDiffusionGrid; /// False front (to ignore Catalyst in gtests) class CatalystAdaptor { public: explicit CatalystAdaptor(const std::string& script) {} void Visualize(uint64_t, bool) {} private: friend class CatalystAdaptorTest_GenerateSimulationInfoJson_Test; friend class CatalystAdaptorTest_GenerateParaviewState_Test; friend class CatalystAdaptorTest_CheckVisualizationSelection_Test; friend class DISABLED_DiffusionTest_ModelInitializer_Test; void LiveVisualization(double time, size_t time_step, bool last_time_step) {} void ExportVisualization(double step, size_t time_step, bool last_time_step) { } void WriteToFile(size_t step) {} static void GenerateSimulationInfoJson( const std::unordered_map& vtk_so_grids, const std::unordered_map& vtk_dgrids) {} static void GenerateParaviewState() {} }; } // namespace bdm #endif // defined(USE_CATALYST) && !defined(__ROOTCLING__) #endif // CORE_VISUALIZATION_CATALYST_ADAPTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_VISUALIZATION_CATALYST_HELPER_STRUCTS_H_ #define CORE_VISUALIZATION_CATALYST_HELPER_STRUCTS_H_ // check for ROOTCLING was necessary, due to ambigous reference to namespace // detail when using ROOT I/O #if defined(USE_CATALYST) && !defined(__ROOTCLING__) #include #include #include #include "core/param/param.h" #include "core/shape.h" #include "core/sim_object/sim_object.h" #include "core/simulation.h" #include #include #include #include #include #include #include #include #include #include namespace bdm { /// Add data member `time_step_` to a vtkDataArray. /// It is required to determine when the data array should be reset. (At the /// beginning of each iteration) struct VtkDataArrayWrapper { explicit VtkDataArrayWrapper(vtkDataArray* data) : data_(data) {} vtkDataArray* data_; uint64_t time_step_ = 0; }; /// Adds additional data members to the `vtkUnstructuredGrid` required by /// `CatalystAdaptor` to visualize simulation objects. struct VtkSoGrid { VtkSoGrid(const char* type_name, const vtkNew& data_description) { data_ = vtkUnstructuredGrid::New(); name_ = type_name; data_description->AddInput(type_name); data_description->GetInputDescriptionByName(type_name)->SetGrid(data_); } ~VtkSoGrid() { name_ = ""; data_->Delete(); data_ = nullptr; vis_data_members_.clear(); data_arrays_.clear(); } void Init(const SimObject* so) { auto type_name = so->GetTypeName(); vis_data_members_ = so->GetRequiredVisDataMembers(); auto* param = Simulation::GetActive()->GetParam(); for (auto& dm : param->visualize_sim_objects_.at(type_name)) { vis_data_members_.insert(dm); } shape_ = so->GetShape(); initialized_ = true; } bool initialized_ = false; std::string name_; vtkUnstructuredGrid* data_ = nullptr; Shape shape_; std::set vis_data_members_; std::unordered_map data_arrays_; }; /// Adds additional data members to the `vtkImageData` required by /// `CatalystAdaptor` to visualize diffusion grid. struct VtkDiffusionGrid { VtkDiffusionGrid(const std::string& name, const vtkNew& data_description) { data_ = vtkImageData::New(); name_ = name; // get visualization config auto* param = Simulation::GetActive()->GetParam(); const Param::VisualizeDiffusion* vd = nullptr; for (auto& entry : param->visualize_diffusion_) { if (entry.name_ == name) { vd = &entry; break; } } // Add attribute data if (vd->concentration_) { vtkNew concentration; concentration->SetName("Substance Concentration"); concentration_ = concentration.GetPointer(); data_->GetPointData()->AddArray(concentration.GetPointer()); } if (vd->gradient_) { vtkNew gradient; gradient->SetName("Diffusion Gradient"); gradient->SetNumberOfComponents(3); gradient_ = gradient.GetPointer(); data_->GetPointData()->AddArray(gradient.GetPointer()); } data_description->AddInput(name.c_str()); data_description->GetInputDescriptionByName(name.c_str())->SetGrid(data_); } ~VtkDiffusionGrid() { name_ = ""; data_->Delete(); data_ = nullptr; concentration_ = nullptr; gradient_ = nullptr; } void Init() { used_ = true; } bool used_ = false; std::string name_; vtkImageData* data_ = nullptr; vtkDoubleArray* concentration_ = nullptr; vtkDoubleArray* gradient_ = nullptr; }; } // namespace bdm #endif // defined(USE_CATALYST) && !defined(__ROOTCLING__) #endif // CORE_VISUALIZATION_CATALYST_HELPER_STRUCTS_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_VISUALIZATION_CATALYST_SO_VISITOR_H_ #define CORE_VISUALIZATION_CATALYST_SO_VISITOR_H_ // check for ROOTCLING was necessary, due to ambigous reference to namespace // detail when using ROOT I/O #if defined(USE_CATALYST) && !defined(__ROOTCLING__) #include #include "core/container/math_array.h" #include "core/scheduler.h" #include "core/sim_object/so_visitor.h" #include "core/simulation.h" #include "core/visualization/catalyst_helper_structs.h" namespace bdm { /// This simulation object visitor is used to extract data from simulation /// objects. It also creates the required vtk data structures and resets them /// at the beginning of each iteration. class CatalystSoVisitor : public SoVisitor { public: explicit CatalystSoVisitor(VtkSoGrid* so_grid) : so_grid_(so_grid) {} virtual ~CatalystSoVisitor() {} void Visit(const std::string& dm_name, size_t type_hash_code, const void* data) override { if (type_hash_code == typeid(double).hash_code()) { Double(dm_name, data); } else if (type_hash_code == typeid(int).hash_code()) { Int(dm_name, data); } else if (type_hash_code == typeid(uint64_t).hash_code()) { Uint64T(dm_name, data); } else if (type_hash_code == typeid(Double3).hash_code()) { MathArray3(dm_name, data); } else if (type_hash_code == typeid(std::array).hash_code()) { Int3(dm_name, data); } else { Log::Fatal("CatalystSoVisitor::Visit", "This data member is not supported for visualization"); } } void Double(const std::string& dm_name, const void* d) { auto& data = *reinterpret_cast(d); auto* vtk_array = GetDataArray(dm_name); vtk_array->InsertNextTuple1(data); } void MathArray3(const std::string& dm_name, const void* d) { auto& data = *reinterpret_cast(d); auto* vtk_array = GetDouble3Array(dm_name); // TODO(lukas, ahmad) is there a better way? vtk_array->InsertNextTuple3(data[0], data[1], data[2]); } void Int(const std::string& dm_name, const void* d) { auto& data = *reinterpret_cast(d); auto* vtk_array = GetDataArray(dm_name); vtk_array->InsertNextTuple1(data); } void Uint64T(const std::string& dm_name, const void* d) { auto& data = *reinterpret_cast(d); auto* vtk_array = GetDataArray(dm_name); vtk_array->InsertNextTuple1(data); } void Int3(const std::string& dm_name, const void* d) { auto& data = *reinterpret_cast*>(d); auto* vtk_array = GetDataArray(dm_name, 3); // TODO(lukas, ahmad) is there a better way? vtk_array->InsertNextTuple3(data[0], data[1], data[2]); } private: VtkSoGrid* so_grid_; template TDataArray* GetDataArray(const std::string& dm_name, int components = 1) const { TDataArray* vtk_array = nullptr; auto& data_arrays = so_grid_->data_arrays_; auto search = data_arrays.find(dm_name); if (search != data_arrays.end()) { auto& da_wrapper = search->second; vtk_array = static_cast(da_wrapper.data_); // reset auto* scheduler = Simulation::GetActive()->GetScheduler(); if (da_wrapper.time_step_ != scheduler->GetSimulatedSteps()) { vtk_array->Reset(); da_wrapper.time_step_ = scheduler->GetSimulatedSteps(); } } else { // create vtkNew new_vtk_array; new_vtk_array->SetName(dm_name.c_str()); vtk_array = new_vtk_array.GetPointer(); vtk_array->SetNumberOfComponents(components); auto* point_data = so_grid_->data_->GetPointData(); point_data->AddArray(vtk_array); data_arrays.insert({dm_name, VtkDataArrayWrapper(vtk_array)}); } return vtk_array; } vtkDoubleArray* GetDouble3Array(const std::string& dm_name) const { vtkDoubleArray* vtk_array = nullptr; auto& data_arrays = so_grid_->data_arrays_; auto search = data_arrays.find(dm_name); if (search != data_arrays.end()) { auto& da_wrapper = search->second; vtk_array = static_cast(da_wrapper.data_); // reset auto* scheduler = Simulation::GetActive()->GetScheduler(); if (da_wrapper.time_step_ != scheduler->GetSimulatedSteps()) { vtk_array->Reset(); da_wrapper.time_step_ = scheduler->GetSimulatedSteps(); } } else { // create vtkNew new_vtk_array; new_vtk_array->SetName(dm_name.c_str()); vtk_array = new_vtk_array.GetPointer(); vtk_array->SetNumberOfComponents(3); data_arrays.insert({dm_name, VtkDataArrayWrapper(vtk_array)}); if (dm_name == "position_") { // TODO(lukas) performance vtkNew points; points->SetData(vtk_array); so_grid_->data_->SetPoints(points.GetPointer()); } else if (dm_name == "mass_location_") { // create points with position {0, 0, 0} // BDMGlyph will rotate and translate based on the attribute data vtkNew points; points->SetData(vtk_array); so_grid_->data_->SetPoints(points.GetPointer()); so_grid_->data_->GetPointData()->AddArray(vtk_array); } else { so_grid_->data_->GetPointData()->AddArray(vtk_array); } } return vtk_array; } }; } // namespace bdm #endif // defined(USE_CATALYST) && !defined(__ROOTCLING__) #endif // CORE_VISUALIZATION_CATALYST_SO_VISITOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_VISUALIZATION_INSITU_PIPELINE_H_ #define CORE_VISUALIZATION_INSITU_PIPELINE_H_ #include #include #include #include #include #include "core/util/log.h" #if defined(USE_CATALYST) && !defined(__ROOTCLING__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/visualization/catalyst_helper_structs.h" #endif // defined(USE_CATALYST) && !defined(__ROOTCLING__) namespace bdm { #if defined(USE_CATALYST) && !defined(__ROOTCLING__) class InSituPipeline : public vtkCPPipeline { public: vtkTypeMacro(InSituPipeline, vtkCPPipeline); InSituPipeline() { this->pipeline_created_ = false; // Get current proxy manager proxy_manager_ = vtkSMProxyManager::GetProxyManager(); session_manager_ = proxy_manager_->GetActiveSessionProxyManager(); controller_ = vtkSMParaViewPipelineControllerWithRendering::New(); plugin_manager_ = vtkSMPluginManager::New(); #ifdef __APPLE__ std::string plugin_path = std::string(std::getenv("BDM_INSTALL_DIR")) + "/biodynamo/lib/pv_plugin/libBDMGlyphFilter.dylib"; #else std::string plugin_path = std::string(std::getenv("BDM_INSTALL_DIR")) + "/biodynamo/lib/pv_plugin/libBDMGlyphFilter.so"; #endif // Load custom plugin to enable cylinder glyph scaling if (!plugin_manager_->LoadLocalPlugin(plugin_path.c_str())) { Fatal("LoadLocalPlugin", "Was unable to load our custom visualzation plugin. Have you " "sourced the BioDynaMo environment?"); } } virtual ~InSituPipeline() { // Do not delete proxy_manager_, session_manager_ plugin_manager_->Delete(); controller_->Delete(); } void Initialize( const std::unordered_map& vtk_so_grids) { vtk_so_grids_ = vtk_so_grids; initialized_ = true; } int RequestDataDescription(vtkCPDataDescription* data_description) override { if (!data_description) { vtkWarningMacro( "The data description is empty. There is nothing to visualize!"); return 0; } int n_inputs = data_description->GetNumberOfInputDescriptions(); for (int i = 0; i < n_inputs; i++) { data_description->GetInputDescription(i)->AllFieldsOn(); data_description->GetInputDescription(i)->GenerateMeshOn(); } return 1; } vtkSMProxy* GetSource(vtkSMProxy* glyph, std::string source_name) { vtkSMProxyListDomain* pld = vtkSMProxyListDomain::SafeDownCast( glyph->GetProperty("Source")->FindDomain("vtkSMProxyListDomain")); for (unsigned int cc = 0; cc < pld->GetNumberOfProxies(); cc++) { if (pld->GetProxyName(cc) && strcmp(pld->GetProxyName(cc), source_name.c_str()) == 0) { return pld->GetProxy(cc); } } return nullptr; } // FOR ALL VTK OBJECTS: // 1. Get vtkObject + it's unique name // 2. Create a producer for it // 3. Create a proxy for the producer and register it to the session_manager // 4. Create a "real_producer" that sets the vtkObject to its output // 5. Add it to the map of producers void CreatePipeline(vtkCPDataDescription* data_description) { auto n_inputs = data_description->GetNumberOfInputDescriptions(); for (size_t i = 0; i < n_inputs; i++) { auto vtk_object = data_description->GetInputDescription(i)->GetGrid(); auto object_name = data_description->GetInputDescriptionName(i); // Create a vtkPVTrivialProducer and set its output to be the input grid. vtkSmartPointer producer; producer.TakeReference(vtkSMSourceProxy::SafeDownCast( session_manager_->NewProxy("sources", "PVTrivialProducer"))); session_manager_->RegisterProxy("sources", object_name, producer); producer->UpdateVTKObjects(); vtkObjectBase* client_side_object = producer->GetClientSideObject(); vtkPVTrivialProducer* real_producer = vtkPVTrivialProducer::SafeDownCast(client_side_object); std::string source_name = "SphereSource"; std::string glyph_type = "Glyph"; int scale_mode = 0; if (vtk_object->IsA("vtkUnstructuredGrid")) { real_producer->SetOutput(vtk_object); auto& shape = vtk_so_grids_[object_name]->shape_; if (shape == Shape::kCylinder) { source_name = "CylinderSource"; glyph_type = "BDMGlyph"; scale_mode = 4; } else if (shape != Shape::kSphere) { Log::Warning("CreatePipeline", "We currently support only spherical and cylindrical " "shaped objects for visualization. Received value %d", shape); } // Create a Glyph filter vtkSmartPointer glyph; glyph.TakeReference(vtkSMSourceProxy::SafeDownCast( session_manager_->NewProxy("filters", glyph_type.c_str()))); controller_->PreInitializeProxy(glyph); std::string object_name_str = object_name; std::string glyph_name = object_name_str + "_Glyph"; vtkSMPropertyHelper(glyph, "Input").Set(producer); vtkSMPropertyHelper(glyph, "Source").Set(GetSource(glyph, source_name)); vtkSMPropertyHelper(glyph, "ScaleMode", true).Set(scale_mode); vtkSMPropertyHelper(glyph, "ScaleFactor", true).Set(1.0); vtkSMPropertyHelper(glyph, "GlyphMode", true).Set(0); if (scale_mode == 0) { vtkSMPropertyHelper(glyph, "Scalars") .SetInputArrayToProcess(vtkDataObject::POINT, "diameter_"); } else if (scale_mode == 4) { vtkSMPropertyHelper(glyph, "X-Scaling") .SetInputArrayToProcess(vtkDataObject::POINT, "diameter_"); vtkSMPropertyHelper(glyph, "Y-Scaling") .SetInputArrayToProcess(vtkDataObject::POINT, "actual_length_"); vtkSMPropertyHelper(glyph, "Z-Scaling") .SetInputArrayToProcess(vtkDataObject::POINT, "diameter_"); vtkSMPropertyHelper(glyph, "Vectors") .SetInputArrayToProcess(vtkDataObject::POINT, "spring_axis_"); vtkSMPropertyHelper(glyph, "MassLocation") .SetInputArrayToProcess(vtkDataObject::POINT, "mass_location_"); } glyph->UpdateVTKObjects(); producer->UpdateVTKObjects(); controller_->PostInitializeProxy(glyph); controller_->RegisterPipelineProxy(glyph, glyph_name.c_str()); glyph->UpdatePropertyInformation(); glyph->UpdatePipeline(); filter_map_[glyph_name] = glyph; } else if (vtk_object->IsA("vtkImageData")) { real_producer->SetOutput(vtkImageData::SafeDownCast(vtk_object)); } else { Log::Warning("vtkCPVTKPipeline", "Object of type ", vtk_object->GetClassName(), " is not supported for visualization"); } // Record the producer for updates producer_map_[object_name] = producer; } } // 1. Call CreatePipeline to create the producers (only once) // 2. For each producer in the map, update the output void UpdateProducers(vtkCPDataDescription* data_description) { // Ensure that the producers are created if (!pipeline_created_) { this->CreatePipeline(data_description); this->pipeline_created_ = true; } // Update the output of each recorded producer for (auto it = this->producer_map_.begin(); it != this->producer_map_.end(); it++) { vtkObjectBase* client_side_object = it->second->GetClientSideObject(); vtkPVTrivialProducer* real_producer = vtkPVTrivialProducer::SafeDownCast(client_side_object); real_producer->SetOutput( data_description->GetInputDescriptionByName(it->first.c_str()) ->GetGrid(), data_description->GetTime()); } } // 1. Call CreatePipeline to create the producers (only once) // 2. For each producer in the map, update the output void UpdateFilters(vtkCPDataDescription* data_description) { // Update the output of each recorded filter for (auto it = this->filter_map_.begin(); it != this->filter_map_.end(); it++) { it->second->UpdatePipeline(); } } void DoLiveVisualization(vtkCPDataDescription* data_description) { // Initialize the insitu link, if not already done if (!insitu_link_) { insitu_link_ = vtkSmartPointer::New(); insitu_link_->SetHostname("localhost"); insitu_link_->SetInsituPort(22222); insitu_link_->Initialize( vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager()); } // If there is not link with the ParaView client, then do not go trough the // process of updating sources / filters if (!insitu_link_->Initialize(vtkSMProxyManager::GetProxyManager() ->GetActiveSessionProxyManager())) { return; } // Get the time information from the data double time = data_description->GetTime(); vtkIdType time_step = data_description->GetTimeStep(); // Go into the main loop for visualization while (true) { // Initialize the link and update data insitu_link_->InsituUpdate(time, time_step); for (auto it = producer_map_.begin(); it != producer_map_.end(); it++) { // Update the pipeline for the current time_step it->second->UpdatePipeline(time); } for (auto it = this->filter_map_.begin(); it != this->filter_map_.end(); it++) { it->second->UpdatePipeline(time); } insitu_link_->InsituPostProcess(time, time_step); // Wait for changes on ParaView side (Pause ...) if (insitu_link_->GetSimulationPaused()) { if (insitu_link_->WaitForLiveChange()) { break; } } else { break; } } } int CoProcess(vtkCPDataDescription* data_description) override { if (!data_description) { vtkWarningMacro( "The data description is empty. There is nothing to visualize!"); return 0; } if (this->RequestDataDescription(data_description) == 0) { return 1; } // Update the producers and do live visualization this->UpdateProducers(data_description); this->UpdateFilters(data_description); this->DoLiveVisualization(data_description); return 1; } bool IsInitialized() { return initialized_; } private: bool pipeline_created_; vtkSMProxyManager* proxy_manager_; vtkSMPluginManager* plugin_manager_; vtkSMSessionProxyManager* session_manager_; std::map> producer_map_; std::map> filter_map_; vtkSmartPointer insitu_link_; vtkSMParaViewPipelineControllerWithRendering* controller_; std::unordered_map vtk_so_grids_; bool initialized_ = false; }; #else /// False front (to ignore Catalyst in gtests) class vtkCPDataDescription; class InSituPipeline { public: void Initialize() { Log::Fatal("vtkCPVTKPipeline::Initialize", "Simulation was compiled without ParaView support, but you are " "trying to use it."); } int RequestDataDescription(vtkCPDataDescription* data_description) { Log::Fatal("vtkCPVTKPipeline::RequestDataDescription", "Simulation was compiled without ParaView support, but you are " "trying to use it."); } void CreatePipeline(vtkCPDataDescription* data_description) { Log::Fatal("vtkCPVTKPipeline::CreatePipeline", "Simulation was compiled without ParaView support, but you are " "trying to use it."); } void DoLiveVisualization(vtkCPDataDescription* data_description) { Log::Fatal("vtkCPVTKPipeline::DoLiveVisualization", "Simulation was compiled without ParaView support, but you are " "trying to use it."); } int CoProcess(vtkCPDataDescription* data_description) { Log::Fatal("vtkCPVTKPipeline::CoProcess", "Simulation was compiled without ParaView support, but you are " "trying to use it."); } }; #endif // defined(USE_CATALYST) && !defined(__ROOTCLING__) } // namespace bdm #endif // CORE_VISUALIZATION_INSITU_PIPELINE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef CORE_VISUALIZATION_NOTEBOOK_UTIL #define CORE_VISUALIZATION_NOTEBOOK_UTIL #include "core/simulation.h" #include "core/visualization/root_adaptor.h" namespace bdm { /// Visualize the simulation objects in ROOT notebooks inline void VisualizeInNotebook(size_t w = 300, size_t h = 300, std::string opt = "") { auto* sim = Simulation::GetActive(); auto* param = sim->GetParam(); // Force an update of the visualization engine sim->GetScheduler()->GetRootVisualization()->Visualize( param->visualization_export_interval_); sim->GetScheduler()->GetRootVisualization()->DrawInCanvas(w, h, opt); } } // namespace bdm #endif // CORE_VISUALIZATION_NOTEBOOK_UTIL // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef VISUALIZATION_ROOT_ADAPTOR_H_ #define VISUALIZATION_ROOT_ADAPTOR_H_ #include #include #include #include #include #include #include #include #include "Math/AxisAngle.h" #include "Math/EulerAngles.h" #include "core/param/param.h" #include "core/resource_manager.h" #include "core/scheduler.h" #include "core/shape.h" #include "core/simulation.h" #include "core/util/log.h" #include "core/util/math.h" #include "neuroscience/neurite_element.h" using ROOT::Math::AxisAngle; using ROOT::Math::EulerAngles; using bdm::experimental::neuroscience::NeuriteElement; namespace bdm { /// The class that bridges the simulation code with ROOT Visualization class RootAdaptor { public: RootAdaptor() : initialized_(false), max_viz_nodes_(1e6) {} /// Visualize one timestep void Visualize(uint64_t total_steps) { if (!initialized_) { Initialize(); initialized_ = true; } auto *param = Simulation::GetActive()->GetParam(); if (total_steps % param->visualization_export_interval_ != 0) { return; } top_->ClearNodes(); auto *rm = Simulation::GetActive()->GetResourceManager(); rm->ApplyOnAllElements([&](SimObject *so) { auto container = new TGeoVolumeAssembly("A"); this->AddBranch(so, container); top_->AddNode(container, top_->GetNdaughters()); }); gSystem->ProcessEvents(); gGeoManager->Export(outfile_.c_str(), "biodynamo"); } void DrawInCanvas(size_t w = 300, size_t h = 300, std::string opt = "") { canvas_->GetListOfPrimitives()->Clear(); if (opt.empty()) { canvas_->GetListOfPrimitives()->Add(gGeoManager->GetTopVolume(), "all"); } else { opt = "all;" + opt; canvas_->GetListOfPrimitives()->Add(gGeoManager->GetTopVolume(), opt.c_str()); } canvas_->SetCanvasSize(w, h); canvas_->Update(); canvas_->Draw(); } private: void Initialize() { gGeoManager = new TGeoManager("Visualization", "BioDynaMo"); canvas_ = new TCanvas("BioDynaMo Canvas", "For ROOT Notebooks ", 300, 300); outfile_ = Simulation::GetActive()->GetUniqueName() + ".root"; // Set number of segments for approximating circles in drawing. // Keep it low for better performance. gGeoManager->SetNsegments(15); mat_empty_space_ = new TGeoMaterial("EmptySpace", 0, 0, 0); mat_solid_ = new TGeoMaterial("Solid", .938, 1., 10000.); med_empty_space_ = new TGeoMedium("Empty", 1, mat_empty_space_); med_solid_ = new TGeoMedium("Solid", 1, mat_solid_); // Another way to make top volume for world. In this way it will be // unbounded. top_ = new TGeoVolumeAssembly("WORLD"); gGeoManager->SetTopVolume(top_); gGeoManager->SetVisLevel(4); // Number of visualized nodes inside one volume. If you exceed this number, // ROOT will draw nothing. gGeoManager->SetMaxVisNodes(max_viz_nodes_); // Assign the gGeoManager to our canvas to enable resizing of the output // display. The "all" option is necessary to prevent ROOT from hiding // objects (which it does by default for performance reasons). canvas_->GetListOfPrimitives()->Add(gGeoManager->GetTopVolume(), "all"); initialized_ = true; } /// Recursively adds sphere and its daughters to the container. void AddBranch(const SimObject *so, TGeoVolume *container) { switch (so->GetShape()) { case Shape::kSphere: AddSphere(so, container); break; case Shape::kCylinder: AddCylinder(so, container); break; default: Log::Error("RootAdaptor", "Tried to add a shape to the Root visualization that's not " "one of the supported types : ", so->GetShape()); } // to be extended for other object } /// Adds a sphere object to the volume void AddSphere(const SimObject *so, TGeoVolume *container) { std::string name = so->GetTypeName() + std::to_string(so->GetUid()); auto radius = so->GetDiameter() / 2; auto massLocation = so->GetPosition(); auto x = massLocation[0]; auto y = massLocation[1]; auto z = massLocation[2]; auto position = new TGeoTranslation(x, y, z); auto volume = gGeoManager->MakeSphere(name.c_str(), med_solid_, 0, radius); volume->SetLineColor(kBlue); container->AddNode(volume, container->GetNdaughters(), position); } /// Adds a cylinder object to the volume void AddCylinder(const SimObject *so, TGeoVolume *container) { if (auto neurite = dynamic_cast(so)) { std::string name = so->GetTypeName() + std::to_string(so->GetUid()); auto radius = neurite->GetDiameter() / 2; auto half_length = neurite->GetLength() / 2; auto massLocation = neurite->GetPosition(); auto x = massLocation[0]; auto y = massLocation[1]; auto z = massLocation[2]; auto trans = new TGeoTranslation(x, y, z); // The initial orientation of the cylinder TVector3 orig(0, 0, 1); // The vector that we want to orient the cylinder to (symmetry axis // aligns with this vector) Double3 d = neurite->GetSpringAxis(); TVector3 dir(d[0], d[1], d[2]); dir = dir.Unit(); // Compute the Axis-Angle rotation representation auto dot_product = dir.Dot(orig); auto angle = std::acos(dot_product); // TODO: make sure it is `z x dir, and not `dir x z` TVector3 n = dir.Cross(orig); n = n.Unit(); auto axis = AxisAngle::AxisVector(n[0], n[1], n[2]); AxisAngle aa(axis, angle); EulerAngles ea(aa); TGeoRotation *rot = new TGeoRotation("rot", Math::ToDegree(ea.Phi()), Math::ToDegree(ea.Theta()), Math::ToDegree(ea.Psi())); TGeoCombiTrans *transrot = new TGeoCombiTrans(*trans, *rot); auto volume = gGeoManager->MakeTube(name.c_str(), med_solid_, 0, radius, half_length); volume->SetLineColor(kBlue); container->AddNode(volume, container->GetNdaughters(), transrot); } else { Log::Error("RootAdaptor", "This is not a cylindrical shaped object!"); } } std::string outfile_; TCanvas *canvas_ = nullptr; /// Top volumes for TGeo and TEve (world) TGeoVolume *top_; /// Eve materials and medium TGeoMaterial *mat_empty_space_; TGeoMaterial *mat_solid_; TGeoMedium *med_empty_space_; TGeoMedium *med_solid_; bool initialized_; /// Max visualized shapes per volume. int max_viz_nodes_; }; } // namespace bdm #endif // VISUALIZATION_ROOT_ADAPTOR_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_EVENT_NEURITE_BIFURCATION_EVENT_H_ #define NEUROSCIENCE_EVENT_NEURITE_BIFURCATION_EVENT_H_ #include #include "core/container/math_array.h" #include "core/event/event.h" namespace bdm { namespace experimental { namespace neuroscience { /// \brief Contains the parameters to bifurcate a growth cone. /// /// This event is only possible for terminal neurite segments. /// It creates **two** new neurite elements and assigns it to daughter left /// and daughter right of the neurite element that triggered the event /// (=mother). struct NeuriteBifurcationEvent : public Event { static const EventId kEventId; NeuriteBifurcationEvent(double length, double diameter_l, double diameter_r, const Double3& direction_l, const Double3& direction_r) : length_(length), diameter_left_(diameter_l), diameter_right_(diameter_r), direction_left_(direction_l), direction_right_(direction_r) {} virtual ~NeuriteBifurcationEvent() {} EventId GetId() const override { return kEventId; } /// length of new branches double length_; /// diameter of new branch left double diameter_left_; /// diameter of new branch right double diameter_right_; /// direction branch right /// NB: direction will be corrected if it is pointing backward. Double3 direction_left_; /// direction branch left /// NB: direction will be corrected if it is pointing backward. Double3 direction_right_; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_EVENT_NEURITE_BIFURCATION_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_EVENT_NEURITE_BRANCHING_EVENT_H_ #define NEUROSCIENCE_EVENT_NEURITE_BRANCHING_EVENT_H_ #include #include "core/event/event.h" namespace bdm { namespace experimental { namespace neuroscience { /// \brief This event splits the current neurite element into two elements /// and adds a new side branch as daughter right at the proximal half. /// /// It is therefore a combination of SplitNeuriteElementEvent and /// SideNeuriteExtensionEvent. The parameter names must be compatible to the /// two mentioned events to enable code reuse. /// (This event creates **two** new neurite elements.) struct NeuriteBranchingEvent : public Event { static const EventId kEventId; NeuriteBranchingEvent(double distal_portion, double length, double diameter, const Double3 direction) : distal_portion_(distal_portion), length_(length), diameter_(diameter), direction_(direction) {} virtual ~NeuriteBranchingEvent() {} EventId GetId() const override { return kEventId; } /// the fraction of the total old length devoted to the /// distal half (should be between 0 and 1). double distal_portion_; /// length of the new side branch double length_; /// diameter of the new side branch double diameter_; /// direction of the new side branch. /// will be automatically corrected if not at least 45 degrees from the /// cylinder's axis. Double3 direction_; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_EVENT_NEURITE_BRANCHING_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_EVENT_NEW_NEURITE_EXTENSION_EVENT_H_ #define NEUROSCIENCE_EVENT_NEW_NEURITE_EXTENSION_EVENT_H_ #include "core/event/event.h" namespace bdm { namespace experimental { namespace neuroscience { /// \brief Contains the parameters to extend a new neurite from a neuron soma. /// /// The cell that triggers the event is the neuron soma. /// The event creates a new neurite element. struct NewNeuriteExtensionEvent : public Event { static const EventId kEventId; NewNeuriteExtensionEvent(double diameter, double phi, double theta) : diameter_(diameter), phi_(phi), theta_(theta) {} virtual ~NewNeuriteExtensionEvent() {} EventId GetId() const override { return kEventId; } /// diameter the diameter of the new neurite double diameter_; /// phi_ azimuthal angle (spherical coordinates) double phi_; /// theta_ polar angle (spherical coordinates) double theta_; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_EVENT_NEW_NEURITE_EXTENSION_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_EVENT_SIDE_NEURITE_EXTENSION_EVENT_H_ #define NEUROSCIENCE_EVENT_SIDE_NEURITE_EXTENSION_EVENT_H_ #include "core/event/event.h" namespace bdm { namespace experimental { namespace neuroscience { /// \brief Contains the parameters to add a side neurite element. /// /// This event adds a side neurite (daughter right) to the neurite element /// that triggered the event. struct SideNeuriteExtensionEvent : public Event { static const EventId kEventId; SideNeuriteExtensionEvent(double length, double diameter, const Double3 direction) : length_(length), diameter_(diameter), direction_(direction) {} virtual ~SideNeuriteExtensionEvent() {} EventId GetId() const override { return kEventId; } /// length of the new branch double length_; /// diameter of the new branch double diameter_; /// direction of the new branch Double3 direction_; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_EVENT_SIDE_NEURITE_EXTENSION_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_EVENT_SPLIT_NEURITE_ELEMENT_EVENT_H_ #define NEUROSCIENCE_EVENT_SPLIT_NEURITE_ELEMENT_EVENT_H_ #include "core/event/event.h" namespace bdm { namespace experimental { namespace neuroscience { /// \brief Contains the parameters to split a neurite element into two segments. /// /// This event splits a neurite element into two segments. /// The neurite element that triggers the event becomes the distal one. /// The new neurite element will be the proximal one. struct SplitNeuriteElementEvent : public Event { static const EventId kEventId; explicit SplitNeuriteElementEvent(double distal_portion) : distal_portion_(distal_portion) {} virtual ~SplitNeuriteElementEvent() {} EventId GetId() const override { return kEventId; } /// The fraction of the total old length devoted to the distal half /// (should be between 0 and 1). double distal_portion_; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_EVENT_SPLIT_NEURITE_ELEMENT_EVENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_MODULE_H_ #define NEUROSCIENCE_MODULE_H_ namespace bdm { namespace experimental { namespace neuroscience { /// Initializes the neuroscience module. /// NB: Must be called before `bdm::Simulation` is created! void InitModule(); } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_MODULE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_NEURITE_ELEMENT_H_ #define NEUROSCIENCE_NEURITE_ELEMENT_H_ #include #include #include #include #include "core/default_force.h" #include "core/shape.h" #include "core/sim_object/sim_object.h" #include "core/util/log.h" #include "core/util/math.h" #include "core/util/random.h" #include "neuroscience/event/neurite_bifurcation_event.h" #include "neuroscience/event/neurite_branching_event.h" #include "neuroscience/event/new_neurite_extension_event.h" #include "neuroscience/event/side_neurite_extension_event.h" #include "neuroscience/event/split_neurite_element_event.h" #include "neuroscience/neuron_or_neurite.h" #include "neuroscience/neuron_soma.h" #include "neuroscience/param.h" namespace bdm { namespace experimental { namespace neuroscience { /// Class defining a neurite element with cylindrical geometry. /// A cylinder can be seen as a normal cylinder, with two end points and a /// diameter. It is oriented; the two points are called proximal and distal. /// The neurite element is be part of a tree-like structure with (one and only) /// one object at its proximal point and (up to) two neurite elements at /// its distal end. The proximal end can be a Neurite or Neuron cell body. /// If there is only one daughter, it is the left one. /// If `daughter_left_ == nullptr`, there is no distal neurite element. /// (it is a terminal neurite element). The presence of a `daughter_left_` /// means that this branch has a bifurcation at its distal end. /// \n /// All the mass of the neurite element is concentrated at the distal point. /// Only the distal end is moved. All the forces that are applied to the /// proximal node are transmitted to the mother element class NeuriteElement : public SimObject, public NeuronOrNeurite { BDM_SIM_OBJECT_HEADER(NeuriteElement, SimObject, 1, mass_location_, position_, volume_, diameter_, density_, adherence_, x_axis_, y_axis_, z_axis_, is_axon_, mother_, daughter_left_, daughter_right_, branch_order_, force_to_transmit_to_proximal_mass_, spring_axis_, actual_length_, tension_, spring_constant_, resting_length_); public: NeuriteElement() { resting_length_ = spring_constant_ * actual_length_ / (tension_ + spring_constant_); auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); tension_ = param->neurite_default_tension_; SetDiameter(param->neurite_default_diameter_); SetActualLength(param->neurite_default_actual_length_); density_ = param->neurite_default_density_; spring_constant_ = param->neurite_default_spring_constant_; adherence_ = param->neurite_default_adherence_; UpdateVolume(); } /// TODO NeuriteElement(const Event& event, SimObject* other, uint64_t new_oid = 0) : Base(event, other, new_oid) { resting_length_ = spring_constant_ * actual_length_ / (tension_ + spring_constant_); if (event.GetId() == NewNeuriteExtensionEvent::kEventId) { const auto& e = dynamic_cast(event); auto* soma = dynamic_cast(other); InitializeNewNeuriteExtension(soma, e.diameter_, e.phi_, e.theta_); } else if (event.GetId() == NeuriteBifurcationEvent::kEventId) { const auto& e = dynamic_cast(event); auto* ne = dynamic_cast(other); double diameter; Double3 direction; if (new_oid == 0) { // left branch diameter = e.diameter_left_; direction = e.direction_left_; } else { // right branch diameter = e.diameter_right_; direction = e.direction_right_; } InitializeNeuriteBifurcation(ne, e.length_, diameter, direction); } else if (event.GetId() == SideNeuriteExtensionEvent::kEventId) { const auto& e = dynamic_cast(event); auto* ne = dynamic_cast(other); InitializeSideExtensionOrBranching(ne, e.length_, e.diameter_, e.direction_); } else if (event.GetId() == SplitNeuriteElementEvent::kEventId) { const auto& e = dynamic_cast(event); auto* ne = dynamic_cast(other); InitializeSplitOrBranching(ne, e.distal_portion_); } else if (event.GetId() == NeuriteBranchingEvent::kEventId) { const auto& e = dynamic_cast(event); auto* ne = dynamic_cast(other); if (new_oid == 0) { InitializeSplitOrBranching(ne, e.distal_portion_); } else { InitializeSideExtensionOrBranching(ne, e.length_, e.diameter_, e.direction_); } } } void EventHandler(const Event& event, SimObject* other1, SimObject* other2 = nullptr) override { Base::EventHandler(event, other1, other2); if (event.GetId() == NeuriteBifurcationEvent::kEventId) { SetDaughterLeft(other1->GetSoPtr()); SetDaughterRight(other2->GetSoPtr()); } else if (event.GetId() == SideNeuriteExtensionEvent::kEventId) { SetDaughterRight(other2->GetSoPtr()); } else if (event.GetId() == SplitNeuriteElementEvent::kEventId) { const auto& e = dynamic_cast(event); auto* proximal = dynamic_cast(other1); resting_length_ *= e.distal_portion_; // family relations mother_->UpdateRelative(*this, *proximal); mother_ = proximal->GetSoPtr(); UpdateDependentPhysicalVariables(); proximal->UpdateDependentPhysicalVariables(); // UpdateLocalCoordinateAxis has to come after UpdateDepend... proximal->UpdateLocalCoordinateAxis(); } else if (event.GetId() == NeuriteBranchingEvent::kEventId) { const auto& e = dynamic_cast(event); auto* proximal = dynamic_cast(other1); auto* branch = dynamic_cast(other2); // TODO(lukas) some code duplication with SplitNeuriteElementEvent and // SideNeuriteExtensionEvent event handler proximal->SetDaughterRight(branch->GetSoPtr()); // elongation resting_length_ *= e.distal_portion_; mother_->UpdateRelative(*this, *proximal); mother_ = proximal->GetSoPtr(); UpdateDependentPhysicalVariables(); proximal->UpdateDependentPhysicalVariables(); // UpdateLocalCoordinateAxis has to come after UpdateDepend... proximal->UpdateLocalCoordinateAxis(); } } SoUid GetUid() const override { return Base::GetUid(); } Shape GetShape() const override { return Shape::kCylinder; } /// Returns the data members that are required to visualize this simulation /// object. std::set GetRequiredVisDataMembers() const override { return {"mass_location_", "diameter_", "actual_length_", "spring_axis_"}; } void SetDiameter(double diameter) override { if (diameter > diameter_) { SetRunDisplacementForAllNextTs(); } diameter_ = diameter; UpdateVolume(); } void SetDensity(double density) { density_ = density; } const Double3& GetPosition() const override { return position_; } void SetPosition(const Double3& position) override { position_ = position; SetMassLocation(position + spring_axis_ * 0.5); } void UpdatePosition() { position_ = mass_location_ - (spring_axis_ * 0.5); } /// return end of neurite element position const Double3& GetMassLocation() const { return mass_location_; } void SetMassLocation(const Double3& mass_location) { mass_location_ = mass_location; SetRunDisplacementForAllNextTs(); } double GetAdherence() const { return adherence_; } void SetAdherence(double adherence) { adherence_ = adherence; } const Double3& GetXAxis() const { return x_axis_; } const Double3& GetYAxis() const { return y_axis_; } const Double3& GetZAxis() const { return z_axis_; } double GetVolume() const { return volume_; } double GetDiameter() const override { return diameter_; } double GetDensity() const { return density_; } double GetMass() const { return density_ * volume_; } /// Returns the absolute coordinates of the location where the daughter is /// attached. /// @param daughter_element_idx element_idx of the daughter /// @return the coord Double3 OriginOf(SoUid daughter_uid) const override { return mass_location_; } // TODO(neurites) arrange in order end /// Retracts the neurite element, if it is a terminal one. /// Branch retraction by moving the distal end toward the /// proximal end (the mother), maintaining the same tension in the /// neurite element. The method shortens the actual and the resting length /// so that the result is a shorter neurite element with the same tension. /// * If this neurite element is longer than the required shortening, it /// simply retracts. /// * If it is shorter and its mother has no other daughter, it merges with /// it's mother and the method is recursively called (this time the cylinder /// length is bigger because we have a new neurite element that resulted /// from the fusion of two). /// * If it is shorter and either the previous neurite element has another /// daughter or the mother is not a neurite element, it disappears. /// @param speed the retraction speed in microns / h void RetractTerminalEnd(double speed) { // check if is a terminal branch if (daughter_left_ != nullptr) { return; } // scaling for integration step auto* core_param = Simulation::GetActive()->GetParam(); speed *= core_param->simulation_time_step_; auto* mother_soma = dynamic_cast(mother_.Get()); auto* mother_neurite = dynamic_cast(mother_.Get()); if (actual_length_ > speed + 0.1) { // if actual_length_ > length : retraction keeping the same tension // (putting a limit on how short a branch can be is absolutely necessary // otherwise the tension might explode) double new_actual_length = actual_length_ - speed; double factor = new_actual_length / actual_length_; SetActualLength(new_actual_length); // cf removeproximalCylinder() resting_length_ = spring_constant_ * actual_length_ / (tension_ + spring_constant_); SetSpringAxis(spring_axis_ * factor); SetMassLocation(mother_->OriginOf(Base::GetUid()) + spring_axis_); UpdatePosition(); UpdateVolume(); // and update concentration of internal stuff. } else if (mother_soma) { mother_->RemoveDaughter(Base::GetSoPtr()); this->RemoveFromSimulation(); } else if (mother_neurite && mother_neurite->GetDaughterRight() == nullptr) { // if actual_length_ < length and mother is a neurite element with no // other daughter : merge with mother RemoveProximalNeuriteElement(); // also updates volume_... RetractTerminalEnd(speed / core_param->simulation_time_step_); } else { // if mother is neurite element with other daughter or is not a neurite // segment: disappear. mother_->RemoveDaughter(Base::GetSoPtr()); this->RemoveFromSimulation(); mother_->UpdateDependentPhysicalVariables(); } } /// Method used for active extension of a terminal branch, representing the /// steering of a growth cone. The movement should always be forward, /// otherwise no movement is performed. /// If `direction` points in an opposite direction than the axis, i.e. /// if the dot product is negative, there is no movement (only elongation is /// possible). /// @param speed /// @param direction void ElongateTerminalEnd(double speed, const Double3& direction) { double temp = direction * spring_axis_; if (temp > 0) { MovePointMass(speed, direction); } } /// Returns true if a side branch is physically possible. That is if this is /// not a terminal branch and if there is not already a second daughter. bool BranchPermitted() const { return daughter_left_ != nullptr && daughter_right_ == nullptr; } /// \brief Create a branch for this neurite element. /// /// \see NeuriteBranchingEvent NeuriteElement* Branch(double new_branch_diameter, const Double3& direction, double length = 1.0) { // create a new neurite element for side branch // we first split this neurite element into two pieces // then append a "daughter right" between the two auto* ctxt = Simulation::GetActive()->GetExecutionContext(); NeuriteBranchingEvent event(0.5, length, new_branch_diameter, direction); auto* proximal = GetInstance(event, this, 0); ctxt->push_back(proximal); auto* branch = bdm_static_cast(GetInstance(event, proximal, 1)); ctxt->push_back(branch); EventHandler(event, proximal, branch); return branch; } /// \brief Create a branch for this neurite element. /// /// Diameter of new side branch will be equal to this neurites diameter. /// \see NeuriteBranchingEvent NeuriteElement* Branch(const Double3& direction) { return Branch(diameter_, direction); } /// \brief Create a branch for this neurite element. /// /// Use a random growth direction for the side branch. /// \see NeuriteBranchingEvent NeuriteElement* Branch(double diameter) { auto* random = Simulation::GetActive()->GetRandom(); auto rand_noise = random->template UniformArray<3>(-0.1, 0.1); auto growth_direction = Math::Perp3( GetUnitaryAxisDirectionVector() + rand_noise, random->Uniform(0, 1)); growth_direction.Normalize(); return Branch(diameter, growth_direction); } /// \brief Create a branch for this neurite element. /// /// Use a random growth direction for the side branch. /// Diameter of new side branch will be equal to this neurites diameter. /// \see NeuriteBranchingEvent NeuriteElement* Branch() { auto* random = Simulation::GetActive()->GetRandom(); double branch_diameter = diameter_; auto rand_noise = random->template UniformArray<3>(-0.1, 0.1); auto growth_direction = Math::Perp3( GetUnitaryAxisDirectionVector() + rand_noise, random->Uniform(0, 1)); return Branch(branch_diameter, growth_direction); } /// Returns true if a bifurcation is physicaly possible. That is if the /// neurite element has no daughter and the actual length is bigger than the /// minimum required. bool BifurcationPermitted() const { auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); return (daughter_left_ == nullptr && actual_length_ > param->neurite_minimial_bifurcation_length_); } /// \brief Growth cone bifurcation. /// /// \see NeuriteBifurcationEvent std::array Bifurcate(double length, double diameter_1, double diameter_2, const Double3& direction_1, const Double3& direction_2) { // 1) physical bifurcation // check it is a terminal branch if (daughter_left_ != nullptr) { Fatal("NeuriteElements", "Bifurcation only allowed on a terminal neurite element"); } auto* ctxt = Simulation::GetActive()->GetExecutionContext(); NeuriteBifurcationEvent event(length, diameter_1, diameter_2, direction_1, direction_2); auto* new_branch_l = bdm_static_cast(GetInstance(event, this, 0)); ctxt->push_back(new_branch_l); auto* new_branch_r = bdm_static_cast(GetInstance(event, this, 1)); ctxt->push_back(new_branch_r); EventHandler(event, new_branch_l, new_branch_r); return {new_branch_l, new_branch_r}; } /// \brief Growth cone bifurcation. /// /// \see NeuriteBifurcationEvent std::array Bifurcate(double diameter_1, double diameter_2, const Double3& direction_1, const Double3& direction_2); /// \brief Growth cone bifurcation. /// /// \see NeuriteBifurcationEvent std::array Bifurcate(const Double3& direction_1, const Double3& direction_2) { // initial default length : auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); double l = param->neurite_default_actual_length_; // diameters : double d = diameter_; return Bifurcate(l, d, d, direction_1, direction_2); } /// \brief Growth cone bifurcation. /// /// \see NeuriteBifurcationEvent std::array Bifurcate() { // initial default length : auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); double l = param->neurite_default_actual_length_; // diameters : double d = diameter_; // direction : (60 degrees between branches) auto* random = Simulation::GetActive()->GetRandom(); double random_val = random->Uniform(0, 1); auto perp_plane = Math::Perp3(spring_axis_, random_val); double angle_between_branches = Math::kPi / 3.0; auto direction_1 = Math::RotAroundAxis( spring_axis_, angle_between_branches * 0.5, perp_plane); auto direction_2 = Math::RotAroundAxis( spring_axis_, -angle_between_branches * 0.5, perp_plane); return Bifurcate(l, d, d, direction_1, direction_2); } // *************************************************************************** // METHODS FOR NEURON TREE STRUCTURE * // *************************************************************************** // TODO(neurites) documentation void RemoveDaughter(const SoPointer& daughter) override { // If there is another daughter than the one we want to remove, // we have to be sure that it will be the daughterLeft-> if (daughter == daughter_right_) { daughter_right_ = nullptr; return; } if (daughter == daughter_left_) { daughter_left_ = daughter_right_; daughter_right_ = nullptr; return; } Fatal("NeuriteElement", "Given object is not a daughter!"); } // TODO(neurites) add documentation void UpdateRelative(const NeuronOrNeurite& old_relative, const NeuronOrNeurite& new_relative) override { if (&old_relative == &*mother_) { mother_ = new_relative.GetNeuronOrNeuriteSoPtr(); } else { auto new_neurite_soptr = bdm_static_cast(&new_relative) ->GetSoPtr(); if (&*daughter_left_ == dynamic_cast(&old_relative)) { daughter_left_ = new_neurite_soptr; } else if (&*daughter_right_ == dynamic_cast(&old_relative)) { daughter_right_ = new_neurite_soptr; } } } /// Returns the total force that this `NeuriteElement` exerts on it's mother. /// It is the sum of the spring force and the part of the inter-object force /// computed earlier in `CalculateDisplacement` Double3 ForceTransmittedFromDaugtherToMother(const NeuronOrNeurite& mother) { if (mother_ != mother) { Fatal("NeuriteElement", "Given object is not the mother!"); return {0, 0, 0}; } // The inner tension is added to the external force that was computed // earlier. // (The reason for dividing by the actualLength is to normalize the // direction : T = T * axis/ (axis length) double factor = tension_ / actual_length_; if (factor < 0) { factor = 0; } return (spring_axis_ * factor) + force_to_transmit_to_proximal_mass_; } // *************************************************************************** // DISCRETIZATION , SPATIAL NODE, CELL ELEMENT // *************************************************************************** /// Checks if this NeuriteElement is either too long or too short. /// * too long: insert another NeuriteElement /// * too short fuse it with the proximal element or even delete it /// /// Only executed for terminal neurite elements. void RunDiscretization() override { if (daughter_left_ != nullptr) { return; } auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); auto* mother_soma = dynamic_cast(mother_.Get()); auto* mother_neurite = dynamic_cast(mother_.Get()); if (actual_length_ > param->neurite_max_length_) { if (daughter_left_ == nullptr) { // if terminal branch : SplitNeuriteElement(0.1); } else if (mother_soma != nullptr) { // if initial branch : SplitNeuriteElement(0.9); } else { SplitNeuriteElement(0.5); } } else if (actual_length_ < param->neurite_min_length_ && mother_neurite != nullptr && mother_neurite->GetRestingLength() < param->neurite_max_length_ - resting_length_ - 1 && mother_neurite->GetDaughterRight() == nullptr && daughter_left_ != nullptr) { // if the previous branch is removed, we first remove its associated // NeuriteElement mother_neurite->RemoveFromSimulation(); // then we remove it RemoveProximalNeuriteElement(); // TODO(neurites) LB: what about ourselves?? } } // *************************************************************************** // ELONGATION, RETRACTION, BRANCHING // *************************************************************************** /// Method used for active extension of a terminal branch, representing the /// steering of a /// growth cone. There is no check for real extension (unlike in /// `ExtendCylinder()`` ). /// /// @param speed of the growth rate (microns/hours). /// @param direction the 3D direction of movement. void MovePointMass(double speed, const Double3& direction) { // check if is a terminal branch if (daughter_left_ != nullptr) { return; } // scaling for integration step auto* core_param = Simulation::GetActive()->GetParam(); double length = speed * core_param->simulation_time_step_; auto dir = direction; auto displacement = dir.Normalize() * length; auto new_mass_location = displacement + mass_location_; // here I have to define the actual length .......... auto relative_ml = mother_->OriginOf(Base::GetUid()); // change to auto&& SetSpringAxis(new_mass_location - relative_ml); SetMassLocation(new_mass_location); UpdatePosition(); SetActualLength(std::sqrt(spring_axis_ * spring_axis_)); // process of elongation : setting tension to 0 increases the resting length SetRestingLengthForDesiredTension(0.0); // some physics and computation obligations.... UpdateVolume(); // and update concentration of internal stuff. UpdateLocalCoordinateAxis(); } void SetRestingLengthForDesiredTension(double tension) { tension_ = tension; if (tension == 0.0) { resting_length_ = actual_length_; } else { // T = k*(A-R)/R --> R = k*A/(T+K) resting_length_ = spring_constant_ * actual_length_ / (tension_ + spring_constant_); } } /// Progressive modification of the volume. Updates the diameter. /// @param speed cubic micron/ h void ChangeVolume(double speed) { // scaling for integration step auto* core_param = Simulation::GetActive()->GetParam(); double delta = speed * core_param->simulation_time_step_; volume_ += delta; if (volume_ < 5.2359877E-7) { // minimum volume_, corresponds to minimal diameter_ volume_ = 5.2359877E-7; } UpdateDiameter(); } /// Progressive modification of the diameter. Updates the volume. /// @param speed micron/ h void ChangeDiameter(double speed) { // scaling for integration step auto* core_param = Simulation::GetActive()->GetParam(); double delta = speed * core_param->simulation_time_step_; diameter_ += delta; UpdateVolume(); } // *************************************************************************** // Physics // *************************************************************************** Double3 CalculateDisplacement(double squared_radius, double dt) override { Double3 force_on_my_point_mass{0, 0, 0}; Double3 force_on_my_mothers_point_mass{0, 0, 0}; // 1) Spring force // Only the spring of this cylinder. The daughters spring also act on this // mass, but they are treated in point (2) double factor = -tension_ / actual_length_; // the minus sign is important // because the spring axis goes // in the opposite direction force_on_my_point_mass = force_on_my_point_mass + (spring_axis_ * factor); // 2) Force transmitted by daugthers (if they exist) if (daughter_left_ != nullptr) { auto force_from_daughter = daughter_left_->ForceTransmittedFromDaugtherToMother(*this); force_on_my_point_mass = force_on_my_point_mass + force_from_daughter; } if (daughter_right_ != nullptr) { auto force_from_daughter = daughter_right_->ForceTransmittedFromDaugtherToMother(*this); force_on_my_point_mass = force_on_my_point_mass + force_from_daughter; } Double3 force_from_neighbors = {0, 0, 0}; auto* core_param = Simulation::GetActive()->GetParam(); // this value will be used to reduce force for neurite/neurite interactions double h_over_m = 0.01; // 3) Object avoidance force bool has_neurite_neighbor = false; // (We check for every neighbor object if they touch us, i.e. push us away) auto calculate_neighbor_forces = [this, &force_from_neighbors, &force_on_my_mothers_point_mass, &h_over_m, &has_neurite_neighbor]( const SimObject* neighbor) { // if neighbor is a NeuriteElement // use shape to determine if neighbor is a NeuriteElement // this is much faster than using a dynamic_cast if (neighbor->GetShape() == Shape::kCylinder) { // if it is a direct relative, or sister branch, we don't take it into // account if (this->GetDaughterLeft() == *neighbor || this->GetDaughterRight() == *neighbor || this->GetMother() == bdm_static_cast(neighbor)->GetMother() || (this->GetMother() == *neighbor)) { return; } } else if (auto* neighbor_soma = dynamic_cast(neighbor)) { // if neighbor is NeuronSoma // if it is a direct relative, we don't take it into account if (this->GetMother() == *neighbor_soma) { return; } } DefaultForce force; Double4 force_from_neighbor = force.GetForce(this, neighbor); // hack: if the neighbour is a neurite, we need to reduce the force from // that neighbour in order to avoid kink behaviour if (neighbor->GetShape() == Shape::kCylinder) { force_from_neighbor = force_from_neighbor * h_over_m; has_neurite_neighbor = true; } if (std::abs(force_from_neighbor[3]) < 1E-10) { // TODO(neurites) hard coded value // (if all the force is transmitted to the (distal end) point mass) force_from_neighbors[0] += force_from_neighbor[0]; force_from_neighbors[1] += force_from_neighbor[1]; force_from_neighbors[2] += force_from_neighbor[2]; } else { // (if there is a part transmitted to the proximal end) double part_for_point_mass = 1.0 - force_from_neighbor[3]; force_from_neighbors[0] += force_from_neighbor[0] * part_for_point_mass; force_from_neighbors[1] += force_from_neighbor[1] * part_for_point_mass; force_from_neighbors[2] += force_from_neighbor[2] * part_for_point_mass; force_on_my_mothers_point_mass[0] += force_from_neighbor[0] * force_from_neighbor[3]; force_on_my_mothers_point_mass[1] += force_from_neighbor[1] * force_from_neighbor[3]; force_on_my_mothers_point_mass[2] += force_from_neighbor[2] * force_from_neighbor[3]; } }; auto* ctxt = Simulation::GetActive()->GetExecutionContext(); ctxt->ForEachNeighborWithinRadius(calculate_neighbor_forces, *this, squared_radius); // hack: if the neighbour is a neurite, and as we reduced the force from // that neighbour, we also need to reduce my internal force (from internal // tension and daughters) if (has_neurite_neighbor) { force_on_my_point_mass = force_on_my_point_mass * h_over_m; } force_on_my_point_mass = force_on_my_point_mass + force_from_neighbors; // 5) define the force that will be transmitted to the mother force_to_transmit_to_proximal_mass_ = force_on_my_mothers_point_mass; // 6.1) Define movement scale double force_norm = force_on_my_point_mass.Norm(); // 6.2) If is F not strong enough -> no movements if (force_norm < adherence_) { return {0, 0, 0}; } // So, what follows is only executed if we do actually move : // 6.3) Since there's going be a move, we calculate it auto& displacement = force_on_my_point_mass; double& displacement_norm = force_norm; // 6.4) There is an upper bound for the movement. if (displacement_norm > core_param->simulation_max_displacement_) { displacement = displacement * (core_param->simulation_max_displacement_ / displacement_norm); } return displacement; } // TODO(neurites) documentation void ApplyDisplacement(const Double3& displacement) override { // FIXME comparing doubles if (displacement == Double3{0, 0, 0}) { return; } // move of our mass SetMassLocation(GetMassLocation() + displacement); // Recompute length, tension and re-center the computation node, and // redefine axis UpdateDependentPhysicalVariables(); UpdateLocalCoordinateAxis(); // FIXME this whole block might be superfluous - ApplyDisplacement is called // For the relatives: recompute the lenght, tension etc. (why for mother? // have to think about that) if (daughter_left_ != nullptr) { daughter_left_->UpdateDependentPhysicalVariables(); daughter_left_->UpdateLocalCoordinateAxis(); } if (daughter_right_ != nullptr) { daughter_right_->UpdateDependentPhysicalVariables(); daughter_right_->UpdateLocalCoordinateAxis(); } } /// Defines the three orthonormal local axis so that a cylindrical coordinate /// system can be used. The `x_axis_` is aligned with the `spring_axis_`. /// The two other are in the plane perpendicular to `spring_axis_`. /// This method to update the axis was suggested by Matt Coock. /// Although not perfectly exact, it is accurate enough for us to use. void UpdateLocalCoordinateAxis() { // x (new) = something new // z (new) = x (new) cross y(old) // y (new) = z(new) cross x(new) auto spring_axis_normalized = spring_axis_; x_axis_ = spring_axis_normalized.Normalize(); z_axis_ = Math::CrossProduct(x_axis_, y_axis_); double norm_of_z = z_axis_.Norm(); if (norm_of_z < 1E-10) { // TODO(neurites) use parameter // If new x_axis_ and old y_axis_ are aligned, we cannot use this scheme; // we start by re-defining new perp vectors. Ok, we loose the previous // info, but this should almost never happen.... auto* random = Simulation::GetActive()->GetRandom(); z_axis_ = Math::Perp3(x_axis_, random->Uniform(0, 1)); } else { z_axis_ = z_axis_ * (1 / norm_of_z); } y_axis_ = Math::CrossProduct(z_axis_, x_axis_); } /// Recomputes diameter after volume has changed. void UpdateDiameter() { double diameter = std::sqrt(4 / Math::kPi * volume_ / actual_length_); if (diameter > diameter_) { Base::SetRunDisplacementForAllNextTs(); } diameter_ = diameter; } /// Recomputes volume, after diameter has been changed. void UpdateVolume() { volume_ = Math::kPi / 4 * diameter_ * diameter_ * actual_length_; } // *************************************************************************** // Coordinates transform // *************************************************************************** /// 3 systems of coordinates : /// /// Global : cartesian coord, defined by orthogonal axis (1,0,0), (0,1,0) /// and (0,0,1) /// with origin at (0,0,0). /// Local : defined by orthogonal axis xAxis (=vect proximal to distal /// end), yAxis and zAxis, /// with origin at proximal end /// Polar : cylindrical coordinates [h,theta,r] with /// h = first local coord (along xAxis), /// theta = angle from yAxis, /// r euclidian distance from xAxis; /// with origin at proximal end /// /// Note: The methods below transform POSITIONS and not DIRECTIONS !!! /// /// G -> L /// L -> G /// /// L -> P /// P -> L /// /// G -> P = G -> L, then L -> P /// P -> P = P -> L, then L -> G /// G -> L /// Returns the position in the local coordinate system (xAxis, yXis, zAxis) /// of a point expressed in global cartesian coordinates /// ([1,0,0],[0,1,0],[0,0,1]). /// @param position in global coordinates Double3 TransformCoordinatesGlobalToLocal(const Double3& position) const { auto pos = position - ProximalEnd(); return {pos * x_axis_, pos * y_axis_, pos * z_axis_}; } /// L -> G /// Returns the position in global cartesian coordinates /// ([1,0,0],[0,1,0],[0,0,1]) /// of a point expressed in the local coordinate system (xAxis, yXis, zAxis). /// @param position in local coordinates Double3 TransformCoordinatesLocalToGlobal(const Double3& position) const { Double3 axis_0{x_axis_[0], y_axis_[0], z_axis_[0]}; Double3 axis_1{x_axis_[1], y_axis_[1], z_axis_[1]}; Double3 axis_2{x_axis_[2], y_axis_[2], z_axis_[2]}; auto x = position * axis_0; auto y = position * axis_1; auto z = position * axis_2; Double3 glob{x, y, z}; return glob + ProximalEnd(); } /// L -> P /// Returns the position in cylindrical coordinates (h,theta,r) /// of a point expressed in the local coordinate system (xAxis, yXis, zAxis). /// @param position in local coordinates Double3 TransformCoordinatesLocalToPolar(const Double3& position) const { return {position[0], std::atan2(position[2], position[1]), std::sqrt(position[1] * position[1] + position[2] * position[2])}; } /// P -> L /// Returns the position in the local coordinate system (xAxis, yXis, zAxis) /// of a point expressed in cylindrical coordinates (h,theta,r). /// @param position in local coordinates Double3 TransformCoordinatesPolarToLocal(const Double3& position) const { return {position[0], position[2] * std::cos(position[1]), position[2] * std::sin(position[1])}; } /// P -> G : P -> L, then L -> G Double3 TransformCoordinatesPolarToGlobal( const std::array& position) const { // the position is in cylindrical coord (h,theta,r) // with r being implicit (half the diameter_) // We thus have h (along x_axis_) and theta (the angle from the y_axis_). double r = 0.5 * diameter_; Double3 polar_position{position[0], position[1], r}; auto local = TransformCoordinatesPolarToLocal(polar_position); return TransformCoordinatesLocalToGlobal(local); } /// G -> L : G -> L, then L -> P Double3 TransformCoordinatesGlobalToPolar(const Double3& position) const { auto local = TransformCoordinatesGlobalToLocal(position); return TransformCoordinatesLocalToPolar(local); } // *************************************************************************** // GETTERS & SETTERS // *************************************************************************** bool IsAxon() const { return is_axon_; } void SetAxon(bool is_axon) { is_axon_ = is_axon; } SoPointer GetMother() { return mother_; } const SoPointer GetMother() const { return mother_; } void SetMother(const SoPointer& mother) { mother_ = mother; } /// @return the (first) distal neurite element, if it exists, /// i.e. if this is not the terminal segment (otherwise returns nullptr). const SoPointer& GetDaughterLeft() const { return daughter_left_; } void SetDaughterLeft(const SoPointer& daughter) { daughter_left_ = daughter; } /// @return the second distal neurite element, if it exists /// i.e. if there is a branching point just after this element (otherwise /// returns nullptr). const SoPointer& GetDaughterRight() const { return daughter_right_; } void SetDaughterRight(const SoPointer& daughter) { daughter_right_ = daughter; } int GetBranchOrder() const { return branch_order_; } void SetBranchOrder(int branch_order) { branch_order_ = branch_order; } double GetActualLength() const { return actual_length_; } /// Should not be used, since the actual length depends on the geometry. void SetActualLength(double actual_length) { if (actual_length > actual_length_) { SetRunDisplacementForAllNextTs(); } actual_length_ = actual_length; } double GetRestingLength() const { return resting_length_; } void SetRestingLength(double resting_length) { resting_length_ = resting_length; } const Double3& GetSpringAxis() const { return spring_axis_; } void SetSpringAxis(const Double3& axis) { SetRunDisplacementForAllNextTs(); spring_axis_ = axis; } double GetSpringConstant() const { return spring_constant_; } void SetSpringConstant(double spring_constant) { spring_constant_ = spring_constant; } double GetTension() const { return tension_; } void SetTension(double tension) { tension_ = tension; } /// NOT A "REAL" GETTER /// Gets a vector of length 1, with the same direction as the SpringAxis. /// @return a normalized spring axis Double3 GetUnitaryAxisDirectionVector() const { double factor = 1.0 / actual_length_; return spring_axis_ * factor; } /// Should return yes if the PhysicalCylinder is considered a terminal branch. /// @return is it a terminal branch bool IsTerminal() const { return daughter_left_ == nullptr; } /// retuns the position of the proximal end, ie the position minus the spring /// axis. /// Is mainly used for paint Double3 ProximalEnd() const { return mass_location_ - spring_axis_; } /// Returns the position of the distal end == position_ const Double3& DistalEnd() const { return mass_location_; } /// Returns the total (actual) length of all the neurite elements (including /// the one in which this method is /// called) before the previous branching point. Used to decide if long enough /// to bifurcate or branch, /// independently of the discretization. double LengthToProximalBranchingPoint() const { double length = actual_length_; if (auto* mother_neurite = dynamic_cast(mother_.Get())) { if (mother_neurite->GetDaughterRight() == nullptr) { length += mother_neurite->LengthToProximalBranchingPoint(); } } return length; } double GetLength() const { return actual_length_; } /// Returns the axis direction of a neurite element const Double3& GetAxis() const { // local coordinate x_axis_ is equal to cylinder axis return x_axis_; } /// Updates the spring axis, the actual length, the tension and the volume. /// /// For tension, `T = k * (aL - rL) / rL`. k = spring constant, /// rL = resting length, aL = actual length. (Note the division by rL. /// Otherwise we could have cylinders with big aL and rL = 0).\n void UpdateDependentPhysicalVariables() override { auto relative_ml = mother_->OriginOf(Base::GetUid()); SetSpringAxis(mass_location_ - relative_ml); SetActualLength(std::sqrt(spring_axis_ * spring_axis_)); UpdatePosition(); if (std::abs(actual_length_ - resting_length_) > 1e-13) { tension_ = spring_constant_ * (actual_length_ - resting_length_) / resting_length_; } else { // avoid floating point rounding effects that increase the tension tension_ = 0.0; } UpdateVolume(); } friend std::ostream& operator<<(std::ostream& str, const NeuriteElement& n) { auto pos = n.GetPosition(); str << "MassLocation: " << n.mass_location_[0] << ", " << n.mass_location_[1] << ", " << n.mass_location_[2] << ", " << std::endl; str << "Position: " << pos[0] << ", " << pos[1] << ", " << pos[2] << ", " << std::endl; str << "x_axis_: " << n.x_axis_[0] << ", " << n.x_axis_[1] << ", " << n.x_axis_[2] << ", " << std::endl; str << "y_axis_: " << n.y_axis_[0] << ", " << n.y_axis_[1] << ", " << n.y_axis_[2] << ", " << std::endl; str << "z_axis_: " << n.z_axis_[0] << ", " << n.z_axis_[1] << ", " << n.z_axis_[2] << ", " << std::endl; str << "spring_axis_: " << n.spring_axis_[0] << ", " << n.spring_axis_[1] << ", " << n.spring_axis_[2] << ", " << std::endl; str << "volume_: " << n.volume_ << std::endl; str << "diameter_: " << n.diameter_ << std::endl; str << "is_axon_: " << n.is_axon_ << std::endl; str << "branch_order_: " << n.branch_order_ << std::endl; str << "actual_length_: " << n.actual_length_ << std::endl; str << "tension_: " << n.tension_ << std::endl; str << "spring_constant_: " << n.spring_constant_ << std::endl; str << "resting_length_: " << n.resting_length_ << std::endl; str << "resting_length_: " << n.resting_length_ << std::endl; str << "d left : " << n.daughter_left_ << std::endl; str << "d right : " << n.daughter_right_ << std::endl; auto* mother_soma = dynamic_cast(n.mother_.Get()); auto* mother_neurite = dynamic_cast(n.mother_.Get()); auto mother = mother_soma ? "neuron" : (mother_neurite ? "neurite" : "nullptr"); str << "mother_ " << mother << std::endl; return str; } protected: void Copy(const NeuriteElement& rhs) { // TODO(neurites) adherence adherence_ = rhs.GetAdherence(); // density_ SetDiameter(rhs.GetDiameter()); // also updates voluume x_axis_ = rhs.GetXAxis(); y_axis_ = rhs.GetYAxis(); z_axis_ = rhs.GetZAxis(); SetSpringAxis(rhs.GetSpringAxis()); branch_order_ = rhs.GetBranchOrder(); spring_constant_ = rhs.GetSpringConstant(); // TODO(neurites) what about actual length, tension and resting_length_ ?? } private: // TODO(lukas) data members same as in cell -> resolve once ROOT-9321 has been // resolved /// mass_location_ is distal end of the cylinder /// NB: Use setter and don't assign values directly Double3 mass_location_ = {{0.0, 0.0, 0.0}}; /// position_ is the middle point of cylinder Double3 position_ = {{0.0, 0.0, 0.0}}; double volume_; /// NB: Use setter and don't assign values directly double diameter_ = 1; double density_; double adherence_; /// First axis of the local coordinate system equal to cylinder axis Double3 x_axis_ = {{1.0, 0.0, 0.0}}; /// Second axis of the local coordinate system. Double3 y_axis_ = {{0.0, 1.0, 0.0}}; /// Third axis of the local coordinate system. Double3 z_axis_ = {{0.0, 0.0, 1.0}}; bool is_axon_ = false; ClassDef(NeuriteElement, 1); /// Parent node in the neuron tree structure can be a Neurite element /// or cell body SoPointer mother_; /// First child node in the neuron tree structure (can only be a Neurite /// element) SoPointer daughter_left_; /// Second child node in the neuron tree structure. (can only be a Neurite /// element) SoPointer daughter_right_; /// number of branching points from here to the soma (root of the neuron /// tree-structure). int branch_order_ = 0; /// The part of the inter-object force transmitted to the mother (parent node) Double3 force_to_transmit_to_proximal_mass_ = {{0, 0, 0}}; /// from the attachment point to the mass location /// (proximal -> distal). /// NB: Use setter and don't assign values directly Double3 spring_axis_ = {{0, 0, 0}}; /// Real length of the PhysicalCylinder (norm of the springAxis). /// NB: Use setter and don't assign values directly double actual_length_ = 1; /// Tension in the cylinder spring. double tension_; /// Spring constant per distance unit (springConstant restingLength = "real" /// spring constant). double spring_constant_; /// The length of the internal spring where tension would be zero. /// T = k*(A-R)/R --> R = k*A/(T+K) double resting_length_; /// \brief Split this neurite element into two segments. /// /// \see SplitNeuriteElementEvent NeuriteElement* SplitNeuriteElement(double distal_portion = 0.5) { auto* ctxt = Simulation::GetActive()->GetExecutionContext(); SplitNeuriteElementEvent event(distal_portion); auto* new_proximal_element = bdm_static_cast(GetInstance(event, this)); ctxt->push_back(new_proximal_element); EventHandler(event, new_proximal_element); return new_proximal_element; } /// Merges two neurite elements together. The one in which the method is /// called phagocytes it's mother. void RemoveProximalNeuriteElement() { // The mother is removed if (a) it is a neurite element and (b) it has no // other daughter than auto* mother_neurite = dynamic_cast(mother_.Get()); if (mother_neurite == nullptr || mother_neurite->GetDaughterRight() != nullptr) { return; } // The guy we gonna remove auto* proximal_ne = mother_neurite; // Re-organisation of the PhysicalObject tree structure: by-passing // proximalCylinder proximal_ne->GetMother()->UpdateRelative(*mother_, *this); SetMother(mother_neurite->GetMother()->GetNeuronOrNeuriteSoPtr()); // Keeping the same tension : // (we don't use updateDependentPhysicalVariables(), because we have tension // and want to // compute restingLength, and not the opposite...) // T = k*(A-R)/R --> R = k*A/(T+K) spring_axis_ = mass_location_ - mother_->OriginOf(Base::GetUid()); SetActualLength(spring_axis_.Norm()); resting_length_ = spring_constant_ * actual_length_ / (tension_ + spring_constant_); // .... and volume_ UpdateVolume(); // and local coord UpdateLocalCoordinateAxis(); proximal_ne->RemoveFromSimulation(); } /// \brief Extend a side neurite element and assign it to daughter right. /// /// \see SideNeuriteExtensionEvent NeuriteElement* ExtendSideNeuriteElement(double length, double diameter, const Double3& direction) { if (daughter_right_ != nullptr) { Fatal( "NeuriteElement", "Can't extend a side neurite since daughter_right is not a nullptr!"); } auto* ctxt = Simulation::GetActive()->GetExecutionContext(); SideNeuriteExtensionEvent event{length, diameter, direction}; auto* new_branch = bdm_static_cast(GetInstance(event, this)); new_branch->EventHandler(event, this); ctxt->push_back(new_branch); EventHandler(event, new_branch); return new_branch; } /// TODO void InitializeNewNeuriteExtension(NeuronSoma* soma, double diameter, double phi, double theta) { auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); tension_ = param->neurite_default_tension_; SetDiameter(param->neurite_default_diameter_); SetActualLength(param->neurite_default_actual_length_); density_ = param->neurite_default_density_; spring_constant_ = param->neurite_default_spring_constant_; adherence_ = param->neurite_default_adherence_; double radius = 0.5 * soma->GetDiameter(); double new_length = param->neurite_default_actual_length_; // position in bdm.cells coord double x_coord = std::sin(theta) * std::cos(phi); double y_coord = std::sin(theta) * std::sin(phi); double z_coord = std::cos(theta); Double3 axis_direction{ x_coord * soma->kXAxis[0] + y_coord * soma->kYAxis[0] + z_coord * soma->kZAxis[0], x_coord * soma->kXAxis[1] + y_coord * soma->kYAxis[1] + z_coord * soma->kZAxis[1], x_coord * soma->kXAxis[2] + y_coord * soma->kYAxis[2] + z_coord * soma->kZAxis[2]}; // positions & axis in cartesian coord auto new_begin_location = soma->GetPosition() + (axis_direction * radius); auto new_spring_axis = axis_direction * new_length; auto new_mass_location = new_begin_location + new_spring_axis; // set attributes of new neurite segment SetDiameter(diameter); UpdateVolume(); SetSpringAxis(new_spring_axis); SetMassLocation(new_mass_location); UpdatePosition(); SetActualLength(new_length); SetRestingLengthForDesiredTension(param->neurite_default_tension_); UpdateLocalCoordinateAxis(); // family relations SetMother(soma->GetSoPtr()); } /// TODO void InitializeNeuriteBifurcation(NeuriteElement* mother, double length, double diameter, const Double3& direction) { auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); tension_ = param->neurite_default_tension_; SetDiameter(param->neurite_default_diameter_); SetActualLength(param->neurite_default_actual_length_); density_ = param->neurite_default_density_; spring_constant_ = param->neurite_default_spring_constant_; adherence_ = param->neurite_default_adherence_; Copy(*mother); SetMother(mother->GetSoPtr()); // check that the directions are not pointing backwards auto dir_1 = direction; // todo avoid cpy const auto& mother_spring_axis = mother->GetSpringAxis(); if (Math::AngleRadian(mother_spring_axis, direction) > Math::kPi / 2.0) { auto proj = Math::ProjectionOnto(direction, mother_spring_axis); proj = proj * -1; dir_1 = direction + proj; } // mass location and spring axis const auto& mother_ml = mother->GetMassLocation(); dir_1.Normalize(); SetSpringAxis(dir_1 * length); SetMassLocation(mother_ml + spring_axis_); UpdatePosition(); UpdateLocalCoordinateAxis(); // (important so that x_axis_ is correct) // physics of tension : SetActualLength(length); SetRestingLengthForDesiredTension(param->neurite_default_tension_); // set local coordinate axis in the new branches // TODO(neurites) again?? alreay done a few lines up UpdateLocalCoordinateAxis(); // 2) creating the first daughter branch SetDiameter(diameter); branch_order_ = mother->GetBranchOrder() + 1; UpdateDependentPhysicalVariables(); } /// Neurite branching is composed of neurite splitting and side neurite /// extension. To avoid code duplication in constructors, logic has been moved /// here. /// \see SplitNeuriteElementEvent, NeuriteBranchingEvent void InitializeSplitOrBranching(NeuriteElement* other, double distal_portion) { auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); tension_ = param->neurite_default_tension_; SetDiameter(param->neurite_default_diameter_); SetActualLength(param->neurite_default_actual_length_); density_ = param->neurite_default_density_; spring_constant_ = param->neurite_default_spring_constant_; adherence_ = param->neurite_default_adherence_; const auto& other_ml = other->GetMassLocation(); const auto& other_sa = other->GetSpringAxis(); const auto& other_rl = other->GetRestingLength(); // TODO(neurites) reformulate to mass_location_ auto new_position = other_ml - (other_sa * distal_portion); SetPosition(new_position); Copy(*other); UpdatePosition(); // family relations SetMother(other->GetMother()->GetNeuronOrNeuriteSoPtr()); SetDaughterLeft(other->GetSoPtr()); // physics resting_length_ = ((1 - distal_portion) * other_rl); } /// Neurite branching is composed of neurite splitting and side neurite /// extension. To avoid code duplication in constructors, logic has been moved /// here. void InitializeSideExtensionOrBranching(NeuriteElement* mother, double length, double diameter, const Double3& direction) { auto* param = Simulation::GetActive()->GetParam()->GetModuleParam(); tension_ = param->neurite_default_tension_; SetDiameter(param->neurite_default_diameter_); SetActualLength(param->neurite_default_actual_length_); density_ = param->neurite_default_density_; spring_constant_ = param->neurite_default_spring_constant_; adherence_ = param->neurite_default_adherence_; Copy(*mother); auto dir = direction; auto direction_normalized = direction; direction_normalized.Normalize(); const auto& mother_spring_axis = mother->GetSpringAxis(); double angle_with_side_branch = Math::AngleRadian(mother_spring_axis, direction); if (angle_with_side_branch < 0.78 || angle_with_side_branch > 2.35) { // 45-135 degrees auto p = Math::CrossProduct(mother_spring_axis, direction); p = Math::CrossProduct(p, mother_spring_axis); dir = direction_normalized + p.Normalize(); } // location of mass and computation center auto new_spring_axis = direction_normalized * length; const auto& mother_ml = mother->GetMassLocation(); SetSpringAxis(new_spring_axis); SetMassLocation(mother_ml + new_spring_axis); UpdatePosition(); // physics SetActualLength(length); SetRestingLengthForDesiredTension(param->neurite_default_tension_); SetDiameter(param->neurite_default_diameter_); UpdateLocalCoordinateAxis(); // family relations SetMother(mother->GetSoPtr()); branch_order_ = mother->GetBranchOrder() + 1; SetDiameter(diameter); // correct physical values (has to be after family relations UpdateDependentPhysicalVariables(); } }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_NEURITE_ELEMENT_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_NEURON_OR_NEURITE_H_ #define NEUROSCIENCE_NEURON_OR_NEURITE_H_ #include #include "core/container/math_array.h" #include "core/sim_object/sim_object.h" #include "core/sim_object/so_pointer.h" namespace bdm { namespace experimental { namespace neuroscience { class NeuriteElement; class NeuronSoma; /// The mother of a neurite element can either be a neuron or a neurite. /// This class declares this interface. class NeuronOrNeurite { public: virtual ~NeuronOrNeurite(); virtual SoUid GetUid() const = 0; SoPointer GetNeuronOrNeuriteSoPtr() const; bool IsNeuronSoma() const; bool IsNeuriteElement() const; virtual Double3 OriginOf(SoUid daughter_uid) const = 0; virtual void RemoveDaughter(const SoPointer& daughter) = 0; virtual void UpdateDependentPhysicalVariables() = 0; virtual void UpdateRelative(const NeuronOrNeurite& old_rel, const NeuronOrNeurite& new_rel) = 0; }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_NEURON_OR_NEURITE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_NEURON_SOMA_H_ #define NEUROSCIENCE_NEURON_SOMA_H_ #include #include #include "core/container/math_array.h" #include "core/sim_object/cell.h" #include "neuroscience/neuron_or_neurite.h" namespace bdm { namespace experimental { namespace neuroscience { class NeuriteElement; class NeuronSoma : public Cell, public NeuronOrNeurite { BDM_SIM_OBJECT_HEADER(NeuronSoma, Cell, 1, daughters_, daughters_coord_); public: NeuronSoma(); virtual ~NeuronSoma(); explicit NeuronSoma(const Double3& position); /// \brief This constructor is used to initialise the values of daughter /// 2 for a cell division event. /// /// Please note that this implementation does not allow division of neuron /// somas with already attached neurite elements. /// /// \see CellDivisionEvent NeuronSoma(const Event& event, SimObject* mother_so, uint64_t new_oid = 0); NeuronSoma(const NeuronSoma& other) : Base(other), daughters_(other.daughters_), daughters_coord_(other.daughters_coord_) {} /// \brief EventHandler to modify the data members of this cell /// after a cell division, or new neurite branching event /// /// Performs the transition mother to daughter 1 /// \param event contains parameters for cell division /// \param daughter_2 pointer to new cell (=daughter 2) /// \see Event, CellDivisionEvent void EventHandler(const Event& event, SimObject* other1, SimObject* other2 = nullptr) override; SoUid GetUid() const override { return Base::GetUid(); } // *************************************************************************** // METHODS FOR NEURON TREE STRUCTURE * // *************************************************************************** /// \brief Extend a new neurite from this soma. /// /// Uses default diameter for new neurite /// \see NewNeuriteExtensionEvent NeuriteElement* ExtendNewNeurite(const Double3& direction, NeuriteElement* prototype = nullptr); /// \brief Extend a new neurite from this soma. /// /// \see NewNeuriteExtensionEvent NeuriteElement* ExtendNewNeurite(double diameter, double phi, double theta, NeuriteElement* prototype = nullptr); void RemoveDaughter(const SoPointer& daughter) override; /// Returns the absolute coordinates of the location where the daughter is /// attached. /// @param daughter_element_idx element_idx of the daughter /// @return the coord Double3 OriginOf(SoUid daughter_uid) const override; void UpdateDependentPhysicalVariables() override; void UpdateRelative(const NeuronOrNeurite& old_rel, const NeuronOrNeurite& new_rel) override; const std::vector>& GetDaughters() const; protected: std::vector> daughters_; /// Daughter attachment points in local coordinates /// Key: neurite segment uid /// Value: position std::unordered_map daughters_coord_; private: BDM_CLASS_DEF_OVERRIDE(NeuronSoma, 1); }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_NEURON_SOMA_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_NEUROSCIENCE_H_ #define NEUROSCIENCE_NEUROSCIENCE_H_ #include "neuroscience/event/neurite_bifurcation_event.h" #include "neuroscience/event/neurite_branching_event.h" #include "neuroscience/event/new_neurite_extension_event.h" #include "neuroscience/event/side_neurite_extension_event.h" #include "neuroscience/event/split_neurite_element_event.h" #include "neuroscience/module.h" #include "neuroscience/neurite_element.h" #include "neuroscience/neuron_soma.h" #include "neuroscience/param.h" #endif // NEUROSCIENCE_NEUROSCIENCE_H_ // ----------------------------------------------------------------------------- // // Copyright (C) The BioDynaMo Project. // All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // // See the LICENSE file distributed with this work for details. // See the NOTICE file distributed with this work for additional information // regarding copyright ownership. // // ----------------------------------------------------------------------------- #ifndef NEUROSCIENCE_PARAM_H_ #define NEUROSCIENCE_PARAM_H_ #include #include #include #include #include #include "core/param/module_param.h" #include "core/util/root.h" #include "cpptoml/cpptoml.h" namespace bdm { namespace experimental { namespace neuroscience { struct Param : public ModuleParam { static const ModuleParamUid kUid; ModuleParam* GetCopy() const override; ModuleParamUid GetUid() const override; /// Default actual length value of a neurite.\n /// Default value: `1.0`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_actual_length = 1.0 double neurite_default_actual_length_ = 1.0; /// Default density value of a neurite.\n /// Default value: `1.0`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_density = 1.0 double neurite_default_density_ = 1.0; /// Default diameter value of a neurite.\n /// Default value: `1.0`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_diameter = 1.0 double neurite_default_diameter_ = 1.0; /// Default spring constant value of a neurite.\n /// Default value: `10`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_spring_constant = 10 double neurite_default_spring_constant_ = 10; /// Default adherence value of a neurite.\n /// Default value: `0.1`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_adherence = 0.1 double neurite_default_adherence_ = 0.1; /// Default tension value of a neurite.\n /// Default value: `0.0`\n /// TOML config file: /// /// [neuroscience] /// neurite_default_tension = 0.0 double neurite_default_tension_ = 0.0; /// Minimum allowed length of a neurite element.\n /// Default value: `2.0`\n /// TOML config file: /// /// [neuroscience] /// neurite_min_length = 2.0 double neurite_min_length_ = 2.0; /// Maximum allowed length of a neurite element.\n /// Default value: `15`\n /// TOML config file: /// /// [neuroscience] /// neurite_max_length = 15 double neurite_max_length_ = 15; /// Minumum bifurcation length of a neurite element.\n /// If the length is below this threshold, bifurcation is not permitted.\n /// Default value: `0`\n /// TOML config file: /// /// [neuroscience] /// neurite_minimial_bifurcation_length = 0 double neurite_minimial_bifurcation_length_ = 0; protected: /// Assign values from config file to variables void AssignFromConfig(const std::shared_ptr&) override; private: BDM_CLASS_DEF_OVERRIDE(Param, 1); }; } // namespace neuroscience } // namespace experimental } // namespace bdm #endif // NEUROSCIENCE_PARAM_H_ #undef _BACKWARD_BACKWARD_WARNING_H )DICTPAYLOAD"; static const char* classesHeaders[]={ "bdm::BaseBiologyModule", payloadCode, "@", "bdm::Cell", payloadCode, "@", "bdm::DiffusionGrid", payloadCode, "@", "bdm::GrowDivide", payloadCode, "@", "bdm::IntegralTypeWrapper", payloadCode, "@", "bdm::IntegralTypeWrapper", payloadCode, "@", "bdm::MathArray", payloadCode, "@", "bdm::MathArray", payloadCode, "@", "bdm::ModuleParam", payloadCode, "@", "bdm::Param", payloadCode, "@", "bdm::Random", payloadCode, "@", "bdm::RegulateGenes", payloadCode, "@", "bdm::ResourceManager", payloadCode, "@", "bdm::RuntimeVariables", payloadCode, "@", "bdm::Simulation", payloadCode, "@", "bdm::SoHandle", payloadCode, "@", "bdm::experimental::neuroscience::NeuriteElement", payloadCode, "@", "bdm::experimental::neuroscience::NeuronSoma", payloadCode, "@", "bdm::experimental::neuroscience::Param", payloadCode, "@", nullptr}; static bool isInitialized = false; if (!isInitialized) { TROOT::RegisterModule("libbiodynamo_dict", headers, includePaths, payloadCode, fwdDeclCode, TriggerDictionaryInitialization_libbiodynamo_dict_Impl, {}, classesHeaders, /*has no C++ module*/false); isInitialized = true; } } static struct DictInit { DictInit() { TriggerDictionaryInitialization_libbiodynamo_dict_Impl(); } } __TheDictionaryInitializer; } void TriggerDictionaryInitialization_libbiodynamo_dict() { TriggerDictionaryInitialization_libbiodynamo_dict_Impl(); }