gen_model.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2021 by
  4. * The Salk Institute for Biological Studies
  5. *
  6. * Use of this source code is governed by an MIT-style
  7. * license that can be found in the LICENSE file or at
  8. * https://opensource.org/licenses/MIT.
  9. *
  10. ******************************************************************************/
  11. #include <sstream>
  12. #include "api/pybind11_stl_include.h"
  13. #include "api/python_export_utils.h"
  14. #include "gen_model.h"
  15. #include "api/model.h"
  16. #include "api/base_chkpt_mol.h"
  17. #include "api/color.h"
  18. #include "api/complex.h"
  19. #include "api/api_config.h"
  20. #include "api/count.h"
  21. #include "api/elementary_molecule_type.h"
  22. #include "api/geometry_object.h"
  23. #include "api/instantiation.h"
  24. #include "api/mol_wall_hit_info.h"
  25. #include "api/molecule.h"
  26. #include "api/notifications.h"
  27. #include "api/observables.h"
  28. #include "api/reaction_info.h"
  29. #include "api/reaction_rule.h"
  30. #include "api/region.h"
  31. #include "api/release_site.h"
  32. #include "api/species.h"
  33. #include "api/subsystem.h"
  34. #include "api/surface_class.h"
  35. #include "api/viz_output.h"
  36. #include "api/wall.h"
  37. #include "api/wall_wall_hit_info.h"
  38. #include "api/warnings.h"
  39. namespace MCell {
  40. namespace API {
  41. std::shared_ptr<Model> GenModel::copy_model() const {
  42. std::shared_ptr<Model> res = std::make_shared<Model>(DefaultCtorArgType());
  43. res->config = config;
  44. res->warnings = warnings;
  45. res->notifications = notifications;
  46. res->species = species;
  47. res->reaction_rules = reaction_rules;
  48. res->surface_classes = surface_classes;
  49. res->elementary_molecule_types = elementary_molecule_types;
  50. res->release_sites = release_sites;
  51. res->geometry_objects = geometry_objects;
  52. res->checkpointed_molecules = checkpointed_molecules;
  53. res->viz_outputs = viz_outputs;
  54. res->counts = counts;
  55. return res;
  56. }
  57. std::shared_ptr<Model> GenModel::deepcopy_model(py::dict) const {
  58. std::shared_ptr<Model> res = std::make_shared<Model>(DefaultCtorArgType());
  59. res->config = config;
  60. res->warnings = warnings;
  61. res->notifications = notifications;
  62. for (const auto& item: species) {
  63. res->species.push_back((is_set(item)) ? item->deepcopy_species() : nullptr);
  64. }
  65. for (const auto& item: reaction_rules) {
  66. res->reaction_rules.push_back((is_set(item)) ? item->deepcopy_reaction_rule() : nullptr);
  67. }
  68. for (const auto& item: surface_classes) {
  69. res->surface_classes.push_back((is_set(item)) ? item->deepcopy_surface_class() : nullptr);
  70. }
  71. for (const auto& item: elementary_molecule_types) {
  72. res->elementary_molecule_types.push_back((is_set(item)) ? item->deepcopy_elementary_molecule_type() : nullptr);
  73. }
  74. for (const auto& item: release_sites) {
  75. res->release_sites.push_back((is_set(item)) ? item->deepcopy_release_site() : nullptr);
  76. }
  77. for (const auto& item: geometry_objects) {
  78. res->geometry_objects.push_back((is_set(item)) ? item->deepcopy_geometry_object() : nullptr);
  79. }
  80. for (const auto& item: checkpointed_molecules) {
  81. res->checkpointed_molecules.push_back((is_set(item)) ? item->deepcopy_base_chkpt_mol() : nullptr);
  82. }
  83. for (const auto& item: viz_outputs) {
  84. res->viz_outputs.push_back((is_set(item)) ? item->deepcopy_viz_output() : nullptr);
  85. }
  86. for (const auto& item: counts) {
  87. res->counts.push_back((is_set(item)) ? item->deepcopy_count() : nullptr);
  88. }
  89. return res;
  90. }
  91. bool GenModel::__eq__(const Model& other) const {
  92. return
  93. config == other.config &&
  94. warnings == other.warnings &&
  95. notifications == other.notifications &&
  96. vec_ptr_eq(species, other.species) &&
  97. vec_ptr_eq(reaction_rules, other.reaction_rules) &&
  98. vec_ptr_eq(surface_classes, other.surface_classes) &&
  99. vec_ptr_eq(elementary_molecule_types, other.elementary_molecule_types) &&
  100. vec_ptr_eq(release_sites, other.release_sites) &&
  101. vec_ptr_eq(geometry_objects, other.geometry_objects) &&
  102. vec_ptr_eq(checkpointed_molecules, other.checkpointed_molecules) &&
  103. vec_ptr_eq(viz_outputs, other.viz_outputs) &&
  104. vec_ptr_eq(counts, other.counts);
  105. }
  106. bool GenModel::eq_nonarray_attributes(const Model& other, const bool ignore_name) const {
  107. return
  108. config == other.config &&
  109. warnings == other.warnings &&
  110. notifications == other.notifications &&
  111. true /*species*/ &&
  112. true /*reaction_rules*/ &&
  113. true /*surface_classes*/ &&
  114. true /*elementary_molecule_types*/ &&
  115. true /*release_sites*/ &&
  116. true /*geometry_objects*/ &&
  117. true /*checkpointed_molecules*/ &&
  118. true /*viz_outputs*/ &&
  119. true /*counts*/;
  120. }
  121. std::string GenModel::to_str(const bool all_details, const std::string ind) const {
  122. std::stringstream ss;
  123. ss << "Model" << ": " <<
  124. "\n" << ind + " " << "config=" << "(" << config.to_str(all_details, ind + " ") << ")" << ", " << "\n" << ind + " " <<
  125. "warnings=" << "(" << warnings.to_str(all_details, ind + " ") << ")" << ", " << "\n" << ind + " " <<
  126. "notifications=" << "(" << notifications.to_str(all_details, ind + " ") << ")" << ", " << "\n" << ind + " " <<
  127. "species=" << vec_ptr_to_str(species, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  128. "reaction_rules=" << vec_ptr_to_str(reaction_rules, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  129. "surface_classes=" << vec_ptr_to_str(surface_classes, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  130. "elementary_molecule_types=" << vec_ptr_to_str(elementary_molecule_types, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  131. "release_sites=" << vec_ptr_to_str(release_sites, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  132. "geometry_objects=" << vec_ptr_to_str(geometry_objects, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  133. "checkpointed_molecules=" << vec_ptr_to_str(checkpointed_molecules, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  134. "viz_outputs=" << vec_ptr_to_str(viz_outputs, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  135. "counts=" << vec_ptr_to_str(counts, all_details, ind + " ");
  136. return ss.str();
  137. }
  138. py::class_<Model> define_pybinding_Model(py::module& m) {
  139. return py::class_<Model, std::shared_ptr<Model>>(m, "Model", "This is the main class that is used to assemble all simulation input \nand configuration. It also provides methods to do initialization,\nrun simulation, and introspect the running simulation.\n")
  140. .def(
  141. py::init<
  142. >()
  143. )
  144. .def("__copy__", &Model::copy_model)
  145. .def("__deepcopy__", &Model::deepcopy_model, py::arg("memo"))
  146. .def("__str__", &Model::to_str, py::arg("all_details") = false, py::arg("ind") = std::string(""))
  147. .def("__eq__", &Model::__eq__, py::arg("other"))
  148. .def("initialize", &Model::initialize, py::arg("print_copyright") = true, "Initializes model, initialization blocks most of changes to \ncontained components. \n\n- print_copyright: Prints information about MCell.\n\n")
  149. .def("run_iterations", &Model::run_iterations, py::arg("iterations"), "Runs specified number of iterations. Returns the number of iterations\nexecuted (it might be less than the requested number of iterations when \na checkpoint was scheduled). \n\n- iterations: Number of iterations to run. Value is truncated to an integer.\n\n")
  150. .def("end_simulation", &Model::end_simulation, py::arg("print_final_report") = true, "Generates the last visualization and reaction output (if they are included \nin the model), then flushes all buffers and optionally prints simulation report. \nBuffers are also flushed when the Model object is destroyed such as when Ctrl-C\nis pressed during simulation. \n\n- print_final_report: Print information on simulation time and counts of selected events.\n\n")
  151. .def("add_subsystem", &Model::add_subsystem, py::arg("subsystem"), "Adds all components of a Subsystem object to the model.\n- subsystem\n")
  152. .def("add_instantiation", &Model::add_instantiation, py::arg("instantiation"), "Adds all components of an Instantiation object to the model.\n- instantiation\n")
  153. .def("add_observables", &Model::add_observables, py::arg("observables"), "Adds all counts and viz outputs of an Observables object to the model.\n- observables\n")
  154. .def("dump_internal_state", &Model::dump_internal_state, py::arg("with_geometry") = false, "Prints out the simulation engine's internal state, mainly for debugging.\n- with_geometry: Include geometry in the dump.\n\n")
  155. .def("export_data_model", &Model::export_data_model, py::arg("file") = STR_UNSET, "Exports the current state of the model into a data model JSON format.\nDoes not export state of molecules.\nMust be called after model initialization.\nAlways exports the current state, i.e. with the current geometry and reaction rates. \nEvents (ReleaseSites and VizOutputs) with scheduled time other than zero are not exported correctly yet. \n\n- file: If file is not set, then uses the first VizOutput to determine the target directory \nand creates name using the current iteration. Fails if argument file is not set and \nthere is no VizOutput in the model.\n\n\n")
  156. .def("export_viz_data_model", &Model::export_viz_data_model, py::arg("file") = STR_UNSET, "Same as export_data_model, only the created data model will contain only information required for visualization\nin CellBlender. This makes the loading of the model by CellBlender faster and also allows to avoid potential\ncompatibility issues.\nMust be called after model initialization.\n\n- file: Optional path to the output data model file.\n\n")
  157. .def("export_geometry", &Model::export_geometry, py::arg("output_files_prefix") = STR_UNSET, "Exports model geometry as Wavefront OBJ format. \nMust be called after model initialization.\nDoes not export material colors (yet).\n\n- output_files_prefix: Optional prefix for .obj and .mtl files that will be created on export. \nIf output_files_prefix is not set, then uses the first VizOutput to determine the target directory \nand creates names using the current iteration. Fails if argument output_files_prefix is not set and \nthere is no VizOutput in the model.\n\n\n")
  158. .def("release_molecules", &Model::release_molecules, py::arg("release_site"), "Performs immediate release of molecules based on the definition of the release site argument.\nThe ReleaseSite.release_time must not be in the past and must be within the current iteration \nmeaning that the time must be greater or equal iteration * time_step and less than (iteration + 1) * time_step.\nThe ReleaseEvent must not use a release_pattern because this is an immediate release and it is not \nscheduled into the global scheduler.\n\n- release_site\n")
  159. .def("run_reaction", &Model::run_reaction, py::arg("reaction_rule"), py::arg("reactant_ids"), py::arg("time"), "Run a single reaction on reactants. Callbacks will be called if they are registered for the given reaction.\nReturns a list of product IDs.\nNote: only unimolecular reactions are currently supported.\n\n- reaction_rule: Reaction rule to run.\n\n- reactant_ids: The number of reactants for a unimolecular reaction must be 1 and for a bimolecular reaction must be 2.\nReactants for a bimolecular reaction do not have to be listed in the same order as in the reaction rule definition. \n\n\n- time: Precise time in seconds when this reaction occurs. Important to know for how long the products\nwill be diffused when they are created in a middle of a time step. \n\n\n")
  160. .def("add_vertex_move", &Model::add_vertex_move, py::arg("object"), py::arg("vertex_index"), py::arg("displacement"), "Appends information about a displacement for given object's vertex into an internal list of vertex moves. \nTo do the actual geometry change, call Model.apply_vertex_moves.\nThe reason why we first need to collect all changes and then apply them all at the same time is for performance\nreasons. \n\n- object: Object whose vertex will be changed.\n\n- vertex_index: Index of vertex in object's vertex list that will be changed.\n\n- displacement: Change of vertex coordinates [x, y, z] (in um) that will be added to the current \ncoordinates of the vertex.\n\n\n")
  161. .def("apply_vertex_moves", &Model::apply_vertex_moves, py::arg("collect_wall_wall_hits") = false, py::arg("randomize_order") = true, "Applies all the vertex moves specified with Model.add_vertex_move call.\n\nAll affected vertices are first divided based on to which geometery object they belong. \nThen each object is manipulated one by one. \n\nDuring vertex moves, collisions are checked:\na) When a moved vertex hits a wall of another object, it is stopped at the wall.\nb) When a second object's vertex would end up inside the moved object, the vertex move \nthat would cause it is canceled (its displacement set to 0) because finding the maximum \ndistance we can move is too computationally expensive. To minimize the impact of this \ncancellation, the vertices should be moved only by a small distance.\n\nApplying vertex moves also takes paired molecules into account: \nWhen moves are applied to an object, all moved molecules that are paired are collected.\nFor each of the paired molecules, we collect displacements for each \nof the vertices of the 'primary' wall where this molecule is located (that were provided by the user \nthrough add_vertex_move, and were possibly truncated due to collisions).\nThen we find the second wall where the second molecule of the pair is located.\nFor each of the vertices of all 'secondary' walls, we collect a list of displacements\nthat move the vertices of 'primary' walls. \nThen, an average displacement is computed for each vertex, and these average displacements\nare used to move the 'secondary' walls.\nWhen a 'primary' wall collides, its displacement is clamped or canceled. This is true even if \nit collides with a 'secondary' wall that would be otherwise moved. So, the displacement of the \n'primary' wall will mostly just pull the 'secondary' wall, not push. Therefore it is needed \nthat both objects are active and pull each other. \n\nThis process is well commented in MCell code: \n`partition.cpp <https://github.com/mcellteam/mcell/blob/master/src4/partition.cpp>`_ in functions\napply_vertex_moves, apply_vertex_moves_per_object, and move_walls_with_paired_molecules. \n \nWhen argument collect_wall_wall_hits is True, a list of wall pairs that collided is returned,\nwhen collect_wall_wall_hits is False, an empty list is returned.\n\n- collect_wall_wall_hits: When set to True, a list of wall pairs that collided is returned,\notherwise an empty list is returned.\n\n\n- randomize_order: When set to True (default), the ordering of the vertex move list created by add_vertex_move\ncalls is randomized. This allows to avoid any bias in the resulting positions of surface\nmolecules. \nHowever, the individual vertex moves are then sorted by the object to which the vertex belongs\nand the moves are applied object by object for correctness. Setting this to True also radomizes the \norder of objects to which the vertex moves are applied.\n\n\n")
  162. .def("pair_molecules", &Model::pair_molecules, py::arg("id1"), py::arg("id2"), "Sets that two surface molecules are paired. Paired molecules bind walls together\nand when one wall is moved, the wall that is bound through a paired molecule is moved as well.\nThrows exception if the molecule ids are not surface molecules.\nThrows exception if the molecules are on the same object. \nThrows exception if any of the molecules is already paired.\nMay be called only after model initialization.\n\n- id1\n- id2\n")
  163. .def("unpair_molecules", &Model::unpair_molecules, py::arg("id1"), py::arg("id2"), "Sets that two surface molecules are not paired. \nThrows exception if the molecule ids are not surface molecules. \nThrows exception if the molecules are not paired together.\nMay be called only after model initialization.\n\n- id1\n- id2\n")
  164. .def("get_paired_molecule", &Model::get_paired_molecule, py::arg("id"), "Return id of the molecule to which the molecule with 'id' is paired.\nReturns ID_INVALID (-1) when the molecule is not paired.\nMay be called only after model initialization.\n\n- id\n")
  165. .def("get_paired_molecules", &Model::get_paired_molecules, "Returns a dictionary that contains all molecules that are paired.\nMolecule ids are keys and the value associated with the key is the second paired molecule.\nThe returned dictionary is a copy and any changes made to it are ignored by MCell.\nNote: The reason why uint32 is used as the base type for the dictionary but type int is used\neverywhere else for molecule ids is only for performance reasons. \n")
  166. .def("register_mol_wall_hit_callback", &Model::register_mol_wall_hit_callback, py::arg("function"), py::arg("context"), py::arg("object") = nullptr, py::arg("species") = nullptr, "Register a callback for event when a molecule hits a wall. \nMay be called only after model initialization because it internally uses geometry object\nand species ids that are set during the initialization. \n\n- function: Callback function to be called. \nThe function must have two arguments MolWallHitInfo and context.\nDo not modify the received MolWallHitInfo object since it may be reused for other \nwall hit callbacks (e.g. when the first callback is for a specific geometry object and \nthe second callback is for any geometry object). \nThe context object (py::object type argument) is on the other hand provided \nto be modified and one can for instance use it to count the number of hits.. \n\n\n- context: Context passed to the callback function, the callback function can store\ninformation to this object. Some context must be always passed, even when \nit is a useless python object. \n\n\n- object: Only hits of this object will be reported, any object hit is reported when not set.\n\n- species: Only hits of molecules of this species will be reported, any hit of volume molecules of \nany species is reported when this argument is not set.\nSets an internal flag for this species to make sure that the species id does not change \nduring simulation. \n\n\n")
  167. .def("register_reaction_callback", &Model::register_reaction_callback, py::arg("function"), py::arg("context"), py::arg("reaction_rule"), "Defines a function to be called when a reaction was processed.\nIt is allowed to do state modifications except for removing reacting molecules, \nthey will be removed automatically after return from this callback. \nUnlimited number of reaction callbacks is allowed. \nMay be called only after model initialization because it internally uses \nreaction rule ids that are set during the initialization. \n\n- function: Callback function to be called. \nThe function must have two arguments ReactionInfo and context.\nCalled right after a reaction occured but before the reactants were removed.\nAfter return the reaction proceeds and reactants are removed (unless they were kept\nby the reaction such as with reaction A + B -> A + C).\n\n\n- context: Context passed to the callback function, the callback function can store\ninformation to this object. Some context must be always passed, even when \nit is a useless python object. \n\n\n- reaction_rule: The callback function will be called whenever this reaction rule is applied.\n\n")
  168. .def("load_bngl", &Model::load_bngl, py::arg("file_name"), py::arg("observables_path_or_file") = STR_UNSET, py::arg("default_release_region") = nullptr, py::arg("parameter_overrides") = std::map<std::string, double>(), py::arg("observables_output_format") = CountOutputFormat::AUTOMATIC_FROM_EXTENSION, "Loads sections: molecule types, reaction rules, seed species, and observables from a BNGL file\nand creates objects in the current model according to it.\nAll elementary molecule types used in the seed species section must be defined in subsystem.\nIf an item in the seed species section does not have its compartment set,\nthe argument default_region must be set and the molecules are released into or onto the \ndefault_region. \n\n- file_name: Path to the BNGL file to be loaded.\n\n- observables_path_or_file: Directory prefix or file name where observable values will be stored.\nIf a directory such as './react_data/seed_' + str(SEED).zfill(5) + '/' or an empty \nstring/unset is used, each observable gets its own file and the output file format for created Count \nobjects is CountOutputFormat.DAT.\nWhen not set, this path is used: './react_data/seed_' + str(model.config.seed).zfill(5) + '/'.\nIf a file has a .gdat extension such as \n'./react_data/seed_' + str(SEED).zfill(5) + '/counts.gdat', all observable are stored in this \nfile and the output file format for created Count objects is CountOutputFormat.GDAT.\nMust not be empty when observables_output_format is explicitly set to CountOutputFormat.GDAT.\n\n\n- default_release_region: Used as region for releases for seed species that have no compartments specified.\n\n\n- parameter_overrides: For each key k in the parameter_overrides, if it is defined in the BNGL's parameters section,\nits value is ignored and instead value parameter_overrides[k] is used.\n\n\n- observables_output_format: Selection of output format. Default setting uses automatic detection\nbased on contents of the 'observables_path_or_file' attribute.\n\n\n")
  169. .def("export_to_bngl", &Model::export_to_bngl, py::arg("file_name"), py::arg("simulation_method") = BNGSimulationMethod::ODE, "Exports all defined species, reaction rules and applicable observables\nas a BNGL file that can be then loaded by MCell4 or BioNetGen. \nThe resulting file should be validated that it produces expected results. \nMany MCell features cannot be exported into BNGL and when such a feature is \nencountered the export fails with a RuntimeError exception.\nHowever, the export code tries to export as much as possible and one can catch\nthe RuntimeError exception and use the possibly incomplete BNGL file anyway. \n\n- file_name: Output file name.\n\n- simulation_method: Selection of the BioNetGen simulation method. \nSelects BioNetGen action to run with the selected simulation method.\nFor BNGSimulationMethod.NF the export is limited to a single volume and\na single surface and the enerated rates use volume and surface area so that \nsimulation with NFSim produces corect results. \n\n\n")
  170. .def("save_checkpoint", &Model::save_checkpoint, py::arg("custom_dir") = STR_UNSET, "Saves current model state as checkpoint. \nThe default directory structure is checkpoints/seed_<SEED>/it_<ITERATION>,\nit can be changed by setting 'custom_dir'.\nIf used during an iteration such as in a callback, an event is scheduled for the \nbeginning of the next iteration. This scheduled event saves the checkpoint. \n\n- custom_dir: Sets custom directory where the checkpoint will be stored. \nThe default is 'checkpoints/seed_<SEED>/it_<ITERATION>'. \n\n\n")
  171. .def("schedule_checkpoint", &Model::schedule_checkpoint, py::arg("iteration") = 0, py::arg("continue_simulation") = false, py::arg("custom_dir") = STR_UNSET, "Schedules checkpoint save event that will occur when an iteration is started. \nThis means that it will be executed right before any other events scheduled for \nthe given iteration are executed.\nCan be called asynchronously at any time after initialization.\n\n- iteration: Specifies iteration number when the checkpoint save will occur. \nPlease note that iterations are counted from 0.\nTo schedule a checkpoint for the closest time as possible, keep the default value 0,\nthis will schedule checkpoint for the beginning of the iteration with number current iteration + 1. \nIf calling schedule_checkpoint from a different thread (e.g. by using threading.Timer), \nit is highly recommended to keep the default value 0 or choose some time that will be \nfor sure in the future.\n\n\n- continue_simulation: When false, saving the checkpoint means that we want to terminate the simulation \nright after the save. The currently running function Model.run_iterations\nwill not simulate any following iterations and execution will return from this function\nto execute the next statement which is usually 'model.end_simulation()'.\nWhen true, the checkpoint is saved and simulation continues uninterrupted.\n \n\n\n- custom_dir: Sets custom directory where the checkpoint will be stored. \nThe default is 'checkpoints/seed_<SEED>/it_<ITERATION>'. \n\n\n")
  172. .def("add_species", &Model::add_species, py::arg("s"), "Add a reference to a Species object to the species list.\n- s\n")
  173. .def("find_species", &Model::find_species, py::arg("name"), "Find a Species object using name in the species list. \nReturns None if no such species is found.\n\n- name\n")
  174. .def("add_reaction_rule", &Model::add_reaction_rule, py::arg("r"), "Add a reference to a ReactionRule object to the reaction_rules list.\n- r\n")
  175. .def("find_reaction_rule", &Model::find_reaction_rule, py::arg("name"), "Find a ReactionRule object using name in the reaction_rules list. \nReturns None if no such reaction rule is found.\n\n- name\n")
  176. .def("add_surface_class", &Model::add_surface_class, py::arg("sc"), "Add a reference to a SurfaceClass object to the surface_classes list.\n- sc\n")
  177. .def("find_surface_class", &Model::find_surface_class, py::arg("name"), "Find a SurfaceClass object using name in the surface_classes list. \nReturns None if no such surface class is found.\n\n- name\n")
  178. .def("add_elementary_molecule_type", &Model::add_elementary_molecule_type, py::arg("mt"), "Add a reference to an ElementaryMoleculeType object to the elementary_molecule_types list.\n- mt\n")
  179. .def("find_elementary_molecule_type", &Model::find_elementary_molecule_type, py::arg("name"), "Find an ElementaryMoleculeType object using name in the elementary_molecule_types list. \nReturns None if no such elementary molecule type is found.\n\n- name\n")
  180. .def("load_bngl_molecule_types_and_reaction_rules", &Model::load_bngl_molecule_types_and_reaction_rules, py::arg("file_name"), py::arg("parameter_overrides") = std::map<std::string, double>(), "Parses a BNGL file, only reads molecule types and reaction rules sections, \ni.e. ignores observables and seed species. \nParameter values are evaluated and the result value is directly used. \nCompartments names are stored in rxn rules as strings because compartments belong \nto geometry objects and the subsystem is independent on specific geometry.\nHowever, the compartments and their objects must be defined before initialization.\n\n- file_name: Path to the BNGL file to be loaded.\n\n- parameter_overrides: For each key k in the parameter_overrides, if it is defined in the BNGL's parameters section,\nits value is ignored and instead value parameter_overrides[k] is used.\n\n\n")
  181. .def("add_release_site", &Model::add_release_site, py::arg("s"), "Adds a reference to the release site s to the list of release sites.\n- s\n")
  182. .def("find_release_site", &Model::find_release_site, py::arg("name"), "Finds a release site by its name, returns None if no such release site is present.\n- name\n")
  183. .def("add_geometry_object", &Model::add_geometry_object, py::arg("o"), "Adds a reference to the geometry object o to the list of geometry objects.\n- o\n")
  184. .def("find_geometry_object", &Model::find_geometry_object, py::arg("name"), "Finds a geometry object by its name, returns None if no such geometry object is present.\n- name\n")
  185. .def("find_volume_compartment_object", &Model::find_volume_compartment_object, py::arg("name"), "Finds a geometry object by its name, the geometry object must be a BNGL compartment.\nReturns None if no such geometry object is present.\n\n- name\n")
  186. .def("find_surface_compartment_object", &Model::find_surface_compartment_object, py::arg("name"), "Finds a geometry object that is a BNGL compartment and its surface name is name.\nReturns None if no such geometry object is present.\n\n- name\n")
  187. .def("load_bngl_compartments_and_seed_species", &Model::load_bngl_compartments_and_seed_species, py::arg("file_name"), py::arg("default_release_region") = nullptr, py::arg("parameter_overrides") = std::map<std::string, double>(), "First loads section compartments and for each 3D compartment that does not \nalready exist as a geometry object in this Instantiation object, creates a \nbox with compartment's volume and also sets its 2D (membrane) compartment name.\nWhen multiple identical geometry objects are added to the final Model object, \nonly one copy is left so one can merge multiple Instantiation objects that created \ncompartments assuming that their volume is the same. \nThen loads section seed species from a BNGL file and creates release sites according to it.\nAll elementary molecule types used in the seed species section must be already defined in subsystem.\nIf an item in the BNGL seed species section does not have its compartment set,\nthe argument default_region must be set and the molecules are then released into or onto the \ndefault_region. \n\n- file_name: Path to the BNGL file.\n\n- default_release_region: Used as region for releases for seed species that have no compartments specified.\n\n\n- parameter_overrides: For each key k in the parameter_overrides, if it is defined in the BNGL's parameters section,\nits value is ignored and instead value parameter_overrides[k] is used.\n\n\n")
  188. .def("add_viz_output", &Model::add_viz_output, py::arg("viz_output"), "Adds a reference to the viz_output object to the list of visualization output specifications.\n- viz_output\n")
  189. .def("add_count", &Model::add_count, py::arg("count"), "Adds a reference to the count object to the list of count specifications.\n- count\n")
  190. .def("find_count", &Model::find_count, py::arg("name"), "Finds a count object by its name, returns None if no such count is present.\n- name\n")
  191. .def("load_bngl_observables", &Model::load_bngl_observables, py::arg("file_name"), py::arg("observables_path_or_file") = STR_UNSET, py::arg("parameter_overrides") = std::map<std::string, double>(), py::arg("observables_output_format") = CountOutputFormat::AUTOMATIC_FROM_EXTENSION, "Loads section observables from a BNGL file and creates Count objects according to it.\nAll elementary molecule types used in the seed species section must be defined in subsystem.\n\n- file_name: Path to the BNGL file.\n\n- observables_path_or_file: Directory prefix or file name where observable values will be stored.\nIf a directory such as './react_data/seed_' + str(SEED).zfill(5) + '/' or an empty \nstring/unset is used, each observable gets its own file and the output file format for created Count \nobjects is CountOutputFormat.DAT.\nWhen not set, this path is used: './react_data/seed_' + str(model.config.seed).zfill(5) + '/'.\nIf a file has a .gdat extension such as \n'./react_data/seed_' + str(SEED).zfill(5) + '/counts.gdat', all observable are stored in this \nfile and the output file format for created Count objects is CountOutputFormat.GDAT.\nMust not be empty when observables_output_format is explicitly set to CountOutputFormat.GDAT.\n\n\n- parameter_overrides: For each key k in the parameter_overrides, if it is defined in the BNGL's parameters section,\nits value is ignored and instead value parameter_overrides[k] is used.\n\n\n- observables_output_format: Selection of output format. Default setting uses automatic detection\nbased on contents of the 'observables_path_or_file' attribute.\n \n\n\n")
  192. .def("get_molecule_ids", &Model::get_molecule_ids, py::arg("pattern") = nullptr, "Returns a list of ids of molecules.\nIf the arguments pattern is not set, the list of all molecule ids is returned. \nIf the argument pattern is set, the list of all molecule ids whose species match \nthe pattern is returned. \n\n- pattern: BNGL pattern to select molecules based on their species, might use compartments.\n\n")
  193. .def("get_molecule", &Model::get_molecule, py::arg("id"), "Returns a information on a molecule from the simulated environment, \nNone if the molecule does not exist.\n\n- id: Unique id of the molecule to be retrieved.\n\n")
  194. .def("get_species_name", &Model::get_species_name, py::arg("species_id"), "Returns a string representing canonical species name in the BNGL format.\n\n- species_id: Id of the species.\n\n")
  195. .def("get_vertex", &Model::get_vertex, py::arg("object"), py::arg("vertex_index"), "Returns coordinates of a vertex.\n- object\n- vertex_index: This is the index of the vertex in the geometry object's walls (wall_list).\n\n")
  196. .def("get_wall", &Model::get_wall, py::arg("object"), py::arg("wall_index"), "Returns information about a wall belonging to a given object.\n- object: Geometry object whose wall to retrieve.\n\n- wall_index: This is the index of the wall in the geometry object's walls (wall_list).\n\n")
  197. .def("get_vertex_unit_normal", &Model::get_vertex_unit_normal, py::arg("object"), py::arg("vertex_index"), "Returns sum of all wall normals that use this vertex converted to a unit vector of \nlength 1 um (micrometer).\nThis represents the unit vector pointing outwards from the vertex.\n\n- object: Geometry object whose vertex to retrieve.\n\n- vertex_index: This is the index of the vertex in the geometry object's vertex_list.\n\n")
  198. .def("get_wall_unit_normal", &Model::get_wall_unit_normal, py::arg("object"), py::arg("wall_index"), "Returns wall normal converted to a unit vector of length 1um.\n- object: Geometry object whose wall's normal to retrieve.\n\n- wall_index: This is the index of the vertex in the geometry object's walls (wall_list).\n\n")
  199. .def("get_wall_color", &Model::get_wall_color, py::arg("object"), py::arg("wall_index"), "Returns color of a wall.\n- object: Geometry object whose wall's color to retrieve.\n\n- wall_index: This is the index of the vertex in the geometry object's walls (wall_list).\n\n")
  200. .def("set_wall_color", &Model::set_wall_color, py::arg("object"), py::arg("wall_index"), py::arg("color"), "Sets color of a wall.\n- object: Geometry object whose wall's color to retrieve.\n\n- wall_index: This is the index of the vertex in the geometry object's walls (wall_list).\n\n- color: Color to be set.\n\n")
  201. .def("dump", &Model::dump)
  202. .def_property("config", &Model::get_config, &Model::set_config, "Simulation configuration.")
  203. .def_property("warnings", &Model::get_warnings, &Model::set_warnings, "Configuration on how to report warnings.")
  204. .def_property("notifications", &Model::get_notifications, &Model::set_notifications, "Configuration on how to report certain notifications.")
  205. .def_property("species", &Model::get_species, &Model::set_species, py::return_value_policy::reference, "List of species to be included in the model for initialization.\nUsed usually only for simple species (species that are defined using a\nsingle molecule type without components such as 'A').\nOther species may be created inside simulation \n")
  206. .def_property("reaction_rules", &Model::get_reaction_rules, &Model::set_reaction_rules, py::return_value_policy::reference)
  207. .def_property("surface_classes", &Model::get_surface_classes, &Model::set_surface_classes, py::return_value_policy::reference)
  208. .def_property("elementary_molecule_types", &Model::get_elementary_molecule_types, &Model::set_elementary_molecule_types, py::return_value_policy::reference, "Contains list of elementary molecule types with their diffusion constants and other information. \nPopulated when a BNGL file is loaded and also on initialization from Species objects present in \nthe species list.\n")
  209. .def_property("release_sites", &Model::get_release_sites, &Model::set_release_sites, py::return_value_policy::reference, "List of release sites to be included in the model. \n")
  210. .def_property("geometry_objects", &Model::get_geometry_objects, &Model::set_geometry_objects, py::return_value_policy::reference, "List of geometry objects to be included in the model. \n")
  211. .def_property("checkpointed_molecules", &Model::get_checkpointed_molecules, &Model::set_checkpointed_molecules, py::return_value_policy::reference, "Used when resuming simulation from a checkpoint.\n")
  212. .def_property("viz_outputs", &Model::get_viz_outputs, &Model::set_viz_outputs, py::return_value_policy::reference, "List of visualization outputs to be included in the model.\nThere is usually just one VizOutput object. \n")
  213. .def_property("counts", &Model::get_counts, &Model::set_counts, py::return_value_policy::reference, "List of counts to be included in the model.\n")
  214. ;
  215. }
  216. std::string GenModel::export_to_python(std::ostream& out, PythonExportContext& ctx) {
  217. # if 0 // not to be used
  218. std::string exported_name = "model";
  219. bool str_export = export_as_string_without_newlines();
  220. std::string nl = "";
  221. std::string ind = " ";
  222. std::stringstream ss;
  223. if (!str_export) {
  224. nl = "\n";
  225. ind = " ";
  226. ss << exported_name << " = ";
  227. }
  228. ss << "m.Model(" << nl;
  229. if (species != std::vector<std::shared_ptr<Species>>() && !skip_vectors_export()) {
  230. ss << ind << "species = " << export_vec_species(out, ctx, exported_name) << "," << nl;
  231. }
  232. if (reaction_rules != std::vector<std::shared_ptr<ReactionRule>>() && !skip_vectors_export()) {
  233. ss << ind << "reaction_rules = " << export_vec_reaction_rules(out, ctx, exported_name) << "," << nl;
  234. }
  235. if (surface_classes != std::vector<std::shared_ptr<SurfaceClass>>() && !skip_vectors_export()) {
  236. ss << ind << "surface_classes = " << export_vec_surface_classes(out, ctx, exported_name) << "," << nl;
  237. }
  238. if (elementary_molecule_types != std::vector<std::shared_ptr<ElementaryMoleculeType>>() && !skip_vectors_export()) {
  239. ss << ind << "elementary_molecule_types = " << export_vec_elementary_molecule_types(out, ctx, exported_name) << "," << nl;
  240. }
  241. if (release_sites != std::vector<std::shared_ptr<ReleaseSite>>() && !skip_vectors_export()) {
  242. ss << ind << "release_sites = " << export_vec_release_sites(out, ctx, exported_name) << "," << nl;
  243. }
  244. if (geometry_objects != std::vector<std::shared_ptr<GeometryObject>>() && !skip_vectors_export()) {
  245. ss << ind << "geometry_objects = " << export_vec_geometry_objects(out, ctx, exported_name) << "," << nl;
  246. }
  247. if (checkpointed_molecules != std::vector<std::shared_ptr<BaseChkptMol>>() && !skip_vectors_export()) {
  248. ss << ind << "checkpointed_molecules = " << export_vec_checkpointed_molecules(out, ctx, exported_name) << "," << nl;
  249. }
  250. if (viz_outputs != std::vector<std::shared_ptr<VizOutput>>() && !skip_vectors_export()) {
  251. ss << ind << "viz_outputs = " << export_vec_viz_outputs(out, ctx, exported_name) << "," << nl;
  252. }
  253. if (counts != std::vector<std::shared_ptr<Count>>() && !skip_vectors_export()) {
  254. ss << ind << "counts = " << export_vec_counts(out, ctx, exported_name) << "," << nl;
  255. }
  256. if (config != Config()) {
  257. ss << ind << "config = " << config.export_to_python(out, ctx) << "," << nl;
  258. }
  259. if (warnings != Warnings()) {
  260. ss << ind << "warnings = " << warnings.export_to_python(out, ctx) << "," << nl;
  261. }
  262. if (notifications != Notifications()) {
  263. ss << ind << "notifications = " << notifications.export_to_python(out, ctx) << "," << nl;
  264. }
  265. ss << ")" << nl << nl;
  266. if (!str_export) {
  267. out << ss.str();
  268. return exported_name;
  269. }
  270. else {
  271. return ss.str();
  272. }
  273. #else // # if 0
  274. assert(false);
  275. return "";
  276. #endif
  277. }
  278. std::string GenModel::export_vec_species(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  279. // prints vector into 'out' and returns name of the generated list
  280. std::stringstream ss;
  281. std::string exported_name;
  282. if (parent_name != ""){
  283. exported_name = parent_name+ "_species";
  284. }
  285. else {
  286. exported_name = "species";
  287. }
  288. ss << exported_name << " = [\n";
  289. for (size_t i = 0; i < species.size(); i++) {
  290. const auto& item = species[i];
  291. if (i == 0) {
  292. ss << " ";
  293. }
  294. else if (i % 16 == 0) {
  295. ss << "\n ";
  296. }
  297. if (!item->skip_python_export()) {
  298. std::string name = item->export_to_python(out, ctx);
  299. ss << name << ", ";
  300. }
  301. }
  302. ss << "\n]\n\n";
  303. out << ss.str();
  304. return exported_name;
  305. }
  306. std::string GenModel::export_vec_reaction_rules(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  307. // prints vector into 'out' and returns name of the generated list
  308. std::stringstream ss;
  309. std::string exported_name;
  310. if (parent_name != ""){
  311. exported_name = parent_name+ "_reaction_rules";
  312. }
  313. else {
  314. exported_name = "reaction_rules";
  315. }
  316. ss << exported_name << " = [\n";
  317. for (size_t i = 0; i < reaction_rules.size(); i++) {
  318. const auto& item = reaction_rules[i];
  319. if (i == 0) {
  320. ss << " ";
  321. }
  322. else if (i % 16 == 0) {
  323. ss << "\n ";
  324. }
  325. if (!item->skip_python_export()) {
  326. std::string name = item->export_to_python(out, ctx);
  327. ss << name << ", ";
  328. }
  329. }
  330. ss << "\n]\n\n";
  331. out << ss.str();
  332. return exported_name;
  333. }
  334. std::string GenModel::export_vec_surface_classes(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  335. // prints vector into 'out' and returns name of the generated list
  336. std::stringstream ss;
  337. std::string exported_name;
  338. if (parent_name != ""){
  339. exported_name = parent_name+ "_surface_classes";
  340. }
  341. else {
  342. exported_name = "surface_classes";
  343. }
  344. ss << exported_name << " = [\n";
  345. for (size_t i = 0; i < surface_classes.size(); i++) {
  346. const auto& item = surface_classes[i];
  347. if (i == 0) {
  348. ss << " ";
  349. }
  350. else if (i % 16 == 0) {
  351. ss << "\n ";
  352. }
  353. if (!item->skip_python_export()) {
  354. std::string name = item->export_to_python(out, ctx);
  355. ss << name << ", ";
  356. }
  357. }
  358. ss << "\n]\n\n";
  359. out << ss.str();
  360. return exported_name;
  361. }
  362. std::string GenModel::export_vec_elementary_molecule_types(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  363. // prints vector into 'out' and returns name of the generated list
  364. std::stringstream ss;
  365. std::string exported_name;
  366. if (parent_name != ""){
  367. exported_name = parent_name+ "_elementary_molecule_types";
  368. }
  369. else {
  370. exported_name = "elementary_molecule_types";
  371. }
  372. ss << exported_name << " = [\n";
  373. for (size_t i = 0; i < elementary_molecule_types.size(); i++) {
  374. const auto& item = elementary_molecule_types[i];
  375. if (i == 0) {
  376. ss << " ";
  377. }
  378. else if (i % 16 == 0) {
  379. ss << "\n ";
  380. }
  381. if (!item->skip_python_export()) {
  382. std::string name = item->export_to_python(out, ctx);
  383. ss << name << ", ";
  384. }
  385. }
  386. ss << "\n]\n\n";
  387. out << ss.str();
  388. return exported_name;
  389. }
  390. std::string GenModel::export_vec_release_sites(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  391. // prints vector into 'out' and returns name of the generated list
  392. std::stringstream ss;
  393. std::string exported_name;
  394. if (parent_name != ""){
  395. exported_name = parent_name+ "_release_sites";
  396. }
  397. else {
  398. exported_name = "release_sites";
  399. }
  400. ss << exported_name << " = [\n";
  401. for (size_t i = 0; i < release_sites.size(); i++) {
  402. const auto& item = release_sites[i];
  403. if (i == 0) {
  404. ss << " ";
  405. }
  406. else if (i % 16 == 0) {
  407. ss << "\n ";
  408. }
  409. if (!item->skip_python_export()) {
  410. std::string name = item->export_to_python(out, ctx);
  411. ss << name << ", ";
  412. }
  413. }
  414. ss << "\n]\n\n";
  415. out << ss.str();
  416. return exported_name;
  417. }
  418. std::string GenModel::export_vec_geometry_objects(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  419. // prints vector into 'out' and returns name of the generated list
  420. std::stringstream ss;
  421. std::string exported_name;
  422. if (parent_name != ""){
  423. exported_name = parent_name+ "_geometry_objects";
  424. }
  425. else {
  426. exported_name = "geometry_objects";
  427. }
  428. ss << exported_name << " = [\n";
  429. for (size_t i = 0; i < geometry_objects.size(); i++) {
  430. const auto& item = geometry_objects[i];
  431. if (i == 0) {
  432. ss << " ";
  433. }
  434. else if (i % 16 == 0) {
  435. ss << "\n ";
  436. }
  437. if (!item->skip_python_export()) {
  438. std::string name = item->export_to_python(out, ctx);
  439. ss << name << ", ";
  440. }
  441. }
  442. ss << "\n]\n\n";
  443. out << ss.str();
  444. return exported_name;
  445. }
  446. std::string GenModel::export_vec_checkpointed_molecules(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  447. // prints vector into 'out' and returns name of the generated list
  448. std::stringstream ss;
  449. std::string exported_name;
  450. if (parent_name != ""){
  451. exported_name = parent_name+ "_checkpointed_molecules";
  452. }
  453. else {
  454. exported_name = "checkpointed_molecules";
  455. }
  456. ss << exported_name << " = [\n";
  457. for (size_t i = 0; i < checkpointed_molecules.size(); i++) {
  458. const auto& item = checkpointed_molecules[i];
  459. if (i == 0) {
  460. ss << " ";
  461. }
  462. else if (i % 16 == 0) {
  463. ss << "\n ";
  464. }
  465. if (!item->skip_python_export()) {
  466. std::string name = item->export_to_python(out, ctx);
  467. ss << name << ", ";
  468. }
  469. }
  470. ss << "\n]\n\n";
  471. out << ss.str();
  472. return exported_name;
  473. }
  474. std::string GenModel::export_vec_viz_outputs(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  475. // prints vector into 'out' and returns name of the generated list
  476. std::stringstream ss;
  477. std::string exported_name;
  478. if (parent_name != ""){
  479. exported_name = parent_name+ "_viz_outputs";
  480. }
  481. else {
  482. exported_name = "viz_outputs";
  483. }
  484. ss << exported_name << " = [\n";
  485. for (size_t i = 0; i < viz_outputs.size(); i++) {
  486. const auto& item = viz_outputs[i];
  487. if (i == 0) {
  488. ss << " ";
  489. }
  490. else if (i % 16 == 0) {
  491. ss << "\n ";
  492. }
  493. if (!item->skip_python_export()) {
  494. std::string name = item->export_to_python(out, ctx);
  495. ss << name << ", ";
  496. }
  497. }
  498. ss << "\n]\n\n";
  499. out << ss.str();
  500. return exported_name;
  501. }
  502. std::string GenModel::export_vec_counts(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  503. // prints vector into 'out' and returns name of the generated list
  504. std::stringstream ss;
  505. std::string exported_name;
  506. if (parent_name != ""){
  507. exported_name = parent_name+ "_counts";
  508. }
  509. else {
  510. exported_name = "counts";
  511. }
  512. ss << exported_name << " = [\n";
  513. for (size_t i = 0; i < counts.size(); i++) {
  514. const auto& item = counts[i];
  515. if (i == 0) {
  516. ss << " ";
  517. }
  518. else if (i % 16 == 0) {
  519. ss << "\n ";
  520. }
  521. if (!item->skip_python_export()) {
  522. std::string name = item->export_to_python(out, ctx);
  523. ss << name << ", ";
  524. }
  525. }
  526. ss << "\n]\n\n";
  527. out << ss.str();
  528. return exported_name;
  529. }
  530. } // namespace API
  531. } // namespace MCell