gen_reaction_rule.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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_reaction_rule.h"
  15. #include "api/reaction_rule.h"
  16. #include "api/complex.h"
  17. namespace MCell {
  18. namespace API {
  19. void GenReactionRule::check_semantics() const {
  20. }
  21. void GenReactionRule::set_initialized() {
  22. vec_set_initialized(reactants);
  23. vec_set_initialized(products);
  24. initialized = true;
  25. }
  26. void GenReactionRule::set_all_attributes_as_default_or_unset() {
  27. class_name = "ReactionRule";
  28. name = STR_UNSET;
  29. reactants = std::vector<std::shared_ptr<Complex>>();
  30. products = std::vector<std::shared_ptr<Complex>>();
  31. fwd_rate = FLT_UNSET;
  32. rev_name = STR_UNSET;
  33. rev_rate = FLT_UNSET;
  34. variable_rate = std::vector<std::vector<double>>();
  35. is_intermembrane_surface_reaction = false;
  36. }
  37. std::shared_ptr<ReactionRule> GenReactionRule::copy_reaction_rule() const {
  38. std::shared_ptr<ReactionRule> res = std::make_shared<ReactionRule>(DefaultCtorArgType());
  39. res->class_name = class_name;
  40. res->name = name;
  41. res->reactants = reactants;
  42. res->products = products;
  43. res->fwd_rate = fwd_rate;
  44. res->rev_name = rev_name;
  45. res->rev_rate = rev_rate;
  46. res->variable_rate = variable_rate;
  47. res->is_intermembrane_surface_reaction = is_intermembrane_surface_reaction;
  48. return res;
  49. }
  50. std::shared_ptr<ReactionRule> GenReactionRule::deepcopy_reaction_rule(py::dict) const {
  51. std::shared_ptr<ReactionRule> res = std::make_shared<ReactionRule>(DefaultCtorArgType());
  52. res->class_name = class_name;
  53. res->name = name;
  54. for (const auto& item: reactants) {
  55. res->reactants.push_back((is_set(item)) ? item->deepcopy_complex() : nullptr);
  56. }
  57. for (const auto& item: products) {
  58. res->products.push_back((is_set(item)) ? item->deepcopy_complex() : nullptr);
  59. }
  60. res->fwd_rate = fwd_rate;
  61. res->rev_name = rev_name;
  62. res->rev_rate = rev_rate;
  63. res->variable_rate = variable_rate;
  64. res->is_intermembrane_surface_reaction = is_intermembrane_surface_reaction;
  65. return res;
  66. }
  67. bool GenReactionRule::__eq__(const ReactionRule& other) const {
  68. return
  69. name == other.name &&
  70. vec_ptr_eq(reactants, other.reactants) &&
  71. vec_ptr_eq(products, other.products) &&
  72. fwd_rate == other.fwd_rate &&
  73. rev_name == other.rev_name &&
  74. rev_rate == other.rev_rate &&
  75. variable_rate == other.variable_rate &&
  76. is_intermembrane_surface_reaction == other.is_intermembrane_surface_reaction;
  77. }
  78. bool GenReactionRule::eq_nonarray_attributes(const ReactionRule& other, const bool ignore_name) const {
  79. return
  80. (ignore_name || name == other.name) &&
  81. true /*reactants*/ &&
  82. true /*products*/ &&
  83. fwd_rate == other.fwd_rate &&
  84. rev_name == other.rev_name &&
  85. rev_rate == other.rev_rate &&
  86. true /*variable_rate*/ &&
  87. is_intermembrane_surface_reaction == other.is_intermembrane_surface_reaction;
  88. }
  89. std::string GenReactionRule::to_str(const bool all_details, const std::string ind) const {
  90. std::stringstream ss;
  91. ss << get_object_name() << ": " <<
  92. "name=" << name << ", " <<
  93. "\n" << ind + " " << "reactants=" << vec_ptr_to_str(reactants, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  94. "products=" << vec_ptr_to_str(products, all_details, ind + " ") << ", " << "\n" << ind + " " <<
  95. "fwd_rate=" << fwd_rate << ", " <<
  96. "rev_name=" << rev_name << ", " <<
  97. "rev_rate=" << rev_rate << ", " <<
  98. "variable_rate=" << vec_nonptr_to_str(variable_rate, all_details, ind + " ") << ", " <<
  99. "is_intermembrane_surface_reaction=" << is_intermembrane_surface_reaction;
  100. return ss.str();
  101. }
  102. py::class_<ReactionRule> define_pybinding_ReactionRule(py::module& m) {
  103. return py::class_<ReactionRule, std::shared_ptr<ReactionRule>>(m, "ReactionRule", "Represents a BioNetGen Language (BNGL) reaction rule. \nIn BNGL, a reaction is simply one or more transformations\napplied simultaneously to one or more species. The following\ntransformations (and their combinations) are allowed:\n * Forming a bond, e.g. A(b) + B(a) -> A(b!0).B(a!0)\n * Breaking a bond, e.g. A(b!0).B(a!0)-> A(b)+ B(a)\n * Changing of component state, e.g. X(y~0) -> X(y~p)\n * Creating a molecule, e.g. A(b) -> A(b) + C(d)\n * Destroying a molecule, e.g. A(b) + B(a) -> A(b) or A -> Null \n (Null here means that there is no product)\n * Changing species of a bound molecule when the molecule type has the \n same components, e.g. A(b!0).B(a!0)-> A(b!0).C(a!0)\n \nAlso compartments may be specified in reactants (patterns) and for products.\nSpecial compartment classes supported by MCell4 are @IN and @OUT.\nThey can be used in surface reactions to constrain a reaction with a volume molecule \nhitting a surface molecule from the inside or outside of the compartment, \ne.g. A(s)@IN + S(a) -> S(a!1).A(s!1) and/or to define the location of the \nproduct, e.g. S(a!1).A(s!1) -> S(a) + A(s)@OUT. \n")
  104. .def(
  105. py::init<
  106. const std::string&,
  107. const std::vector<std::shared_ptr<Complex>>,
  108. const std::vector<std::shared_ptr<Complex>>,
  109. const double,
  110. const std::string&,
  111. const double,
  112. const std::vector<std::vector<double>>,
  113. const bool
  114. >(),
  115. py::arg("name") = STR_UNSET,
  116. py::arg("reactants") = std::vector<std::shared_ptr<Complex>>(),
  117. py::arg("products") = std::vector<std::shared_ptr<Complex>>(),
  118. py::arg("fwd_rate") = FLT_UNSET,
  119. py::arg("rev_name") = STR_UNSET,
  120. py::arg("rev_rate") = FLT_UNSET,
  121. py::arg("variable_rate") = std::vector<std::vector<double>>(),
  122. py::arg("is_intermembrane_surface_reaction") = false
  123. )
  124. .def("check_semantics", &ReactionRule::check_semantics)
  125. .def("__copy__", &ReactionRule::copy_reaction_rule)
  126. .def("__deepcopy__", &ReactionRule::deepcopy_reaction_rule, py::arg("memo"))
  127. .def("__str__", &ReactionRule::to_str, py::arg("all_details") = false, py::arg("ind") = std::string(""))
  128. .def("__eq__", &ReactionRule::__eq__, py::arg("other"))
  129. .def("to_bngl_str", &ReactionRule::to_bngl_str, "Creates a string that corresponds to the reaction rule's BNGL representation, does not contain rates.")
  130. .def("dump", &ReactionRule::dump)
  131. .def_property("name", &ReactionRule::get_name, &ReactionRule::set_name, "Name of the reaction. If this is a reversible reaction, then it is the name of the \nreaction in forward direction.\n")
  132. .def_property("reactants", &ReactionRule::get_reactants, &ReactionRule::set_reactants, py::return_value_policy::reference, "List of reactant patterns. Must contain one or two patterns.")
  133. .def_property("products", &ReactionRule::get_products, &ReactionRule::set_products, py::return_value_policy::reference, "List of reactant patterns. May be empty.")
  134. .def_property("fwd_rate", &ReactionRule::get_fwd_rate, &ReactionRule::set_fwd_rate, "The units of the reaction rate for uni- and bimolecular reactions are:\n * [s^-1] for unimolecular reactions,\n * [N^-1*s^-1] bimolecular reactions between two surface molecules on different objects \n (this is a highly experimental feature and the unit will likely change in the future, \n not sure if probability is computed correctly, it works the way that the surface molecule \n is first diffused and then a potential collisions within the distance of Config.intermembrane_interaction_radius\n are evaluated). \nOther bimolecular reaction units depend on Model.config.use_bng_units settings.\nWhen use_bng_units is False (default), traditional MCell units are used: \n * [M^-1*s^-1] for bimolecular reactions between either two volume molecules, a volume molecule \n and a surface (molecule), \n * [um^2*N^-1*s^-1] bimolecular reactions between two surface molecules on the same surface, and\nWhen use_bng_units is True, units compatible with BioNetGen's ODE, SSA, and PLA solvers are used:\n * [um^3*N^-1*s^-1] for any bimolecular reactions, surface-surface reaction rate conversion assumes 10nm membrane thickness. \nM is the molarity of the solution and N the number of reactants.\nMay be changed after model initialization. \nSetting of value is ignored if the rate does not change. \nIf the new value differs from previous, updates all information related \nto the new rate including recomputation of reaction times for molecules if this is a\nunimolecular reaction.\n")
  135. .def_property("rev_name", &ReactionRule::get_rev_name, &ReactionRule::set_rev_name, "Name of the reaction in reverse direction. \n")
  136. .def_property("rev_rate", &ReactionRule::get_rev_rate, &ReactionRule::set_rev_rate, "Reverse reactions rate, reaction is unidirectional when not specified.\nMay be changed after model initialization, in the case behaves the same was as for \nchanging the 'fwd_rate'. \nUses the same units as 'fwd_rate'.\n")
  137. .def_property("variable_rate", &ReactionRule::get_variable_rate, &ReactionRule::set_variable_rate, py::return_value_policy::reference, "The array passed as this argument must have as its items a pair of floats (time in s, rate).\nMust be sorted by time (this is not checked). \nVariable rate is applicable only for irreversible reactions.\nWhen simulation starts and the table does not contain value for time 0, the initial fwd_rate is set to 0.\nWhen time advances after the last time in this table, the last rate is used for all subsequent iterations. \nMembers fwd_rate and rev_rate must not be set when setting this attribute through a constructor. \nWhen this attribute is set outside of the class constructor, fwd_rate is automatically reset to an 'unset' value.\nCannot be set after model initialization. \n")
  138. .def_property("is_intermembrane_surface_reaction", &ReactionRule::get_is_intermembrane_surface_reaction, &ReactionRule::set_is_intermembrane_surface_reaction, "Experimental, see addintinal explanation in 'fwd' rate.\nThen set to true, this is a special type of surface-surface reaction that \nallows for two surface molecules to react when they are on different geometrical objects. \nNote: This support is limited for now, the reaction rule must be in the form of A + B -> C + D \nwhere all reactants and products must be surface molecules and their orientation must be 'any' (default). \n")
  139. ;
  140. }
  141. std::string GenReactionRule::export_to_python(std::ostream& out, PythonExportContext& ctx) {
  142. if (!export_even_if_already_exported() && ctx.already_exported(this)) {
  143. return ctx.get_exported_name(this);
  144. }
  145. std::string exported_name = std::string("reaction_rule") + "_" + (is_set(name) ? fix_id(name) : std::to_string(ctx.postinc_counter("reaction_rule")));
  146. if (!export_even_if_already_exported()) {
  147. ctx.add_exported(this, exported_name);
  148. }
  149. bool str_export = export_as_string_without_newlines();
  150. std::string nl = "";
  151. std::string ind = " ";
  152. std::stringstream ss;
  153. if (!str_export) {
  154. nl = "\n";
  155. ind = " ";
  156. ss << exported_name << " = ";
  157. }
  158. ss << "m.ReactionRule(" << nl;
  159. if (name != STR_UNSET) {
  160. ss << ind << "name = " << "'" << name << "'" << "," << nl;
  161. }
  162. if (reactants != std::vector<std::shared_ptr<Complex>>() && !skip_vectors_export()) {
  163. ss << ind << "reactants = " << export_vec_reactants(out, ctx, exported_name) << "," << nl;
  164. }
  165. if (products != std::vector<std::shared_ptr<Complex>>() && !skip_vectors_export()) {
  166. ss << ind << "products = " << export_vec_products(out, ctx, exported_name) << "," << nl;
  167. }
  168. if (fwd_rate != FLT_UNSET) {
  169. ss << ind << "fwd_rate = " << f_to_str(fwd_rate) << "," << nl;
  170. }
  171. if (rev_name != STR_UNSET) {
  172. ss << ind << "rev_name = " << "'" << rev_name << "'" << "," << nl;
  173. }
  174. if (rev_rate != FLT_UNSET) {
  175. ss << ind << "rev_rate = " << f_to_str(rev_rate) << "," << nl;
  176. }
  177. if (variable_rate != std::vector<std::vector<double>>() && !skip_vectors_export()) {
  178. ss << ind << "variable_rate = " << export_vec_variable_rate(out, ctx, exported_name) << "," << nl;
  179. }
  180. if (is_intermembrane_surface_reaction != false) {
  181. ss << ind << "is_intermembrane_surface_reaction = " << is_intermembrane_surface_reaction << "," << nl;
  182. }
  183. ss << ")" << nl << nl;
  184. if (!str_export) {
  185. out << ss.str();
  186. return exported_name;
  187. }
  188. else {
  189. return ss.str();
  190. }
  191. }
  192. std::string GenReactionRule::export_vec_reactants(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  193. // does not print the array itself to 'out' and returns the whole list
  194. std::stringstream ss;
  195. ss << "[";
  196. for (size_t i = 0; i < reactants.size(); i++) {
  197. const auto& item = reactants[i];
  198. if (i == 0) {
  199. ss << " ";
  200. }
  201. else if (i % 16 == 0) {
  202. ss << "\n ";
  203. }
  204. if (!item->skip_python_export()) {
  205. std::string name = item->export_to_python(out, ctx);
  206. ss << name << ", ";
  207. }
  208. }
  209. ss << "]";
  210. return ss.str();
  211. }
  212. std::string GenReactionRule::export_vec_products(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  213. // does not print the array itself to 'out' and returns the whole list
  214. std::stringstream ss;
  215. ss << "[";
  216. for (size_t i = 0; i < products.size(); i++) {
  217. const auto& item = products[i];
  218. if (i == 0) {
  219. ss << " ";
  220. }
  221. else if (i % 16 == 0) {
  222. ss << "\n ";
  223. }
  224. if (!item->skip_python_export()) {
  225. std::string name = item->export_to_python(out, ctx);
  226. ss << name << ", ";
  227. }
  228. }
  229. ss << "]";
  230. return ss.str();
  231. }
  232. std::string GenReactionRule::export_vec_variable_rate(std::ostream& out, PythonExportContext& ctx, const std::string& parent_name) {
  233. // does not print the array itself to 'out' and returns the whole list
  234. std::stringstream ss;
  235. ss << "[";
  236. for (size_t i = 0; i < variable_rate.size(); i++) {
  237. const auto& item = variable_rate[i];
  238. if (i == 0) {
  239. ss << " ";
  240. }
  241. else if (i % 16 == 0) {
  242. ss << "\n ";
  243. }
  244. ss << "[";
  245. for (const auto& value: item) {
  246. ss << f_to_str(value) << ", ";
  247. }
  248. ss << "], ";
  249. }
  250. ss << "]";
  251. return ss.str();
  252. }
  253. } // namespace API
  254. } // namespace MCell