bngl_generator.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2020 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 <stdexcept>
  12. #include "generator_utils.h"
  13. #include "generator_structs.h"
  14. #include "bngl_generator.h"
  15. #include "data_model_geometry.h"
  16. #include "libmcell/api/api_utils.h"
  17. #include "bng/bng_defines.h"
  18. #include "bng/bngl_names.h"
  19. using namespace std;
  20. namespace MCell {
  21. using Json::Value;
  22. using namespace API;
  23. void BNGLGenerator::generate_units_information_header() {
  24. bool use_bng_units = false;
  25. if (data.mcell.isMember(KEY_USE_BNG_UNITS)) {
  26. use_bng_units = data.mcell[KEY_USE_BNG_UNITS].asBool();
  27. }
  28. bng_out << "\n";
  29. if (use_bng_units) {
  30. bng_out << "# File uses BioNetGen ODE/SSA/PLA units for bimolecular reactions (um^3*N^-1*s^-1).\n";
  31. bng_out << "# When used with MCell, global option Model.config.use_bng_units must be set to True.\n";
  32. bng_out << "# WARNING: NFSim simulation won't produce correct result because NFSim uses different units than BioNetGen ODE.\n";
  33. }
  34. else {
  35. bng_out << "# File uses standard MCell units for bimolecular reactions (M^-1*s^-1 and um^2*N^-1*s^-1).\n";
  36. bng_out << "# When used with MCell, global option Model.config.use_bng_units must be set to False.\n";
  37. bng_out << "# WARNING: Simulation with BioNetGen won't produce correct results because BioNetGen uses different units than MCell.\n";
  38. }
  39. bng_out << "\n";
  40. }
  41. void BNGLGenerator::generate_single_bngl_parameter(Value& parameter) {
  42. if (parameter[KEY_PAR_DESCRIPTION].asString() != "") {
  43. bng_out << IND << "# " << parameter[KEY_PAR_DESCRIPTION].asString() << "\n";
  44. }
  45. bng_out << IND << fix_param_id(parameter[KEY_PAR_NAME].asString()) << " " <<
  46. replace_function_calls_in_expr(parameter[KEY_PAR_EXPRESSION].asString(), false);
  47. string units = parameter[KEY_PAR_UNITS].asString();
  48. if (units != "") {
  49. bng_out << " # units: " << units;
  50. }
  51. bng_out << "\n\n";
  52. }
  53. void BNGLGenerator::generate_single_python_parameter(std::ostream& python_out, Value& parameter) {
  54. string name = fix_param_id(parameter[KEY_PAR_NAME].asString());
  55. // skip MCELL_REDEFINE_ params
  56. if (name.find(BNG::MCELL_REDEFINE_PREFIX) == 0) {
  57. return;
  58. }
  59. data.check_if_already_defined_and_add(name, NAME_PARAMETER);
  60. python_out << name << " = " << VAR_BNGL_PARAMS << "['" << name << "']\n";
  61. }
  62. // NOTE: this belongs rather to the python generator
  63. void BNGLGenerator::generate_parameters(std::ostream& python_out) {
  64. python_out << "# load parameters from BNGL\n";
  65. python_out <<
  66. VAR_BNGL_PARAMS << " = m.bngl_utils." << NAME_LOAD_BNGL_PARAMETERS << "(" <<
  67. get_abs_path(bngl_filename) << ", " <<
  68. S(SHARED) + "." + PARAMETER_OVERRIDES << ")\n\n";
  69. // and generate BNGL parameters and also their Python representations
  70. bng_out << BNG::BEGIN_PARAMETERS << "\n";
  71. Value& parameter_system = get_node(data.mcell, KEY_PARAMETER_SYSTEM);
  72. if (parameter_system.isMember(KEY_MODEL_PARAMETERS)) {
  73. Value& parameter_list = get_node(parameter_system, KEY_MODEL_PARAMETERS);
  74. for (Value::ArrayIndex i = 0; i < parameter_list.size(); i++) {
  75. generate_single_bngl_parameter(parameter_list[i]);
  76. generate_single_python_parameter(python_out, parameter_list[i]);
  77. }
  78. }
  79. bng_out << BNG::END_PARAMETERS << "\n\n";
  80. python_out << "\n";
  81. }
  82. void BNGLGenerator::generate_bngl_mol_type(Json::Value& molecule_list_item) {
  83. string name = make_id(molecule_list_item[KEY_MOL_NAME].asString());
  84. gen_description(bng_out, molecule_list_item, IND);
  85. bng_out << IND << name;
  86. bool has_components = false;
  87. if (molecule_list_item.isMember(KEY_BNGL_COMPONENT_LIST) && molecule_list_item[KEY_BNGL_COMPONENT_LIST].size() > 0) {
  88. has_components = true;
  89. }
  90. if (has_components) {
  91. bng_out << "(";
  92. // Components
  93. Value& bngl_component_list = get_node(molecule_list_item, KEY_BNGL_COMPONENT_LIST);
  94. for (Value::ArrayIndex i = 0; i < bngl_component_list.size(); i++) {
  95. Value& bngl_component = bngl_component_list[i];
  96. bng_out << bngl_component[KEY_CNAME].asString();
  97. Value& cstates = bngl_component[KEY_CSTATES];
  98. for (Value::ArrayIndex i = 0; i < cstates.size(); i++) {
  99. bng_out << "~" << cstates[i].asString();
  100. }
  101. if (i + 1 != bngl_component_list.size()) {
  102. bng_out << ",";
  103. }
  104. }
  105. bng_out << ")";
  106. }
  107. bng_out << "\n";
  108. }
  109. void BNGLGenerator::generate_python_mol_type_info(
  110. std::ostream& python_out, Json::Value& molecule_list_item) {
  111. string name = make_id(molecule_list_item[KEY_MOL_NAME].asString());
  112. python_out << IND4 <<
  113. name << " = subsystem." << NAME_FIND_ELEMENTARY_MOLECULE_TYPE << "('" << name << "')\n";
  114. python_out << IND4 << "assert " << name << ", \"Elementary molecule type '" + name + "' was not found\"\n";
  115. string mol_type = molecule_list_item[KEY_MOL_TYPE].asString();
  116. CHECK_PROPERTY(mol_type == VALUE_MOL_TYPE_2D || mol_type == VALUE_MOL_TYPE_3D);
  117. python_out << IND4;
  118. if (mol_type == VALUE_MOL_TYPE_3D) {
  119. gen_assign(python_out, name, NAME_DIFFUSION_CONSTANT_3D, molecule_list_item[KEY_DIFFUSION_CONSTANT].asString());
  120. }
  121. else {
  122. gen_assign(python_out, name, NAME_DIFFUSION_CONSTANT_2D, molecule_list_item[KEY_DIFFUSION_CONSTANT].asString());
  123. }
  124. bool has_custom_time_step = molecule_list_item[KEY_CUSTOM_TIME_STEP].asString() != "";
  125. bool has_custom_space_step = molecule_list_item[KEY_CUSTOM_SPACE_STEP].asString() != "";
  126. CHECK_PROPERTY(!(has_custom_time_step && has_custom_space_step) && "Only one of custom time or space step may be set");
  127. if (has_custom_time_step) {
  128. python_out << IND4;
  129. gen_assign(python_out, name, NAME_CUSTOM_TIME_STEP, molecule_list_item[KEY_CUSTOM_TIME_STEP].asString());
  130. }
  131. else if (has_custom_space_step) {
  132. python_out << IND4;
  133. gen_assign(python_out, name, NAME_CUSTOM_SPACE_STEP, molecule_list_item[NAME_CUSTOM_SPACE_STEP].asString());
  134. }
  135. if (molecule_list_item[KEY_TARGET_ONLY].asBool()) {
  136. python_out << IND4;
  137. gen_assign(python_out, name, NAME_TARGET_ONLY, true);
  138. }
  139. python_out << "\n";
  140. }
  141. void BNGLGenerator::generate_mol_types(std::ostream& python_out) {
  142. bng_out << BNG::BEGIN_MOLECULE_TYPES << "\n";
  143. python_out <<
  144. "# set additional information about species and molecule types that cannot be stored in BNGL,\n"
  145. "# elementary molecule types are already in the subsystem after they were loaded from BNGL\n"
  146. "def " << SET_BNGL_MOLECULE_TYPES_INFO << "(subsystem):\n";
  147. Value& define_molecules = get_node(data.mcell, KEY_DEFINE_MOLECULES);
  148. check_version(KEY_DEFINE_MOLECULES, define_molecules, VER_DM_2014_10_24_1638);
  149. Value& molecule_list = get_node(define_molecules, KEY_MOLECULE_LIST);
  150. if (molecule_list.empty()) {
  151. python_out << IND4 << "pass # no molecule types are defined\n";
  152. }
  153. for (Value::ArrayIndex i = 0; i < molecule_list.size(); i++) {
  154. Value& molecule_list_item = molecule_list[i];
  155. check_version(KEY_MOLECULE_LIST, molecule_list_item, VER_DM_2018_10_16_1632);
  156. generate_bngl_mol_type(molecule_list_item);
  157. generate_python_mol_type_info(python_out, molecule_list_item);
  158. }
  159. bng_out << BNG::END_MOLECULE_TYPES << "\n\n";
  160. }
  161. Json::Value& BNGLGenerator::find_geometry_object(const std::string& name) {
  162. Value& geometrical_objects = get_node(data.mcell, KEY_GEOMETRICAL_OBJECTS);
  163. if (!geometrical_objects.isMember(KEY_OBJECT_LIST)) {
  164. throw ConversionError("Could not find object for compartment " + name + ".");
  165. }
  166. Value& object_list = get_node(geometrical_objects, KEY_OBJECT_LIST);
  167. for (Value::ArrayIndex i = 0; i < object_list.size(); i++) {
  168. Value& object = object_list[i];
  169. if (object[KEY_NAME].asString() == name) {
  170. return object;
  171. }
  172. }
  173. throw ConversionError("Could not find object for compartment " + name + ".");
  174. }
  175. void BNGLGenerator::get_compartment_volume_and_area(const std::string& name, double& volume, double& area) {
  176. auto it = compartment_name_to_volume_area_cache.find(name);
  177. if (it != compartment_name_to_volume_area_cache.end()) {
  178. volume = it->second.first;
  179. area = it->second.second;
  180. return;
  181. }
  182. string msg;
  183. try {
  184. // find object with name under geometrical_objects/object_list
  185. Json::Value& geometry_object = find_geometry_object(name);
  186. compute_volume_and_area(geometry_object, volume, area);
  187. }
  188. catch (const std::exception& ex) {
  189. cerr << "Warning: could not compute volume of a geometry object: " << ex.what() <<
  190. " Compartment volume won't be correct.\n";
  191. bng_out << "\n" << IND << "# Warning: compartment volume and area is not correct: " << ex.what() << "\n";
  192. volume = 1;
  193. area = 0;
  194. }
  195. // remember in cache even if computation failed
  196. compartment_name_to_volume_area_cache[name] = make_pair(volume, area);
  197. }
  198. void BNGLGenerator::generate_single_compartment(Json::Value& model_object) {
  199. const string& name = model_object[KEY_NAME].asString();
  200. const string& membrane_name = model_object[KEY_MEMBRANE_NAME].asString();
  201. const string& parent_object = model_object[KEY_PARENT_OBJECT].asString();
  202. double volume, area;
  203. get_compartment_volume_and_area(name, volume, area);
  204. // subtract children from volume
  205. auto it = volume_compartment_children.find(name);
  206. if (it != volume_compartment_children.end()) {
  207. for (const string& child: it->second) {
  208. double child_volume, child_area;
  209. get_compartment_volume_and_area(child, child_volume, child_area);
  210. volume -= child_volume;
  211. assert(volume > 0);
  212. }
  213. }
  214. // 2d compartment first
  215. if (membrane_name != "") {
  216. bng_out << IND <<
  217. membrane_name << " " <<
  218. "2" << " " <<
  219. // surface volume is ignored by MCell
  220. area << " * 0.01 " <<
  221. // parent object may be unset
  222. parent_object << " # volume = area * 0.01 um thickness\n";
  223. }
  224. // 3d compartment second
  225. bng_out << IND <<
  226. name << " " <<
  227. "3" << " " <<
  228. // volume is ignored by MCell
  229. volume << " " <<
  230. // parent is the membrane, may be unset
  231. membrane_name << "\n";
  232. }
  233. static void add_parent_compartments_recursively(
  234. SharedGenData& data, Value& model_object_list, Value& model_object,
  235. ParentToChildCompartmentsMap& volume_compartment_children) {
  236. // simply keep inserting until we reach the top compartment
  237. const std::string& vol_comp = model_object[KEY_NAME].asString();
  238. assert(vol_comp != "");
  239. data.used_compartments.insert(vol_comp);
  240. const std::string& surf_comp = model_object[KEY_MEMBRANE_NAME].asString();
  241. if (surf_comp != "") {
  242. data.used_compartments.insert(surf_comp);
  243. }
  244. const std::string& parent_comp = model_object[KEY_PARENT_OBJECT].asString();
  245. if (parent_comp != "") {
  246. // create a mapping so that one can find volume compartment children
  247. volume_compartment_children[parent_comp].insert(vol_comp);
  248. // find corresponding parent
  249. for (Value::ArrayIndex i = 0; i < model_object_list.size(); i++) {
  250. Value& parent_object = model_object_list[i];
  251. if (parent_object[KEY_NAME].asString() == parent_comp) {
  252. add_parent_compartments_recursively(
  253. data, model_object_list, parent_object, volume_compartment_children);
  254. }
  255. }
  256. }
  257. }
  258. void BNGLGenerator::generate_compartments() {
  259. Value& model_objects = get_node(data.mcell, KEY_MODEL_OBJECTS);
  260. check_version(KEY_MODEL_OBJECTS, model_objects, VER_DM_2018_01_11_1330);
  261. Value& model_object_list = get_node(model_objects, KEY_MODEL_OBJECT_LIST);
  262. bool generate_compartments = false;
  263. for (Value::ArrayIndex i = 0; i < model_object_list.size(); i++) {
  264. Value& model_object = model_object_list[i];
  265. // generate the compartments that we need for rxns and recursively their parents
  266. if (data.is_used_compartment(model_object)) {
  267. generate_compartments = true;
  268. // recursively add compartment parents to used compartments, we need to generate them as well
  269. // because their children reference them
  270. // also compute mapping volume parent -> volume children
  271. add_parent_compartments_recursively(
  272. data, model_object_list, model_object, volume_compartment_children);
  273. }
  274. }
  275. // do not generate empty section
  276. if (!generate_compartments) {
  277. return;
  278. }
  279. bng_out << BNG::BEGIN_COMPARTMENTS << "\n";
  280. bng_out <<
  281. IND << "# Note: Compartments are defined for MCell in Python using class GeometryObject,\n" <<
  282. IND << "# MCell ignores the volume/area set here\n";
  283. for (Value::ArrayIndex i = 0; i < model_object_list.size(); i++) {
  284. Value& model_object = model_object_list[i];
  285. // generate only the compartments that we need for rxns
  286. if (data.is_used_compartment(model_object)) {
  287. generate_single_compartment(model_object);
  288. }
  289. }
  290. bng_out << BNG::END_COMPARTMENTS << "\n\n";
  291. }
  292. static void fix_dots_in_simple_substances(vector<string>& substances) {
  293. for (string& s: substances) {
  294. s = fix_dots_in_simple_species(s);
  295. }
  296. }
  297. static bool has_in_out_compartments(const vector<string>& substances) {
  298. bool res = false;
  299. for (const string& s: substances) {
  300. size_t pos_in = s.find(S("@") + BNG::COMPARTMENT_NAME_IN);
  301. size_t pos_out = s.find(S("@") + BNG::COMPARTMENT_NAME_OUT);
  302. if (pos_in != string::npos || pos_out != string::npos) {
  303. return true;
  304. }
  305. }
  306. return false;
  307. }
  308. static void check_that_only_allowed_orientations_are_set(
  309. const vector<string>& orientations, const bool has_in_out_compartments) {
  310. // causes weird behavior on macos
  311. // if IN/OUT is not used, volume molecules must not have orientation and
  312. // surface molecules must be UP
  313. // NOTE: this check can be improed with detection of what type of complex it is surface or volume
  314. for (const string& s: orientations) {
  315. release_assert(
  316. ((s == "" || s == "'") ||
  317. (has_in_out_compartments && (s == "'" || s == ","))) &&
  318. "Orientation in BNGL is not supported, should have been checked before");
  319. }
  320. }
  321. std::string BNGLGenerator::generate_single_reaction_rule(Json::Value& reaction_list_item, const bool generate_name) {
  322. string rxn_type = reaction_list_item[KEY_RXN_TYPE].asString();
  323. CHECK_PROPERTY(rxn_type == VALUE_IRREVERSIBLE || rxn_type == VALUE_REVERSIBLE);
  324. bool is_reversible = rxn_type == VALUE_REVERSIBLE;
  325. string name = get_rxn_id(reaction_list_item, data.unnamed_rxn_counter);
  326. gen_description(bng_out, reaction_list_item, IND);
  327. bng_out << IND;
  328. if (generate_name) {
  329. // printing out name all the time would make the BNGL file hard to read
  330. bng_out << name << ": ";
  331. }
  332. vector<string> reac_substances;
  333. vector<string> reac_orientations;
  334. // reactants
  335. parse_rxn_rule_side(reaction_list_item[KEY_REACTANTS], reac_substances, reac_orientations);
  336. fix_dots_in_simple_substances(reac_substances);
  337. bool has_in_out = has_in_out_compartments(reac_substances); // in/out must be used in reactants to be allowed
  338. check_that_only_allowed_orientations_are_set(reac_orientations, has_in_out);
  339. for (size_t i = 0; i < reac_substances.size(); i++) {
  340. bng_out << reac_substances[i];
  341. if (i != reac_substances.size() - 1) {
  342. bng_out << " + ";
  343. }
  344. }
  345. bng_out << " ";
  346. bng_out << ((is_reversible) ? "<->" : "->") << " ";
  347. vector<string> prod_substances;
  348. vector<string> prod_orientations;
  349. // products
  350. parse_rxn_rule_side(reaction_list_item[KEY_PRODUCTS], prod_substances, prod_orientations);
  351. fix_dots_in_simple_substances(prod_substances);
  352. check_that_only_allowed_orientations_are_set(prod_orientations, has_in_out);
  353. for (size_t i = 0; i < prod_substances.size(); i++) {
  354. if (prod_substances[i] == VALUE_NULL) {
  355. // BNGL does not allow "NULL"
  356. bng_out << "0";
  357. }
  358. else {
  359. bng_out << prod_substances[i];
  360. }
  361. if (i != prod_substances.size() - 1) {
  362. bng_out << " + ";
  363. }
  364. }
  365. bng_out << " ";
  366. // rates
  367. bng_out << reaction_list_item[KEY_FWD_RATE].asString();
  368. if (is_reversible) {
  369. bng_out << ", " << reaction_list_item[KEY_BKWD_RATE].asString();
  370. }
  371. bng_out << "\n";
  372. return name;
  373. }
  374. // rather limited for now
  375. bool BNGLGenerator::can_express_count_with_bngl(
  376. const bool single_term,
  377. const bool rxn_not_mol,
  378. const std::string& where_to_count,
  379. const std::string& orientation,
  380. const std::string& mul_div_str,
  381. const std::string& rxn_step
  382. ) const {
  383. if (!single_term) {
  384. return false;
  385. }
  386. if (rxn_not_mol) {
  387. return false;
  388. }
  389. if (where_to_count != "" && where_to_count != VALUE_COUNT_LOCATION_WORLD) {
  390. return false;
  391. }
  392. if (orientation != "") {
  393. return false;
  394. }
  395. if (mul_div_str != "") {
  396. return false;
  397. }
  398. if (rxn_step != "") {
  399. // TODO: add test that uses parameters for both examined values
  400. double rxn_step_val;
  401. bool ok = get_parameter_value(data.mcell, rxn_step, rxn_step_val);
  402. if (!ok) {
  403. return false;
  404. }
  405. double time_step_val;
  406. const string& time_step = data.mcell[KEY_INITIALIZATION][KEY_TIME_STEP].asString();
  407. ok = get_parameter_value(data.mcell, time_step, time_step_val);
  408. if (!ok) {
  409. return false;
  410. }
  411. // time step and rxn step must be the same
  412. if (!cmp_eq(rxn_step_val, time_step_val)) {
  413. return false;
  414. }
  415. }
  416. return true;
  417. }
  418. void BNGLGenerator::generate_single_count(
  419. const std::string& observable_name,
  420. const std::string& what_to_count,
  421. const std::string& description,
  422. const bool molecules_not_species) {
  423. gen_description(bng_out, description, IND);
  424. bng_out << IND;
  425. // there is some issue with macos Apple LLVM version 10.0.0 (clang-1000.10.44.4)
  426. // this gets miscompiled with optimizations enabled: ((molecules_not_species) ? BNG::OBSERVABLE_MOLECULES : BNG::OBSERVABLE_SPECIES)
  427. if (molecules_not_species) {
  428. bng_out << BNG::OBSERVABLE_MOLECULES;
  429. }
  430. else {
  431. bng_out << BNG::OBSERVABLE_SPECIES;
  432. }
  433. bng_out << " " <<
  434. fix_id(observable_name) << " " <<
  435. what_to_count << "\n";
  436. }
  437. bool BNGLGenerator::can_express_release_with_bngl(Json::Value& release_site_item) {
  438. // for now only volume molecules, little limited,
  439. // mainly for data model generated by MCell4 reading a BNGL file
  440. // must be released into an object
  441. if (release_site_item[KEY_SHAPE].asString() != VALUE_OBJECT) {
  442. return false;
  443. }
  444. // must have a single compartment (probably too strict)
  445. bool multiple_compartments;
  446. string compartment = get_single_compartment(release_site_item[KEY_MOLECULE].asString(), &multiple_compartments);
  447. if (multiple_compartments) {
  448. return false;
  449. }
  450. // compartment must correspond to the object, or it must be empty and the object must the special default_comartment
  451. if (compartment == "" && release_site_item[KEY_OBJECT_EXPR].asString() != S(BNG::DEFAULT_COMPARTMENT_NAME) + "[ALL]") {
  452. return false;
  453. }
  454. if (compartment != "") {
  455. if (data.surface_to_volume_compartments_map.count(compartment) == 0) {
  456. if (release_site_item[KEY_OBJECT_EXPR].asString() != compartment + "[ALL]" &&
  457. release_site_item[KEY_OBJECT_EXPR].asString() != compartment) {
  458. return false;
  459. }
  460. }
  461. else {
  462. // get volume compartment name if this is a surface compartment because that is what appears in the data model
  463. string vol_compartment = data.surface_to_volume_compartments_map[compartment];
  464. if (release_site_item[KEY_OBJECT_EXPR].asString() != vol_compartment + "[ALL]" &&
  465. release_site_item[KEY_OBJECT_EXPR].asString() != vol_compartment) {
  466. return false;
  467. }
  468. }
  469. }
  470. if (release_site_item[KEY_QUANTITY_TYPE].asString() != VALUE_NUMBER_TO_RELEASE) {
  471. return false;
  472. }
  473. if (release_site_item[KEY_RELEASE_PROBABILITY].asString() != "1") {
  474. return false;
  475. }
  476. if (release_site_item[KEY_STDDEV].asString() != "0") {
  477. return false;
  478. }
  479. if (release_site_item[KEY_ORIENT].asString() != ";" &&
  480. release_site_item[KEY_ORIENT].asString() != "'" ) {
  481. return false;
  482. }
  483. if (release_site_item[KEY_PATTERN].asString() != "") {
  484. return false;
  485. }
  486. return true;
  487. }
  488. void BNGLGenerator::generate_single_release_site(
  489. const std::string& bngl_cplx,
  490. const std::string& quantity,
  491. const std::string& description) {
  492. gen_description(bng_out, description, IND);
  493. bng_out << IND << bngl_cplx << " " << fix_param_id(quantity) << "\n";
  494. }
  495. } /* namespace MCell */