python_generator.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  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 <fstream>
  12. #include <regex>
  13. #include <ctype.h>
  14. #include "libmcell/api/api_utils.h"
  15. #include "python_generator.h"
  16. #include "generator_utils.h"
  17. #include "mcell4_generator.h"
  18. #include "bng/bng_defines.h"
  19. using namespace std;
  20. using namespace MCell::API;
  21. namespace MCell {
  22. using Json::Value;
  23. void PythonGenerator::generate_single_parameter(std::ostream& out, Json::Value& parameter) {
  24. if (parameter[KEY_PAR_DESCRIPTION].asString() != "") {
  25. out << "# " << parameter[KEY_PAR_DESCRIPTION].asString() << "\n";
  26. }
  27. string python_expr;
  28. // replace operator ^ with operator **
  29. python_expr = regex_replace(parameter[KEY_PAR_EXPRESSION].asString(), regex("\\^"), "**");
  30. python_expr = replace_function_calls_in_expr(python_expr, true);
  31. string name = fix_param_id(parameter[KEY_PAR_NAME].asString());
  32. data.check_if_already_defined_and_add(name, NAME_PARAMETER);
  33. string ind;
  34. if (!data.not_overridable_python_params) {
  35. // allow params to be overridden
  36. out << "if " << NOT_DEFINED << "('" << name << "'):\n";
  37. ind = IND4;
  38. }
  39. out << ind << name << " = " << python_expr;
  40. string units = parameter[KEY_PAR_UNITS].asString();
  41. if (units != "") {
  42. out << " # units: " << units;
  43. }
  44. out << "\n\n";
  45. }
  46. static void get_used_ids(const string& expr, vector<string>& used_ids) {
  47. used_ids.clear();
  48. // same automaton but with different actions is used in replace_function_calls_in_expr
  49. enum state_t {
  50. START,
  51. IN_ID,
  52. IN_NUM
  53. };
  54. state_t state = START;
  55. string curr_id;
  56. for (size_t i = 0; i < expr.size(); i++) {
  57. char c = expr[i];
  58. switch (state) {
  59. case START:
  60. if (isalpha(c) || c == '_') {
  61. state = IN_ID;
  62. curr_id += c;
  63. }
  64. else if (isdigit(c)) {
  65. state = IN_NUM;
  66. }
  67. break;
  68. case IN_ID:
  69. if (isalnum(c) || c == '_') {
  70. curr_id += c;
  71. }
  72. else {
  73. if (mdl_functions_to_py_bngl_map.count(curr_id) == 0) {
  74. // count only IDs
  75. used_ids.push_back(curr_id);
  76. }
  77. curr_id = "";
  78. state = START;
  79. }
  80. break;
  81. case IN_NUM:
  82. if (isdigit(c) || c == '.' || c == 'e' || c == '+' || c == '-') {
  83. // ok
  84. }
  85. else {
  86. if (isalpha(c) || c == '_') {
  87. state = IN_ID;
  88. curr_id += c;
  89. }
  90. else {
  91. state = START;
  92. }
  93. }
  94. break;
  95. }
  96. }
  97. if (state == IN_ID) {
  98. if (mdl_functions_to_py_bngl_map.count(curr_id) == 0) {
  99. // count only IDs
  100. used_ids.push_back(curr_id);
  101. }
  102. }
  103. }
  104. struct ExprDepNode {
  105. size_t index;
  106. vector<ExprDepNode*> parents;
  107. vector<ExprDepNode*> children;
  108. };
  109. // simple class used mainly to delete all created nodes
  110. class ExprDepNodeContainer {
  111. public:
  112. ~ExprDepNodeContainer() {
  113. for (ExprDepNode* n: nodes) {
  114. delete n;
  115. }
  116. nodes.clear();
  117. }
  118. ExprDepNode* create_node(const size_t index) {
  119. ExprDepNode* res = new ExprDepNode();
  120. nodes.push_back(res);
  121. res->index = index;
  122. return res;
  123. }
  124. vector<ExprDepNode*> nodes;
  125. };
  126. // collects
  127. static void traverse_given_level(
  128. const ExprDepNode* n, const uint level, vector<size_t>& level_ordering) {
  129. if (level == 0) {
  130. level_ordering.push_back(n->index);
  131. }
  132. else {
  133. // recursion stops if there are no children
  134. for (ExprDepNode* child: n->children) {
  135. traverse_given_level(child, level-1, level_ordering);
  136. }
  137. }
  138. }
  139. static void level_order_traversal(
  140. ExprDepNode* root, vector<size_t>& ordering) {
  141. vector<size_t> level_ordering;
  142. uint level = 1; // we are not counting the root node
  143. do {
  144. level_ordering.clear();
  145. traverse_given_level(root, level, level_ordering);
  146. ordering.insert(ordering.end(), level_ordering.begin(), level_ordering.end());
  147. level++;
  148. } while (!level_ordering.empty());
  149. }
  150. // not very efficient but the parameter systems in data models are not so huge
  151. static void order_by_dependencies(const vector<ExprDepNode*>& nodes, vector<size_t>& ordering) {
  152. vector<bool> already_handled(nodes.size(), false);
  153. while (ordering.size() != nodes.size()) {
  154. for (size_t i = 0; i < nodes.size(); i++) {
  155. ExprDepNode* n = nodes[i];
  156. if (already_handled[n->index]) {
  157. continue;
  158. }
  159. bool all_parents_are_handled = true;
  160. for (auto& p: n->parents) {
  161. if (!already_handled[p->index]) {
  162. // skip, not ready for this parameter yet
  163. all_parents_are_handled = false;
  164. break;
  165. }
  166. }
  167. if (!all_parents_are_handled) {
  168. continue;
  169. }
  170. ordering.push_back(n->index);
  171. already_handled[n->index] = true;
  172. }
  173. }
  174. }
  175. static void define_parameter_ordering(Value& parameter_list, vector<size_t>& ordering) {
  176. ExprDepNodeContainer nodes;
  177. map<string, ExprDepNode*> defines;
  178. // first create all nodes
  179. for (Value::ArrayIndex i = 0; i < parameter_list.size(); i++) {
  180. const string& name = parameter_list[i][KEY_PAR_NAME].asString();
  181. defines[name] = nodes.create_node(i);
  182. }
  183. // then define dependencies
  184. for (Value::ArrayIndex i = 0; i < parameter_list.size(); i++) {
  185. const string& name = parameter_list[i][KEY_PAR_NAME].asString();
  186. const string& expr = parameter_list[i][KEY_PAR_EXPRESSION].asString();
  187. ExprDepNode* n = defines[name];
  188. vector<string> used_ids;
  189. // e.g. our parameter is a = b + c
  190. // parents of a are b and c (since
  191. // and child of b, resp. c, is a
  192. get_used_ids(expr, used_ids);
  193. for (const string& id: used_ids) {
  194. ExprDepNode* used_node = defines[id];
  195. // check function name
  196. if (used_node == nullptr) {
  197. assert(mdl_functions_to_py_bngl_map.count(id) == 0);
  198. ERROR("Did not find definition of identifier '" + id + "' when converting parameters, some MDL "
  199. "constants or functions are not supported."
  200. );
  201. }
  202. // make a bidirectional link
  203. n->parents.push_back(used_node);
  204. used_node->children.push_back(n);
  205. }
  206. }
  207. // now we might have multiple roots (nodes without uses), create just one
  208. /*ExprDepNode* root = nodes.create_node(INDEX_INVALID);
  209. for (auto node_it: defines) {
  210. if (node_it.second->parents.empty()) {
  211. root->children.push_back(node_it.second);
  212. node_it.second->parents.push_back(root);
  213. }
  214. }*/
  215. // order nodes by dependencies
  216. order_by_dependencies(nodes.nodes, ordering);
  217. // finally do a level-order traversal of the created dependency graph
  218. //level_order_traversal(root, ordering);
  219. }
  220. void PythonGenerator::generate_parameters(std::ostream& out) {
  221. Value& parameter_system = get_node(mcell, KEY_PARAMETER_SYSTEM);
  222. if (parameter_system.isMember(KEY_MODEL_PARAMETERS)) {
  223. Value& parameter_list = get_node(parameter_system, KEY_MODEL_PARAMETERS);
  224. vector<size_t> ordering;
  225. // sort parameters by their dependence
  226. define_parameter_ordering(parameter_list, ordering);
  227. // define_parameter_ordering can insert several parameters multiple times into ordering
  228. // TODO: make a better fix if needed, skipping duplicates for now
  229. set<size_t> already_generated;
  230. for (Value::ArrayIndex i: ordering) {
  231. if (already_generated.count(i) == 0) {
  232. generate_single_parameter(out, parameter_list[i]);
  233. }
  234. already_generated.insert(i);
  235. }
  236. }
  237. out << "\n";
  238. }
  239. std::string PythonGenerator::generate_component_type(
  240. std::ostream& out, Json::Value& bngl_component_item, const std::string& mol_type_name) {
  241. const string& cname = bngl_component_item[KEY_CNAME].asString();
  242. bool has_states =
  243. bngl_component_item.isMember(KEY_CSTATES) &&
  244. !bngl_component_item[KEY_CSTATES].empty();
  245. string name = COMPONENT_PREFIX + mol_type_name + '_' + make_id(cname);
  246. // generate only once
  247. // TODO: do we need to check here that all the declarations are the same?
  248. if (data.is_already_defined(name, NAME_CLASS_COMPONENT_TYPE)) {
  249. return name;
  250. }
  251. data.check_if_already_defined_and_add(name, NAME_CLASS_COMPONENT_TYPE);
  252. gen_ctor_call(out, name, NAME_CLASS_COMPONENT_TYPE);
  253. gen_param(out, NAME_NAME, cname, has_states);
  254. vector<string> state_names;
  255. if (has_states) {
  256. Value& cstates = bngl_component_item[KEY_CSTATES];
  257. for (Value::ArrayIndex i = 0; i < cstates.size(); i++) {
  258. state_names.push_back(cstates[i].asString());
  259. }
  260. gen_param_list(out, NAME_STATES, state_names, false, true);
  261. }
  262. out << CTOR_END;
  263. return name;
  264. }
  265. std::string PythonGenerator::generate_single_species_or_mol_type(
  266. std::ostream& out, Json::Value& molecule_list_item,
  267. const bool generate_species, const std::vector<string>& component_names) {
  268. assert(generate_species == component_names.empty());
  269. string orig_name = molecule_list_item[KEY_MOL_NAME].asString();
  270. string name = make_id(orig_name);
  271. const char* type = (generate_species) ? NAME_CLASS_SPECIES : NAME_CLASS_ELEMENTARY_MOLECULE_TYPE;
  272. data.check_if_already_defined_and_add(name, type);
  273. gen_description(out, molecule_list_item);
  274. string python_name = (generate_species) ? name : ELEMENTARY_MOLECULE_TYPE_PREFIX + name;
  275. gen_ctor_call(out, python_name, type);
  276. string name_to_generate = orig_name;
  277. if (API::is_simple_species(orig_name)) {
  278. // simple species must not contain dots in their name
  279. name_to_generate = name;
  280. }
  281. gen_param(out, NAME_NAME, name_to_generate, true); // using original name
  282. if (!generate_species) {
  283. gen_param_list(out, NAME_COMPONENTS, component_names, true);
  284. }
  285. bool has_target_only = molecule_list_item[KEY_TARGET_ONLY].asBool();
  286. bool has_custom_time_step = molecule_list_item[KEY_CUSTOM_TIME_STEP].asString() != "";
  287. bool has_custom_space_step = molecule_list_item[KEY_CUSTOM_SPACE_STEP].asString() != "";
  288. bool has_extra_args = has_target_only || has_custom_time_step || has_custom_space_step;
  289. string mol_type = molecule_list_item[KEY_MOL_TYPE].asString();
  290. CHECK_PROPERTY(mol_type == VALUE_MOL_TYPE_2D || mol_type == VALUE_MOL_TYPE_3D);
  291. if (mol_type == VALUE_MOL_TYPE_3D) {
  292. gen_param_expr(out, NAME_DIFFUSION_CONSTANT_3D, molecule_list_item[KEY_DIFFUSION_CONSTANT], has_extra_args);
  293. }
  294. else {
  295. gen_param_expr(out, NAME_DIFFUSION_CONSTANT_2D, molecule_list_item[KEY_DIFFUSION_CONSTANT], has_extra_args);
  296. }
  297. CHECK_PROPERTY(!(has_custom_time_step && has_custom_space_step) && "Only one of custom time or space step may be set");
  298. if (has_custom_time_step) {
  299. gen_param_expr(out, NAME_CUSTOM_TIME_STEP, molecule_list_item[KEY_CUSTOM_TIME_STEP], has_target_only);
  300. }
  301. else if (has_custom_space_step) {
  302. gen_param_expr(out, NAME_CUSTOM_SPACE_STEP, molecule_list_item[KEY_CUSTOM_SPACE_STEP], has_target_only);
  303. }
  304. if (has_target_only) {
  305. gen_param(out, NAME_TARGET_ONLY, true, false);
  306. }
  307. out << CTOR_END;
  308. return python_name;
  309. }
  310. SpeciesOrMolType PythonGenerator::generate_single_species_or_mol_type_w_components(
  311. std::ostream& out, Json::Value& molecule_list_item) {
  312. bool has_components = false;
  313. if (molecule_list_item.isMember(KEY_BNGL_COMPONENT_LIST) && molecule_list_item[KEY_BNGL_COMPONENT_LIST].size() > 0) {
  314. has_components = true;
  315. }
  316. // molecules in CellBlender represent either Species if they are a simple ComplexInstance or ElementaryMoleculeTypes
  317. // two different generation modes - when components are present then
  318. // we generate ElementaryMoleculeType, Species are a specific instantiation,
  319. // otherwise Species without components can be defined directly in the MCell3 species style
  320. if (has_components) {
  321. vector<string> component_names;
  322. string component_prefix = make_id(molecule_list_item[KEY_MOL_NAME].asString());
  323. // Components
  324. Value& bngl_component_list = get_node(molecule_list_item, KEY_BNGL_COMPONENT_LIST);
  325. for (Value::ArrayIndex i = 0; i < bngl_component_list.size(); i++) {
  326. string component_name = generate_component_type(out, bngl_component_list[i], component_prefix);
  327. component_names.push_back(component_name);
  328. }
  329. // Molecule Type
  330. string name = generate_single_species_or_mol_type(out, molecule_list_item, false, component_names);
  331. return SpeciesOrMolType(name, false);
  332. }
  333. else {
  334. string name = generate_single_species_or_mol_type(out, molecule_list_item, true);
  335. return SpeciesOrMolType(name, true);
  336. }
  337. }
  338. void PythonGenerator::generate_species_and_mol_types(
  339. std::ostream& out, std::vector<SpeciesOrMolType>& species_and_mt_info) {
  340. Value& define_molecules = get_node(mcell, KEY_DEFINE_MOLECULES);
  341. check_version(KEY_DEFINE_MOLECULES, define_molecules, VER_DM_2014_10_24_1638);
  342. Value& molecule_list = get_node(define_molecules, KEY_MOLECULE_LIST);
  343. for (Value::ArrayIndex i = 0; i < molecule_list.size(); i++) {
  344. Value& molecule_list_item = molecule_list[i];
  345. check_version(KEY_MOLECULE_LIST, molecule_list_item, VER_DM_2018_10_16_1632);
  346. SpeciesOrMolType info = generate_single_species_or_mol_type_w_components(out, molecule_list_item);
  347. species_and_mt_info.push_back(info);
  348. }
  349. }
  350. void PythonGenerator::get_surface_class_property_info(
  351. const string& sc_name,
  352. Value& property,
  353. string& name, string& type_name,
  354. string& affected_mols, string& orientation, string& clamp_concentration
  355. ) {
  356. check_version(KEY_SURFACE_CLASS_PROP_LIST, property, VER_DM_2015_11_08_1756);
  357. affected_mols = property[KEY_AFFECTED_MOLS].asString();
  358. if (affected_mols == BNG::ALL_MOLECULES) {
  359. affected_mols = S(MDOT) + NAME_CV_AllMolecules;
  360. }
  361. else if (affected_mols == BNG::ALL_VOLUME_MOLECULES) {
  362. affected_mols = S(MDOT) + NAME_CV_AllVolumeMolecules;
  363. }
  364. else if (affected_mols == BNG::ALL_SURFACE_MOLECULES) {
  365. affected_mols = S(MDOT) + NAME_CV_AllSurfaceMolecules;
  366. }
  367. else if (affected_mols == VALUE_SINGLE) {
  368. affected_mols = property[KEY_MOLECULE].asString();
  369. }
  370. orientation = convert_orientation(property[KEY_SURF_CLASS_ORIENT].asString());
  371. clamp_concentration = "";
  372. string surf_class_type = property[KEY_SURF_CLASS_TYPE].asString();
  373. if (surf_class_type == VALUE_TRANSPARENT) {
  374. type_name = NAME_EV_TRANSPARENT;
  375. }
  376. else if (surf_class_type == VALUE_REFLECTIVE) {
  377. type_name = NAME_EV_REFLECTIVE;
  378. }
  379. else if (surf_class_type == VALUE_ABSORPTIVE) {
  380. type_name = NAME_EV_ABSORPTIVE;
  381. }
  382. else if (surf_class_type == VALUE_CLAMP_CONCENTRATION) {
  383. type_name = NAME_EV_CONCENTRATION_CLAMP;
  384. clamp_concentration = property[KEY_CLAMP_VALUE].asString();
  385. }
  386. else if (surf_class_type == VALUE_CLAMP_FLUX) {
  387. type_name = NAME_EV_FLUX_CLAMP;
  388. clamp_concentration = property[KEY_CLAMP_VALUE].asString();
  389. }
  390. else if (surf_class_type == "") {
  391. type_name = NAME_EV_REACTIVE;
  392. }
  393. else {
  394. ERROR(S("Invalid type/property ") + KEY_SURF_CLASS_TYPE + " " + surf_class_type +
  395. " of surface class '" + sc_name + "0'.");
  396. }
  397. name = make_id(property[KEY_NAME].asString());
  398. if (name == "") {
  399. string tn_lower = type_name;
  400. transform(tn_lower.begin(), tn_lower.end(), tn_lower.begin(), ::tolower);
  401. // to be sure that there is no conflict, let's put a number at the end
  402. name = SURFACE_CLASS_PREFIX + tn_lower + "_" + affected_mols + to_string(unnamed_surf_class_counter);
  403. unnamed_surf_class_counter++;
  404. }
  405. }
  406. void PythonGenerator::generate_surface_classes(
  407. std::ostream& out, std::vector<std::string>& sc_names) {
  408. if (!mcell.isMember(KEY_DEFINE_SURFACE_CLASSES)) {
  409. return;
  410. }
  411. Value& define_surface_classes = get_node(mcell, KEY_DEFINE_SURFACE_CLASSES);
  412. check_version(KEY_DEFINE_SURFACE_CLASSES, define_surface_classes, VER_DM_2014_10_24_1638);
  413. Value& surface_class_list = get_node(define_surface_classes, KEY_SURFACE_CLASS_LIST);
  414. for (Value::ArrayIndex i = 0; i < surface_class_list.size(); i++) {
  415. Value& surface_class_list_item = surface_class_list[i];
  416. Value& surface_class_prop_list = get_node(surface_class_list_item, KEY_SURFACE_CLASS_PROP_LIST);
  417. string sc_name = make_id(surface_class_list_item[KEY_NAME].asString());
  418. sc_names.push_back(sc_name);
  419. vector<string> sc_prop_names;
  420. if (surface_class_prop_list.size() > 1) {
  421. for (Value::ArrayIndex i = 0; i < surface_class_prop_list.size(); i++) {
  422. Value& surface_class_prop_item = surface_class_prop_list[i];
  423. string name, type_name, affected_mols, orientation_name, clamp_concentration;
  424. get_surface_class_property_info(
  425. sc_name, surface_class_prop_item,
  426. name, type_name, affected_mols, orientation_name, clamp_concentration);
  427. bool is_clamp = type_name == NAME_EV_CONCENTRATION_CLAMP || type_name == NAME_EV_FLUX_CLAMP;
  428. bool has_affected_mols = affected_mols != "";
  429. sc_prop_names.push_back(name);
  430. data.check_if_already_defined_and_add(name, NAME_CLASS_SURFACE_PROPERTY);
  431. gen_ctor_call(out, name, NAME_CLASS_SURFACE_PROPERTY, true);
  432. gen_param_enum(out, NAME_TYPE, NAME_ENUM_SURFACE_PROPERTY_TYPE, type_name, has_affected_mols || is_clamp);
  433. if (has_affected_mols) {
  434. gen_param_expr(
  435. out, NAME_AFFECTED_COMPLEX_PATTERN,
  436. make_species_or_cplx(data, affected_mols, orientation_name), is_clamp);
  437. }
  438. if (is_clamp) {
  439. gen_param_expr(out, NAME_CONCENTRATION, clamp_concentration, false);
  440. }
  441. out << CTOR_END;
  442. }
  443. }
  444. gen_description(out, surface_class_list_item);
  445. data.check_if_already_defined_and_add(sc_name, NAME_CLASS_SURFACE_CLASS);
  446. gen_ctor_call(out, sc_name, NAME_CLASS_SURFACE_CLASS, true);
  447. gen_param(out, NAME_NAME, sc_name, true);
  448. if (!sc_prop_names.empty()) {
  449. // use a list of properties
  450. gen_param_list(out, NAME_PROPERTIES, sc_prop_names, false);
  451. }
  452. else {
  453. // simplified setup, directly set members
  454. string name, type_name, affected_mols, orientation_name, clamp_concentration;
  455. get_surface_class_property_info(
  456. sc_name, surface_class_prop_list[0],
  457. name, type_name, affected_mols, orientation_name, clamp_concentration);
  458. bool is_clamp = type_name == NAME_EV_CONCENTRATION_CLAMP || type_name == NAME_EV_FLUX_CLAMP;
  459. bool has_affected_mols = affected_mols != "";
  460. gen_param_enum(out, NAME_TYPE, NAME_ENUM_SURFACE_PROPERTY_TYPE, type_name, has_affected_mols || is_clamp);
  461. if (has_affected_mols) {
  462. gen_param_expr(
  463. out, NAME_AFFECTED_COMPLEX_PATTERN,
  464. make_species_or_cplx(data, affected_mols, orientation_name), is_clamp);
  465. }
  466. if (is_clamp) {
  467. gen_param_expr(out, NAME_CONCENTRATION, clamp_concentration, false);
  468. }
  469. }
  470. out << CTOR_END;
  471. }
  472. }
  473. void PythonGenerator::generate_variable_rate(const std::string& rate_array_name, Json::Value& variable_rate_text) {
  474. // append to the parameters file
  475. ofstream out;
  476. open_and_check_file_w_prefix(data.output_files_prefix, PARAMETERS, out, true);
  477. out << "\n" << make_section_comment("variable rate");
  478. out << rate_array_name << " = [\n";
  479. string vr = variable_rate_text.asString();
  480. size_t pos = 0;
  481. bool print_comma = false;
  482. while (pos < vr.size()) {
  483. size_t tab = vr.find('\t', pos + 1);
  484. size_t space = vr.find(' ', pos + 1);
  485. if (tab == string::npos || space < tab) {
  486. tab = space;
  487. }
  488. size_t nl = vr.find('\n', pos + 1);
  489. if (nl == string::npos) {
  490. nl = vr.size();
  491. }
  492. if (tab > nl || tab == pos || nl == pos) {
  493. ERROR("Malformed variable_rate_text in datamodel.");
  494. }
  495. if (tab == string::npos) {
  496. break; // end of input
  497. }
  498. string time = vr.substr(pos, tab - pos);
  499. string rate = vr.substr(tab + 1, nl - (tab + 1));
  500. time = trim(time);
  501. rate = trim(rate);
  502. if (print_comma) {
  503. out << ",\n";
  504. }
  505. print_comma = true;
  506. out << " [" << time << ", " << rate << "]";
  507. pos = nl;
  508. pos++;
  509. }
  510. out << "\n] # " << rate_array_name << "\n\n";
  511. out.close();
  512. }
  513. void PythonGenerator::generate_rxn_rule_side(std::ostream& out, Json::Value& substances_node) {
  514. string str = substances_node.asString();
  515. // special case for rxns without products
  516. if (str == NULL_PRODUCTS) {
  517. out << "[ ]";
  518. return;
  519. }
  520. vector<string> substances;
  521. vector<string> orientations;
  522. parse_rxn_rule_side(substances_node, substances, orientations);
  523. out << "[ ";
  524. for (size_t i = 0; i < substances.size(); i++) {
  525. string orient = convert_orientation(orientations[i], true);
  526. string compartment = get_single_compartment(substances[i]);
  527. out << make_species_or_cplx(data, substances[i], orient, compartment);
  528. print_comma(out, i, substances);
  529. }
  530. out << " ]";
  531. }
  532. std::string PythonGenerator::generate_single_reaction_rule(std::ostream& out, Json::Value& reaction_list_item) {
  533. check_version(KEY_MOLECULE_LIST, reaction_list_item, VER_DM_2018_01_11_1330);
  534. string name = get_rxn_id(reaction_list_item, data.unnamed_rxn_counter);
  535. gen_description(out, reaction_list_item);
  536. data.check_if_already_defined_and_add(name, NAME_CLASS_REACTION_RULE);
  537. gen_ctor_call(out, name, NAME_CLASS_REACTION_RULE);
  538. gen_param(out, NAME_NAME, name, true);
  539. // single line for now
  540. out << IND << NAME_REACTANTS << " = ";
  541. generate_rxn_rule_side(out, reaction_list_item[KEY_REACTANTS]);
  542. out << ",\n";
  543. out << IND << NAME_PRODUCTS << " = ";
  544. generate_rxn_rule_side(out, reaction_list_item[KEY_PRODUCTS]);
  545. out << ",\n";
  546. if (reaction_list_item[KEY_VARIABLE_RATE_SWITCH].asBool()) {
  547. // variable rates
  548. CHECK_PROPERTY(reaction_list_item[KEY_VARIABLE_RATE_VALID].asBool() && "variable_rate_switch must be equal to variable_rate_valid");
  549. string rate_array_name = reaction_list_item[KEY_VARIABLE_RATE].asString();
  550. size_t dot = rate_array_name.rfind('.');
  551. if (dot != string::npos) {
  552. // remove file extension
  553. rate_array_name = rate_array_name.substr(0, dot);
  554. }
  555. // and remove possibly other dots in the name
  556. rate_array_name = make_id(rate_array_name);
  557. generate_variable_rate(rate_array_name, reaction_list_item[KEY_VARIABLE_RATE_TEXT]);
  558. gen_param_id(out, NAME_VARIABLE_RATE, rate_array_name, false); // module parameters is imported as *
  559. }
  560. else {
  561. // fwd or rev rates
  562. string rxn_type = reaction_list_item[KEY_RXN_TYPE].asString();
  563. CHECK_PROPERTY(rxn_type == VALUE_IRREVERSIBLE || rxn_type == VALUE_REVERSIBLE);
  564. bool is_reversible = rxn_type == VALUE_REVERSIBLE;
  565. gen_param_expr(out, NAME_FWD_RATE, reaction_list_item[KEY_FWD_RATE], is_reversible);
  566. if (is_reversible) {
  567. gen_param(out, NAME_REV_NAME, name + REV_RXN_SUFFIX, true);
  568. gen_param_expr(out, NAME_REV_RATE, reaction_list_item[KEY_BKWD_RATE], false);
  569. }
  570. }
  571. out << CTOR_END;
  572. return name;
  573. }
  574. void PythonGenerator::generate_reaction_rules(
  575. ostream& out, std::vector<IdLoc>& rxn_names) {
  576. Value& define_reactions = get_node(mcell, KEY_DEFINE_REACTIONS);
  577. check_version(KEY_DEFINE_REACTIONS, define_reactions, VER_DM_2014_10_24_1638);
  578. Value& reaction_list = get_node(define_reactions, KEY_REACTION_LIST);
  579. for (Value::ArrayIndex i = 0; i < reaction_list.size(); i++) {
  580. Value& reaction_list_item = reaction_list[i];
  581. string name = generate_single_reaction_rule(out, reaction_list_item);
  582. rxn_names.push_back(IdLoc(name, true));
  583. }
  584. }
  585. string PythonGenerator::generate_single_geometry_object(
  586. ostream& out, const int index, Value& object) {
  587. string parent_name = S(KEY_OBJECT_LIST) + "[" + to_string(index) + "]";
  588. string name = make_id(get_node(parent_name, object, KEY_NAME).asString());
  589. data.check_if_already_defined_and_add(name, NAME_CLASS_GEOMETRY_OBJECT);
  590. Value& vertex_list = get_node(parent_name, object, KEY_VERTEX_LIST);
  591. Value& element_connections = get_node(parent_name, object, KEY_ELEMENT_CONNECTIONS);
  592. // TODO: material_names
  593. out << make_start_block_comment(name);
  594. // vertex_list
  595. string id_vertex_list = name + "_" + NAME_VERTEX_LIST;
  596. out << id_vertex_list << " = [\n";
  597. for (Value::ArrayIndex i = 0; i < vertex_list.size(); i++) {
  598. out << IND << "[";
  599. Value& vertex = vertex_list[i];
  600. for (Value::ArrayIndex k = 0; k < vertex.size(); k++) {
  601. out << vertex[k].asDouble();
  602. gen_comma(out, k, vertex);
  603. }
  604. out << "]";
  605. gen_comma(out, i, vertex_list);
  606. out << "\n";
  607. }
  608. out << "] # " << id_vertex_list << "\n\n";
  609. // element_connections
  610. string id_element_connections = name + "_" + NAME_WALL_LIST;
  611. out << id_element_connections << " = [\n";
  612. for (Value::ArrayIndex i = 0; i < element_connections.size(); i++) {
  613. out << IND << "[";
  614. Value& element = element_connections[i];
  615. for (Value::ArrayIndex k = 0; k < element.size(); k++) {
  616. out << element[k].asInt();
  617. gen_comma(out, k, element);
  618. }
  619. out << "]";
  620. gen_comma(out, i, element_connections);
  621. out << "\n";
  622. }
  623. out << "] # " << id_element_connections << "\n\n";
  624. // surface areas
  625. vector<string> sr_global_names;
  626. if (object.isMember(KEY_DEFINE_SURFACE_REGIONS)) {
  627. Value& define_surface_regions = object[KEY_DEFINE_SURFACE_REGIONS];
  628. for (Value::ArrayIndex i = 0; i < define_surface_regions.size(); i++) {
  629. string sr_name = make_id(get_node(
  630. KEY_DEFINE_SURFACE_REGIONS, define_surface_regions[i], KEY_NAME).asString());
  631. Value& include_elements = get_node(
  632. KEY_DEFINE_SURFACE_REGIONS, define_surface_regions[i], KEY_INCLUDE_ELEMENTS);
  633. string sr_global_name = name + "_" + sr_name;
  634. string sr_element_connections_name = sr_global_name + "_" + NAME_WALL_INDICES;
  635. out << sr_element_connections_name << " = [";
  636. for (Value::ArrayIndex k = 0; k < include_elements.size(); k++) {
  637. if (k % 16 == 0) {
  638. out << "\n" << IND;
  639. }
  640. out << include_elements[k].asInt();
  641. gen_comma(out, k, include_elements);
  642. }
  643. out << "\n] #" << sr_element_connections_name << "\n\n";
  644. out << sr_global_name << " = " << MDOT << NAME_CLASS_SURFACE_REGION << "(\n";
  645. out << IND << NAME_NAME << " = '" << sr_name << "',\n";
  646. out << IND << NAME_WALL_INDICES << " = " << sr_element_connections_name << "\n";
  647. out << CTOR_END;
  648. sr_global_names.push_back(sr_global_name);
  649. }
  650. }
  651. // object creation itself
  652. out << name << " = " << MDOT << NAME_CLASS_GEOMETRY_OBJECT << "(\n";
  653. gen_param(out, NAME_NAME, name, true);
  654. gen_param_id(out, NAME_VERTEX_LIST, id_vertex_list, true);
  655. gen_param_id(out, NAME_WALL_LIST, id_element_connections, true);
  656. out << IND << NAME_SURFACE_REGIONS << " = [";
  657. for (size_t i = 0; i < sr_global_names.size(); i++) {
  658. out << sr_global_names[i];
  659. print_comma(out, i, sr_global_names);
  660. }
  661. out << "]\n)\n";
  662. out << make_end_block_comment(name);
  663. return name;
  664. }
  665. void PythonGenerator::generate_geometry(std::ostream& out, std::vector<std::string>& geometry_objects) {
  666. Value& geometrical_objects = get_node(mcell, KEY_GEOMETRICAL_OBJECTS);
  667. if (!geometrical_objects.isMember(KEY_OBJECT_LIST)) {
  668. return;
  669. }
  670. Value& object_list = get_node(geometrical_objects, KEY_OBJECT_LIST);
  671. for (Value::ArrayIndex i = 0; i < object_list.size(); i++) {
  672. Value& object = object_list[i];
  673. string name = generate_single_geometry_object(out, i, object);
  674. if (name == BNG::DEFAULT_COMPARTMENT_NAME) {
  675. data.has_default_compartment_object = true;
  676. }
  677. geometry_objects.push_back(name);
  678. }
  679. }
  680. // returns true if the two release sites can be merged into a single one
  681. static bool can_be_in_same_list_release_site(Value& rs1, Value& rs2) {
  682. return
  683. rs1[KEY_SHAPE].asString() == VALUE_LIST &&
  684. rs2[KEY_SHAPE].asString() == VALUE_LIST &&
  685. rs1[KEY_PATTERN].asString() == rs2[KEY_PATTERN].asString() &&
  686. rs1[KEY_QUANTITY].asString() == rs2[KEY_QUANTITY].asString() &&
  687. rs1[KEY_QUANTITY_TYPE].asString() == rs2[KEY_QUANTITY_TYPE].asString() &&
  688. rs1[KEY_RELEASE_PROBABILITY].asString() == rs2[KEY_RELEASE_PROBABILITY].asString() &&
  689. rs1[KEY_SITE_DIAMETER].asString() == rs2[KEY_SITE_DIAMETER].asString() &&
  690. rs1[KEY_STDDEV].asString() == rs2[KEY_STDDEV].asString();
  691. }
  692. std::string PythonGenerator::generate_single_molecule_release_info_array(
  693. std::ostream& out,
  694. std::string& rel_site_name,
  695. Json::Value& release_site_list,
  696. Json::Value::ArrayIndex begin,
  697. Json::Value::ArrayIndex end) {
  698. string name = MOLECULE_LIST_PREFIX + rel_site_name;
  699. out << name << " = [\n";
  700. for (Value::ArrayIndex rs_index = begin; rs_index < end; rs_index++) {
  701. Value& release_site_item = release_site_list[rs_index];
  702. Value& points_list = release_site_item[KEY_POINTS_LIST];
  703. for (Value::ArrayIndex i = 0; i < points_list.size(); i++) {
  704. out << " " << MDOT << NAME_CLASS_MOLECULE_RELEASE_INFO << "(\n";
  705. string cplx = release_site_item[KEY_MOLECULE].asString();
  706. bool is_vol = is_volume_species(mcell, cplx);
  707. string orient = convert_orientation(release_site_item[KEY_ORIENT].asString(), !is_vol);
  708. out << " ";
  709. gen_param_expr(out, NAME_COMPLEX,
  710. make_species_or_cplx(data, cplx, orient),
  711. true);
  712. Value& point = points_list[i];
  713. if (point.size() != 3) {
  714. ERROR("Release site " + rel_site_name + ": points_list item does not have three values.");
  715. }
  716. out << " " << NAME_LOCATION << " = [" << point[0].asDouble() << ", " << point[1].asDouble() << ", " << point[2].asDouble() << "]";
  717. out << "\n )";
  718. if (i + 1 != points_list.size()) {
  719. out << ", ";
  720. }
  721. }
  722. if (rs_index + 1 != end) {
  723. out << ", ";
  724. }
  725. out << "\n";
  726. }
  727. out << "] # " << name << "\n\n";
  728. return name;
  729. }
  730. static void gen_region_expr_assignment_for_rel_site(ostream& out, string region_expr) {
  731. // we don't really need to parse the expression, just dump it in a right way
  732. // example: Cell[ALL] - (Organelle_1[ALL] + Organelle_2[ALL])
  733. // remove [ALL]
  734. // replace [text] with _text
  735. regex pattern_all("\\[ALL\\]");
  736. region_expr = regex_replace(region_expr, pattern_all, "");
  737. regex pattern_surf_region("\\[([^\\]]*)\\]");
  738. region_expr = regex_replace(region_expr, pattern_surf_region, "_$1");
  739. gen_param_expr(out, NAME_REGION, region_expr, true);
  740. }
  741. static void error_release_pattern(const string& name) {
  742. ERROR("Requested release pattern " + name + " not found in data model.");
  743. }
  744. void PythonGenerator::generate_release_pattern(std::ostream& out, const std::string& name, std::string& delay_string) {
  745. // generate only once, however we must return the delay string
  746. bool already_generated = data.is_already_defined(name, NAME_CLASS_RELEASE_PATTERN);
  747. if (!mcell.isMember(KEY_DEFINE_RELEASE_PATTERNS)) {
  748. error_release_pattern(name);
  749. }
  750. Value& define_release_patterns = get_node(mcell, KEY_DEFINE_RELEASE_PATTERNS);
  751. Value& release_pattern_list = get_node(define_release_patterns, KEY_RELEASE_PATTERN_LIST);
  752. for (Value::ArrayIndex i = 0; i < release_pattern_list.size(); i++) {
  753. Value& release_pattern_item = release_pattern_list[i];
  754. if (release_pattern_item[KEY_NAME].asString() != name) {
  755. continue;
  756. }
  757. if (!already_generated) {
  758. // we found the release pattern
  759. check_version(KEY_RELEASE_PATTERN_LIST, release_pattern_item, VER_DM_2018_01_11_1330);
  760. data.check_if_already_defined_and_add(name, NAME_CLASS_RELEASE_PATTERN);
  761. gen_ctor_call(out, name, NAME_CLASS_RELEASE_PATTERN);
  762. gen_param(out, NAME_NAME, name, true);
  763. gen_param_expr(out, NAME_RELEASE_INTERVAL, release_pattern_item[KEY_RELEASE_INTERVAL], true);
  764. gen_param_expr(out, NAME_TRAIN_DURATION, release_pattern_item[KEY_TRAIN_DURATION], true);
  765. gen_param_expr(out, NAME_TRAIN_INTERVAL, release_pattern_item[KEY_TRAIN_INTERVAL], true);
  766. gen_param_expr(out, NAME_NUMBER_OF_TRAINS, release_pattern_item[KEY_NUMBER_OF_TRAINS], false);
  767. out << CTOR_END;
  768. }
  769. delay_string = release_pattern_item[KEY_DELAY].asString();
  770. return;
  771. }
  772. error_release_pattern(name);
  773. }
  774. void PythonGenerator::generate_release_sites(std::ostream& out, std::vector<std::string>& release_site_names) {
  775. if (!mcell.isMember(KEY_RELEASE_SITES)) {
  776. return;
  777. }
  778. Value& release_sites = mcell[KEY_RELEASE_SITES];
  779. check_version(KEY_RELEASE_SITES, release_sites, VER_DM_2014_10_24_1638);
  780. Value& release_site_list = release_sites[KEY_RELEASE_SITE_LIST];
  781. Value::ArrayIndex i = 0;
  782. while (i < release_site_list.size()) {
  783. Value& release_site_item = release_site_list[i];
  784. check_version(KEY_RELEASE_SITE_LIST, release_site_item, VER_DM_2018_01_11_1330);
  785. string name = make_id(release_site_item[KEY_NAME].asString());
  786. string shape = release_site_item[KEY_SHAPE].asString();
  787. string molecule_list_name = "";
  788. if (shape == VALUE_LIST) {
  789. // 1) try to find the largest number of subsequent
  790. // release sites that can be represented by a single ReleaseSite
  791. Value::ArrayIndex matching_end = i + 1;
  792. while (matching_end < release_site_list.size() &&
  793. can_be_in_same_list_release_site(release_site_item, release_site_list[matching_end])) {
  794. matching_end++;
  795. }
  796. // remove suffix from release name
  797. if (i < matching_end - 1 && name.substr(name.size() - 2) == "_0") {
  798. name = name.substr(0, name.size() - 2);
  799. }
  800. // 2) generate an array of SingleMoleculeReleaseInfo objects
  801. molecule_list_name =
  802. generate_single_molecule_release_info_array(out, name, release_site_list, i, matching_end);
  803. // skip all release sites we handled here
  804. i = matching_end - 1;
  805. }
  806. // generate release pattern if needed
  807. string rel_pat_name = release_site_item[KEY_PATTERN].asString();
  808. string delay_string = "";
  809. if (rel_pat_name != "") {
  810. generate_release_pattern(out, rel_pat_name, delay_string);
  811. }
  812. release_site_names.push_back(name);
  813. gen_description(out, release_site_item);
  814. data.check_if_already_defined_and_add(name, NAME_CLASS_RELEASE_SITE);
  815. gen_ctor_call(out, name, NAME_CLASS_RELEASE_SITE);
  816. gen_param(out, NAME_NAME, name, true);
  817. string compartment;
  818. if (shape != VALUE_LIST) {
  819. string cplx = release_site_item[KEY_MOLECULE].asString();
  820. if (cplx == "") {
  821. ERROR("Release site '" + name + "' does not have its molecule/complex to be released set.");
  822. }
  823. bool is_vol = is_volume_species(data.mcell, cplx);
  824. string orientation = convert_orientation(release_site_item[KEY_ORIENT].asString(), !is_vol);
  825. if (orientation != "" && is_vol) {
  826. cout <<
  827. "Ignoring orientation set for release site " << name << " with species " << cplx <<
  828. ", this species represent volume molecules.\n";
  829. orientation = "";
  830. }
  831. compartment = get_single_compartment(cplx);
  832. gen_param_expr(out, NAME_COMPLEX, make_species_or_cplx(data, cplx, orientation, compartment), true);
  833. }
  834. if (delay_string != "" && delay_string != "0") {
  835. gen_param_expr(out, NAME_RELEASE_TIME, delay_string, true);
  836. }
  837. if (rel_pat_name != "") {
  838. gen_param_id(out, NAME_RELEASE_PATTERN, rel_pat_name, true);
  839. }
  840. string prob = release_site_item[KEY_RELEASE_PROBABILITY].asString();
  841. bool prob_not_1 = (prob != "1" && prob != "1.0");
  842. if (shape == VALUE_SPHERICAL) {
  843. if (compartment != "") {
  844. ERROR("Cannot use compartment " + compartment + " with spherical release.");
  845. }
  846. gen_param_enum(out, NAME_SHAPE, NAME_ENUM_SHAPE, NAME_EV_SPHERICAL, true);
  847. gen_param_list_3_floats(
  848. out, NAME_LOCATION,
  849. release_site_item[KEY_LOCATION_X], release_site_item[KEY_LOCATION_Y], release_site_item[KEY_LOCATION_Z],
  850. true
  851. );
  852. gen_param_expr(out, NAME_SITE_DIAMETER, release_site_item[KEY_SITE_DIAMETER], true);
  853. }
  854. else if (shape == VALUE_OBJECT) {
  855. gen_region_expr_assignment_for_rel_site(out, release_site_item[KEY_OBJECT_EXPR].asString());
  856. }
  857. else if (shape == VALUE_LIST) {
  858. // FIXME: check that no compartments are used here
  859. assert(molecule_list_name != "");
  860. bool diam_is_zero = release_site_item[KEY_SITE_DIAMETER] == "0";
  861. gen_param_id(out, NAME_MOLECULE_LIST, molecule_list_name, !diam_is_zero);
  862. if (!diam_is_zero) {
  863. gen_param_expr(out, NAME_SITE_DIAMETER, release_site_item[KEY_SITE_DIAMETER], prob_not_1);
  864. }
  865. }
  866. else {
  867. ERROR("Shape " + shape + " is not supported yet");
  868. }
  869. if (shape != VALUE_LIST) {
  870. string quantity_type = release_site_item[KEY_QUANTITY_TYPE].asString();
  871. if (quantity_type == VALUE_NUMBER_TO_RELEASE) {
  872. check_not_empty(release_site_item, KEY_QUANTITY, "Release site");
  873. gen_param_expr(out, NAME_NUMBER_TO_RELEASE, release_site_item[KEY_QUANTITY], prob_not_1);
  874. }
  875. else if (quantity_type == VALUE_DENSITY) {
  876. string species_name = release_site_item[KEY_MOLECULE].asString();
  877. if (is_volume_species(data.mcell, species_name)) {
  878. gen_param_expr(out, NAME_CONCENTRATION, release_site_item[KEY_QUANTITY], prob_not_1);
  879. }
  880. else {
  881. gen_param_expr(out, NAME_DENSITY, release_site_item[KEY_QUANTITY], prob_not_1);
  882. }
  883. }
  884. else {
  885. ERROR("Quantity type " + quantity_type + " is not supported yet");
  886. }
  887. }
  888. if (prob_not_1) {
  889. gen_param_expr(out, NAME_RELEASE_PROBABILITY, prob, false);
  890. }
  891. out << CTOR_END;
  892. i++;
  893. }
  894. }
  895. std::vector<std::string> PythonGenerator::get_species_to_visualize() {
  896. vector<string> res;
  897. Value& define_molecules = get_node(mcell, KEY_DEFINE_MOLECULES);
  898. check_version(KEY_DEFINE_MOLECULES, define_molecules, VER_DM_2014_10_24_1638);
  899. Value& molecule_list = get_node(define_molecules, KEY_MOLECULE_LIST);
  900. for (Value::ArrayIndex i = 0; i < molecule_list.size(); i++) {
  901. Value& molecule_list_item = molecule_list[i];
  902. check_version(KEY_MOLECULE_LIST, molecule_list_item, VER_DM_2018_10_16_1632);
  903. if (molecule_list_item[KEY_EXPORT_VIZ].asBool()) {
  904. if (data.bng_mode) {
  905. res.push_back(make_species(molecule_list_item[KEY_MOL_NAME].asString()));
  906. }
  907. else {
  908. res.push_back(molecule_list_item[KEY_MOL_NAME].asString());
  909. }
  910. }
  911. }
  912. return res;
  913. }
  914. void PythonGenerator::generate_viz_outputs(
  915. std::ostream& out, const bool cellblender_viz,
  916. std::vector<std::string>& viz_output_names) {
  917. if (!mcell.isMember(KEY_VIZ_OUTPUT)) {
  918. return;
  919. }
  920. Value& viz_output = mcell[KEY_VIZ_OUTPUT];
  921. check_version(KEY_VIZ_OUTPUT, viz_output, VER_DM_2014_10_24_1638);
  922. // species_list
  923. vector<string> viz_species = get_species_to_visualize();
  924. if (!viz_output[KEY_EXPORT_ALL].asBool() && viz_species.empty()) {
  925. return; // nothing to generate
  926. }
  927. string name = VIZ_OUTPUT_NAME; // there is only one in datamodel now
  928. viz_output_names.push_back(name);
  929. // CHECK_PROPERTY(viz_output[KEY_ALL_ITERATIONS].asBool()); // don't care
  930. CHECK_PROPERTY(viz_output[KEY_START].asString() == "0");
  931. data.check_if_already_defined_and_add(name, NAME_CLASS_VIZ_OUTPUT);
  932. gen_ctor_call(out, name, NAME_CLASS_VIZ_OUTPUT);
  933. // mode is ascii by default, this information is not in datamodel
  934. const char* mode = (cellblender_viz) ? NAME_EV_CELLBLENDER : NAME_EV_ASCII;
  935. gen_param_enum(out, NAME_MODE, NAME_ENUM_VIZ_MODE, mode, true);
  936. gen_param(out, NAME_OUTPUT_FILES_PREFIX, DEFAULT_VIZ_OUTPUT_FILENAME_PREFIX, true);
  937. // species_list
  938. if (!viz_output[KEY_EXPORT_ALL].asBool() && !viz_species.empty()) {
  939. gen_param_list(out, NAME_SPECIES_LIST, viz_species, true);
  940. }
  941. if (viz_output[KEY_STEP].asString() == "0") {
  942. cout << "Viz output specification has periodicity (" << NAME_EVERY_N_TIMESTEPS << ") value 0. "
  943. << "This is probably caused by conversion from MDL where the number of simulated iterations "
  944. << "lower than the actual periodicity. It is not possible to determine the periodicity in this case.\n";
  945. }
  946. if (!viz_output[KEY_ALL_ITERATIONS].asBool()) {
  947. gen_param_expr(out, NAME_EVERY_N_TIMESTEPS, viz_output[KEY_STEP], false);
  948. }
  949. else {
  950. gen_param_expr(out, NAME_EVERY_N_TIMESTEPS, "1", false);
  951. }
  952. // ignoring KEY_END
  953. out << CTOR_END;
  954. }
  955. std::string PythonGenerator::generate_single_count_term(
  956. ostream& out,
  957. const std::string& what_to_count,
  958. const std::string& where_to_count,
  959. const std::string& orientation,
  960. const bool molecules_not_species,
  961. const bool rxn_not_mol
  962. ) {
  963. string name = COUNT_TERM_PREFIX +
  964. create_count_name(what_to_count, where_to_count, molecules_not_species);
  965. // generate the count term object definition if we don't already have it
  966. // TODO: move this into a function
  967. if (find(data.all_count_term_names.begin(), data.all_count_term_names.end(), name) == data.all_count_term_names.end()) {
  968. data.all_count_term_names.push_back(name);
  969. data.check_if_already_defined_and_add(name, NAME_CLASS_COUNT_TERM);
  970. gen_ctor_call(out, name, NAME_CLASS_COUNT_TERM);
  971. bool comma_after = (where_to_count != "");
  972. if (rxn_not_mol) {
  973. gen_param_expr(out, NAME_REACTION_RULE, what_to_count, comma_after);
  974. }
  975. else {
  976. gen_param_expr(
  977. out, molecules_not_species ? NAME_MOLECULES_PATTERN : NAME_SPECIES_PATTERN,
  978. make_species_or_cplx(data, what_to_count, orientation, ""), comma_after);
  979. }
  980. if (where_to_count != "") {
  981. gen_param_expr(out, NAME_REGION, where_to_count, false);
  982. }
  983. out << CTOR_END;
  984. }
  985. return fix_id(name);
  986. }
  987. // stores multiplier value into the multiplier argument as a string
  988. // if present, the expected form is mult*(<counts>)
  989. string PythonGenerator::generate_count_terms_for_expression(
  990. ostream& out,
  991. const string& mdl_string, // may be empty, in that case we use what_to_count and where_to_count
  992. const std::string& what_to_count,
  993. const std::string& where_to_count,
  994. const std::string& orientation,
  995. const bool rxn_not_mol
  996. ) {
  997. string res_expr;
  998. if (mdl_string != "") {
  999. size_t last_end = 0;
  1000. uint num_counts = get_num_counts_in_mdl_string(mdl_string);
  1001. // the first count term item must be positive
  1002. for (uint i = 0; i < num_counts; i++) {
  1003. size_t start = mdl_string.find(COUNT, last_end);
  1004. size_t end = find_end_brace_pos(mdl_string, start + strlen(COUNT));
  1005. if (end == string::npos) {
  1006. end = mdl_string.size();
  1007. }
  1008. bool rxn_not_mol;
  1009. bool molecules_not_species;
  1010. string what_to_count;
  1011. string where_to_count;
  1012. string orientation;
  1013. process_single_count_term(
  1014. data, mdl_string.substr(start, end - start + 1),
  1015. rxn_not_mol, molecules_not_species, what_to_count, where_to_count, orientation
  1016. );
  1017. string name = generate_single_count_term(
  1018. out, what_to_count, where_to_count, orientation, molecules_not_species, rxn_not_mol);
  1019. // for the res_expr, we cut all the COUNT[..] and replace them with
  1020. // the ids of the CountTerm objects
  1021. if (last_end != 0)
  1022. res_expr += " " + mdl_string.substr(last_end + 1, start - (last_end + 1)) + " " + name;
  1023. else {
  1024. res_expr += name;
  1025. }
  1026. last_end = end;
  1027. }
  1028. }
  1029. else {
  1030. bool molecules_not_species = false; // MCell base counting always uses molecules
  1031. string name = COUNT_TERM_PREFIX +
  1032. create_count_name(what_to_count, where_to_count, molecules_not_species);
  1033. res_expr = generate_single_count_term(
  1034. out, what_to_count, where_to_count, orientation, molecules_not_species, rxn_not_mol);
  1035. }
  1036. return res_expr;
  1037. }
  1038. void PythonGenerator::generate_single_count(
  1039. std::ostream& out,
  1040. const std::string& count_name,
  1041. const std::string& observable_name,
  1042. const std::string& file_name,
  1043. const std::string& count_term_name,
  1044. const std::string& mul_div_str,
  1045. const std::string& rxn_step
  1046. ) {
  1047. data.check_if_already_defined_and_add(count_name, NAME_CLASS_COUNT);
  1048. gen_ctor_call(out, count_name, NAME_CLASS_COUNT);
  1049. gen_param(out, NAME_NAME, observable_name, true);
  1050. gen_param_expr(out, NAME_EXPRESSION, count_term_name, true);
  1051. gen_param(out, NAME_FILE_NAME,
  1052. DEFAULT_RXN_OUTPUT_FILENAME_PREFIX + file_name, mul_div_str != "" || rxn_step != "");
  1053. if (mul_div_str != "") {
  1054. if (mul_div_str[0] == '*') {
  1055. gen_param_expr(out, NAME_MULTIPLIER, mul_div_str.substr(1), rxn_step != "");
  1056. }
  1057. else if (mul_div_str[0] == '/') {
  1058. gen_param_expr(out, NAME_MULTIPLIER, "(1.0/" + mul_div_str.substr(1) + ")", rxn_step != "");
  1059. }
  1060. else {
  1061. release_assert(false && "Invalid count multiplier or divider.");
  1062. }
  1063. }
  1064. if (rxn_step != "") {
  1065. // rxn_step is specified in seconds, need to convert to seconds
  1066. const string& time_step = mcell[KEY_INITIALIZATION][KEY_TIME_STEP].asString();
  1067. gen_param_expr(out, NAME_EVERY_N_TIMESTEPS, rxn_step + "/" + time_step, false);
  1068. }
  1069. out << CTOR_END;
  1070. }
  1071. void PythonGenerator::generate_all_bngl_reaction_rules_used_in_observables(std::ostream& out) {
  1072. // bngl_reaction_rules_used_in_observables are initialized in MCell4Generator::generate_reaction_rules
  1073. out << "# ---- declaration of rxn rules defined in BNGL and used in counts ----\n";
  1074. for (string& name: data.bngl_reaction_rules_used_in_observables) {
  1075. out <<
  1076. name << " = " << get_module_name_w_prefix(data.output_files_prefix, SUBSYSTEM) << "." <<
  1077. NAME_FIND_REACTION_RULE << "('" << name << "')\n";
  1078. out << "assert " << name << ", \"Reaction rule '" + name + "' was not found\"\n\n";
  1079. }
  1080. out << "\n";
  1081. }
  1082. void PythonGenerator::generate_surface_classes_assignments(ostream& out) {
  1083. if (!data.mcell.isMember(KEY_MODIFY_SURFACE_REGIONS)) {
  1084. return;
  1085. }
  1086. Value& modify_surface_regions = data.mcell[KEY_MODIFY_SURFACE_REGIONS];
  1087. check_version(KEY_MODIFY_SURFACE_REGIONS, modify_surface_regions, VER_DM_2014_10_24_1638);
  1088. Value& modify_surface_regions_list = modify_surface_regions[KEY_MODIFY_SURFACE_REGIONS_LIST];
  1089. for (Value::ArrayIndex i = 0; i < modify_surface_regions_list.size(); i++) {
  1090. Value& modify_surface_regions_item = modify_surface_regions_list[i];
  1091. check_versions(
  1092. KEY_MODIFY_SURFACE_REGIONS_LIST, modify_surface_regions_item,
  1093. VER_DM_2018_01_11_1330, VER_DM_2020_07_12_1600
  1094. );
  1095. string object_name = modify_surface_regions_item[KEY_OBJECT_NAME].asString();
  1096. CHECK_PROPERTY(object_name != "");
  1097. string region_selection = modify_surface_regions_item[KEY_REGION_SELECTION].asString();
  1098. string obj_or_region_name;
  1099. if (region_selection == VALUE_ALL) {
  1100. obj_or_region_name = object_name;
  1101. }
  1102. else if (region_selection == VALUE_SEL) {
  1103. obj_or_region_name = object_name + "_" + modify_surface_regions_item[KEY_REGION_NAME].asString();
  1104. }
  1105. else {
  1106. ERROR("Unexpected value " + region_selection + " of " + KEY_REGION_SELECTION + ".");
  1107. }
  1108. // handle initial_region_molecules_list if present
  1109. if (modify_surface_regions_item.isMember(KEY_INITIAL_REGION_MOLECULES_LIST)) {
  1110. string initial_region_molecules_name = obj_or_region_name + "_" + NAME_INITIAL_SURFACE_RELEASES;
  1111. out << initial_region_molecules_name << " = [\n";
  1112. Value& initial_region_molecules_list = modify_surface_regions_item[KEY_INITIAL_REGION_MOLECULES_LIST];
  1113. for (Value::ArrayIndex rel_i = 0; rel_i < initial_region_molecules_list.size(); rel_i++) {
  1114. Value& item = initial_region_molecules_list[rel_i];
  1115. out << " ";
  1116. gen_ctor_call(out, "", NAME_CLASS_INITIAL_SURFACE_RELEASE, true);
  1117. out << " ";
  1118. string species_str = item[KEY_MOLECULE].asString();
  1119. string orient = convert_orientation(item[KEY_ORIENT].asString(), true);
  1120. gen_param_expr(out, NAME_COMPLEX, make_species_or_cplx(data, species_str, orient), true);
  1121. out << " ";
  1122. if (item.isMember(KEY_MOLECULE_NUMBER)) {
  1123. gen_param_expr(out, NAME_NUMBER_TO_RELEASE, item[KEY_MOLECULE_NUMBER], false);
  1124. }
  1125. else if (item.isMember(KEY_MOLECULE_DENSITY)) {
  1126. gen_param_expr(out, NAME_DENSITY, item[KEY_MOLECULE_DENSITY], false);
  1127. }
  1128. else {
  1129. ERROR(
  1130. S("Missing ") + KEY_MOLECULE_NUMBER + " or " + KEY_MOLECULE_DENSITY +
  1131. " in " + KEY_INITIAL_REGION_MOLECULES_LIST + "."
  1132. );
  1133. }
  1134. out << " )";
  1135. gen_comma(out, rel_i, initial_region_molecules_list);
  1136. out << "\n";
  1137. }
  1138. out << "]\n\n";
  1139. out << obj_or_region_name << "." << NAME_INITIAL_SURFACE_RELEASES << " = " << initial_region_molecules_name << "\n";
  1140. }
  1141. // and also surface class name
  1142. if (modify_surface_regions_item.isMember(KEY_SURF_CLASS_NAME)) {
  1143. gen_description(out, modify_surface_regions_item);
  1144. string surf_class_name = modify_surface_regions_item[KEY_SURF_CLASS_NAME].asString();
  1145. out << obj_or_region_name << "." << NAME_SURFACE_CLASS << " = " << surf_class_name << "\n";
  1146. }
  1147. }
  1148. }
  1149. void PythonGenerator::generate_compartment_assignments(std::ostream& out) {
  1150. Value& model_objects = get_node(data.mcell, KEY_MODEL_OBJECTS);
  1151. Value& model_object_list = get_node(model_objects, KEY_MODEL_OBJECT_LIST);
  1152. if (model_object_list.empty()) {
  1153. return;
  1154. }
  1155. for (Value::ArrayIndex i = 0; i < model_object_list.size(); i++) {
  1156. Value& model_object = model_object_list[i];
  1157. // older data model files don't have to have this attribute
  1158. // therefore we are also checking for used compartments in
  1159. // data.is_used_compartment
  1160. bool is_bngl_compartment =
  1161. model_object.isMember(KEY_IS_BNGL_COMPARTMENT) && model_object[KEY_IS_BNGL_COMPARTMENT].asBool();
  1162. if (is_bngl_compartment || data.is_used_compartment(model_object)) {
  1163. // name is the name of the object
  1164. const string& name = model_object[KEY_NAME].asString();
  1165. const string& membrane_name = model_object[KEY_MEMBRANE_NAME].asString();
  1166. gen_assign(out, name, NAME_IS_BNGL_COMPARTMENT, true);
  1167. if (membrane_name != "") {
  1168. gen_assign_str(out, name, NAME_SURFACE_COMPARTMENT_NAME, membrane_name);
  1169. data.surface_to_volume_compartments_map[membrane_name] = name;
  1170. }
  1171. out << "\n";
  1172. }
  1173. }
  1174. }
  1175. } /* namespace MCell */