User Guide
kinetic_block.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <catch2/catch_test_macros.hpp>
9 
10 #include "ast/program.hpp"
11 #include "parser/nmodl_driver.hpp"
19 
20 using namespace nmodl;
21 using namespace visitor;
22 using namespace test;
23 using namespace test_utils;
24 
25 using ast::AstNodeType;
27 
28 //=============================================================================
29 // KineticBlock visitor tests
30 //=============================================================================
31 
32 std::vector<std::string> run_kinetic_block_visitor(const std::string& text) {
33  std::vector<std::string> results;
34 
35  // construct AST from text including KINETIC block(s)
37  const auto& ast = driver.parse_string(text);
38 
39  // construct symbol table from AST
40  SymtabVisitor().visit_program(*ast);
41 
42  // run KineticBlock visitor on AST
43  KineticBlockVisitor().visit_program(*ast);
44 
45  // Since KineticBlockVisitor internally performs const-folding and
46  // loop unrolling, we run CheckParentVisitor.
47  CheckParentVisitor().check_ast(*ast);
48 
49  // run lookup visitor to extract DERIVATIVE block(s) from AST
50  const auto& blocks = collect_nodes(*ast, {AstNodeType::DERIVATIVE_BLOCK});
51  results.reserve(blocks.size());
52  for (const auto& block: blocks) {
53  results.push_back(to_nmodl(block));
54  }
55 
56 
57  // check that, after visitor rearrangement, parents are still up-to-date
58  CheckParentVisitor().check_ast(*ast);
59 
60  return results;
61 }
62 
63 SCENARIO("Convert KINETIC to DERIVATIVE using KineticBlock visitor", "[kinetic][visitor]") {
64  GIVEN("KINETIC block with << reaction statement, 1 state var") {
65  static const std::string input_nmodl_text = R"(
66  STATE {
67  x
68  }
69  KINETIC states {
70  ~ x << (a*c/3.2)
71  })";
72  static const std::string output_nmodl_text = R"(
73  DERIVATIVE states {
74  LOCAL source0_
75  source0_ = a*c/3.2
76  x' = (source0_)
77  })";
78  THEN("Convert to equivalent DERIVATIVE block") {
79  auto result = run_kinetic_block_visitor(input_nmodl_text);
80  CAPTURE(input_nmodl_text);
81  REQUIRE(result[0] == reindent_text(output_nmodl_text));
82  }
83  }
84  GIVEN("KINETIC block with << reaction statement, 1 array state var") {
85  std::string input_nmodl_text = R"(
86  STATE {
87  x[1]
88  }
89  KINETIC states {
90  ~ x[0] << (a*c/3.2)
91  })";
92  std::string output_nmodl_text = R"(
93  DERIVATIVE states {
94  LOCAL source0_
95  source0_ = a*c/3.2
96  x'[0] = (source0_)
97  })";
98  THEN("Convert to equivalent DERIVATIVE block") {
99  auto result = run_kinetic_block_visitor(input_nmodl_text);
100  CAPTURE(input_nmodl_text);
101  REQUIRE(result[0] == reindent_text(output_nmodl_text));
102  }
103  }
104  GIVEN("KINETIC block with << reaction statement, 1 array state var, flux vars") {
105  std::string input_nmodl_text = R"(
106  STATE {
107  x[1]
108  }
109  KINETIC states {
110  ~ x[0] << (a*c/3.2)
111  f0 = f_flux*2
112  f1 = b_flux + f_flux
113  })";
114  std::string output_nmodl_text = R"(
115  DERIVATIVE states {
116  LOCAL source0_
117  source0_ = a*c/3.2
118  f0 = 0*2
119  f1 = 0+0
120  x'[0] = (source0_)
121  })";
122  THEN("Convert to equivalent DERIVATIVE block") {
123  auto result = run_kinetic_block_visitor(input_nmodl_text);
124  CAPTURE(input_nmodl_text);
125  REQUIRE(result[0] == reindent_text(output_nmodl_text));
126  }
127  }
128  GIVEN("KINETIC block with invalid << reaction statement with 2 state vars") {
129  std::string input_nmodl_text = R"(
130  STATE {
131  x y
132  }
133  KINETIC states {
134  ~ x + y << (2*z)
135  })";
136  THEN("Fail by throwing an error.") {
137  CHECK_THROWS(run_kinetic_block_visitor(input_nmodl_text));
138  }
139  }
140  GIVEN("KINETIC block with -> reaction statement, 1 state var, flux vars") {
141  std::string input_nmodl_text = R"(
142  STATE {
143  x
144  }
145  KINETIC states {
146  ~ x -> (a)
147  zf = f_flux
148  zb = b_flux
149  })";
150  std::string output_nmodl_text = R"(
151  DERIVATIVE states {
152  LOCAL kf0_
153  kf0_ = a
154  zf = kf0_*x
155  zb = 0
156  x' = (-1*(kf0_*x))
157  })";
158  THEN("Convert to equivalent DERIVATIVE block") {
159  auto result = run_kinetic_block_visitor(input_nmodl_text);
160  CAPTURE(input_nmodl_text);
161  REQUIRE(result[0] == reindent_text(output_nmodl_text));
162  }
163  }
164  GIVEN("KINETIC block with -> reaction statement, 2 state vars") {
165  std::string input_nmodl_text = R"(
166  STATE {
167  x y
168  }
169  KINETIC states {
170  ~ x + y -> (f(v))
171  })";
172  std::string output_nmodl_text = R"(
173  DERIVATIVE states {
174  LOCAL kf0_
175  kf0_ = f(v)
176  x' = (-1*(kf0_*x*y))
177  y' = (-1*(kf0_*x*y))
178  })";
179  THEN("Convert to equivalent DERIVATIVE block") {
180  auto result = run_kinetic_block_visitor(input_nmodl_text);
181  CAPTURE(input_nmodl_text);
182  REQUIRE(result[0] == reindent_text(output_nmodl_text));
183  }
184  }
185  GIVEN("KINETIC block with -> reaction statement, 2 state vars, CONSERVE statement") {
186  std::string input_nmodl_text = R"(
187  STATE {
188  x y
189  }
190  KINETIC states {
191  ~ x + y -> (f(v))
192  CONSERVE x + y = 1
193  })";
194  std::string output_nmodl_text = R"(
195  DERIVATIVE states {
196  LOCAL kf0_
197  kf0_ = f(v)
198  CONSERVE y = 1-x
199  x' = (-1*(kf0_*x*y))
200  y' = (-1*(kf0_*x*y))
201  })";
202  THEN("Convert to equivalent DERIVATIVE block, rewrite CONSERVE statement") {
203  auto result = run_kinetic_block_visitor(input_nmodl_text);
204  CAPTURE(input_nmodl_text);
205  REQUIRE(result[0] == reindent_text(output_nmodl_text));
206  }
207  }
208  GIVEN("KINETIC block with -> reaction statement, 2 state vars, CONSERVE & COMPARTMENT") {
209  std::string input_nmodl_text = R"(
210  STATE {
211  x y
212  }
213  KINETIC states {
214  COMPARTMENT a { x }
215  COMPARTMENT b { y }
216  ~ x + y -> (f(v))
217  CONSERVE x + y = 1
218  })";
219  std::string output_nmodl_text = R"(
220  DERIVATIVE states {
221  LOCAL kf0_
222  kf0_ = f(v)
223  CONSERVE y = (1-(a*1*x))/(b*1)
224  x' = ((-1*(kf0_*x*y)))/(a)
225  y' = ((-1*(kf0_*x*y)))/(b)
226  })";
227  THEN(
228  "Convert to equivalent DERIVATIVE block, rewrite CONSERVE statement inc COMPARTMENT "
229  "factors") {
230  auto result = run_kinetic_block_visitor(input_nmodl_text);
231  CAPTURE(input_nmodl_text);
232  REQUIRE(result[0] == reindent_text(output_nmodl_text));
233  }
234  }
235  GIVEN("KINETIC block with -> reaction statement, array of 2 state var") {
236  std::string input_nmodl_text = R"(
237  STATE {
238  x[2]
239  }
240  KINETIC states {
241  ~ x[0] + x[1] -> (f(v))
242  })";
243  std::string output_nmodl_text = R"(
244  DERIVATIVE states {
245  LOCAL kf0_
246  kf0_ = f(v)
247  x'[0] = (-1*(kf0_*x[0]*x[1]))
248  x'[1] = (-1*(kf0_*x[0]*x[1]))
249  })";
250  THEN("Convert to equivalent DERIVATIVE block") {
251  auto result = run_kinetic_block_visitor(input_nmodl_text);
252  CAPTURE(input_nmodl_text);
253  REQUIRE(result[0] == reindent_text(output_nmodl_text));
254  }
255  }
256  GIVEN("KINETIC block with -> reaction statement, indexed COMPARTMENT") {
257  std::string input_nmodl_text = R"(
258  STATE {
259  x[2]
260  }
261  KINETIC states {
262  COMPARTMENT i, vol[i] { x }
263  ~ x[0] + x[1] -> (f(v))
264  })";
265  std::string output_nmodl_text = R"(
266  DERIVATIVE states {
267  LOCAL kf0_
268  kf0_ = f(v)
269  x'[0] = ((-1*(kf0_*x[0]*x[1])))/(vol[0])
270  x'[1] = ((-1*(kf0_*x[0]*x[1])))/(vol[1])
271  })";
272  THEN("Convert to equivalent DERIVATIVE block") {
273  auto result = run_kinetic_block_visitor(input_nmodl_text);
274  CAPTURE(input_nmodl_text);
275  REQUIRE(result[0] == reindent_text(output_nmodl_text));
276  }
277  }
278  GIVEN("KINETIC block with one reaction statement, 1 state var, 1 non-state var, flux vars") {
279  // Here c is NOT a state variable
280  // see 9.9.2.1 of NEURON book
281  // c should be treated as a constant, i.e.
282  // -the diff. eq. for x should include the contribution from c
283  // -no diff. eq. should be generated for c itself
284  std::string input_nmodl_text = R"(
285  STATE {
286  x
287  }
288  KINETIC states {
289  ~ x <-> c (r, r)
290  c1 = f_flux - b_flux
291  })";
292  std::string output_nmodl_text = R"(
293  DERIVATIVE states {
294  LOCAL kf0_, kb0_
295  kf0_ = r
296  kb0_ = r
297  c1 = kf0_*x-kb0_*c
298  x' = (-1*(kf0_*x-kb0_*c))
299  })";
300  THEN("Convert to equivalent DERIVATIVE block") {
301  auto result = run_kinetic_block_visitor(input_nmodl_text);
302  CAPTURE(input_nmodl_text);
303  REQUIRE(result[0] == reindent_text(output_nmodl_text));
304  }
305  }
306  GIVEN("KINETIC block with one reaction statement, 2 state vars") {
307  std::string input_nmodl_text = R"(
308  STATE {
309  x y
310  }
311  KINETIC states {
312  ~ x <-> y (a, b)
313  })";
314  std::string output_nmodl_text = R"(
315  DERIVATIVE states {
316  LOCAL kf0_, kb0_
317  kf0_ = a
318  kb0_ = b
319  x' = (-1*(kf0_*x-kb0_*y))
320  y' = (1*(kf0_*x-kb0_*y))
321  })";
322  THEN("Convert to equivalent DERIVATIVE block") {
323  auto result = run_kinetic_block_visitor(input_nmodl_text);
324  CAPTURE(input_nmodl_text);
325  REQUIRE(result[0] == reindent_text(output_nmodl_text));
326  }
327  }
328  GIVEN("KINETIC block with one reaction statement, 2 state vars, CONSERVE statement") {
329  std::string input_nmodl_text = R"(
330  STATE {
331  x y
332  }
333  KINETIC states {
334  ~ x <-> y (a, b)
335  CONSERVE x + y = 0
336  })";
337  std::string output_nmodl_text = R"(
338  DERIVATIVE states {
339  LOCAL kf0_, kb0_
340  kf0_ = a
341  kb0_ = b
342  CONSERVE y = 0-x
343  x' = (-1*(kf0_*x-kb0_*y))
344  y' = (1*(kf0_*x-kb0_*y))
345  })";
346  THEN("Convert to equivalent DERIVATIVE block, rewrite CONSERVE statement") {
347  auto result = run_kinetic_block_visitor(input_nmodl_text);
348  CAPTURE(input_nmodl_text);
349  REQUIRE(result[0] == reindent_text(output_nmodl_text));
350  }
351  }
352  // array vars in CONSERVE statements are implicit sums over elements
353  // see p34 of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.7812&rep=rep1&type=pdf
354  GIVEN("KINETIC block with array state vars, CONSERVE statement") {
355  std::string input_nmodl_text = R"(
356  STATE {
357  x[3] y
358  }
359  KINETIC states {
360  ~ x[0] <-> x[1] (a, b)
361  ~ x[2] <-> y (c, d)
362  CONSERVE y + x = 1
363  })";
364  std::string output_nmodl_text = R"(
365  DERIVATIVE states {
366  LOCAL kf0_, kb0_, kf1_, kb1_
367  kf0_ = a
368  kb0_ = b
369  kf1_ = c
370  kb1_ = d
371  CONSERVE x[2] = 1-y-x[0]-x[1]
372  x'[0] = (-1*(kf0_*x[0]-kb0_*x[1]))
373  x'[1] = (1*(kf0_*x[0]-kb0_*x[1]))
374  x'[2] = (-1*(kf1_*x[2]-kb1_*y))
375  y' = (1*(kf1_*x[2]-kb1_*y))
376  })";
377  THEN(
378  "Convert to equivalent DERIVATIVE block, rewrite CONSERVE statement after summing over "
379  "array elements, with last state var on LHS") {
380  auto result = run_kinetic_block_visitor(input_nmodl_text);
381  CAPTURE(input_nmodl_text);
382  REQUIRE(result[0] == reindent_text(output_nmodl_text));
383  }
384  }
385  // array vars in CONSERVE statements are implicit sums over elements
386  // see p34 of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.7812&rep=rep1&type=pdf
387  GIVEN("KINETIC block with array state vars, re-ordered CONSERVE statement") {
388  std::string input_nmodl_text = R"(
389  STATE {
390  x[3] y
391  }
392  KINETIC states {
393  ~ x[0] <-> x[1] (a, b)
394  ~ x[2] <-> y (c, d)
395  CONSERVE x + y = 1
396  })";
397  std::string output_nmodl_text = R"(
398  DERIVATIVE states {
399  LOCAL kf0_, kb0_, kf1_, kb1_
400  kf0_ = a
401  kb0_ = b
402  kf1_ = c
403  kb1_ = d
404  CONSERVE y = 1-x[0]-x[1]-x[2]
405  x'[0] = (-1*(kf0_*x[0]-kb0_*x[1]))
406  x'[1] = (1*(kf0_*x[0]-kb0_*x[1]))
407  x'[2] = (-1*(kf1_*x[2]-kb1_*y))
408  y' = (1*(kf1_*x[2]-kb1_*y))
409  })";
410  THEN(
411  "Convert to equivalent DERIVATIVE block, rewrite CONSERVE statement after summing over "
412  "array elements, with last state var on LHS") {
413  auto result = run_kinetic_block_visitor(input_nmodl_text);
414  CAPTURE(input_nmodl_text);
415  REQUIRE(result[0] == reindent_text(output_nmodl_text));
416  }
417  }
418  GIVEN("KINETIC block with one reaction statement & 1 COMPARTMENT statement") {
419  std::string input_nmodl_text = R"(
420  STATE {
421  x y
422  }
423  KINETIC states {
424  COMPARTMENT c-d {x y}
425  ~ x <-> y (a, b)
426  })";
427  std::string output_nmodl_text = R"(
428  DERIVATIVE states {
429  LOCAL kf0_, kb0_
430  kf0_ = a
431  kb0_ = b
432  x' = ((-1*(kf0_*x-kb0_*y)))/(c-d)
433  y' = ((1*(kf0_*x-kb0_*y)))/(c-d)
434  })";
435  THEN("Convert to equivalent DERIVATIVE block") {
436  auto result = run_kinetic_block_visitor(input_nmodl_text);
437  CAPTURE(input_nmodl_text);
438  REQUIRE(result[0] == reindent_text(output_nmodl_text));
439  }
440  }
441  GIVEN("KINETIC block with two CONSERVE statements") {
442  std::string input_nmodl_text = R"(
443  STATE {
444  c1 o1 o2 p0 p1
445  }
446  KINETIC ihkin {
447  evaluate_fct(v, cai)
448  ~ c1 <-> o1 (alpha, beta)
449  ~ p0 <-> p1 (k1ca, k2)
450  ~ o1 <-> o2 (k3p, k4)
451  CONSERVE p0+p1 = 1
452  CONSERVE c1+o1+o2 = 1
453  })";
454  std::string output_nmodl_text = R"(
455  DERIVATIVE ihkin {
456  LOCAL kf0_, kb0_, kf1_, kb1_, kf2_, kb2_
457  evaluate_fct(v, cai)
458  kf0_ = alpha
459  kb0_ = beta
460  kf1_ = k1ca
461  kb1_ = k2
462  kf2_ = k3p
463  kb2_ = k4
464  CONSERVE p1 = 1-p0
465  CONSERVE o2 = 1-c1-o1
466  c1' = (-1*(kf0_*c1-kb0_*o1))
467  o1' = (1*(kf0_*c1-kb0_*o1))+(-1*(kf2_*o1-kb2_*o2))
468  o2' = (1*(kf2_*o1-kb2_*o2))
469  p0' = (-1*(kf1_*p0-kb1_*p1))
470  p1' = (1*(kf1_*p0-kb1_*p1))
471  })";
472  THEN("Convert to equivalent DERIVATIVE block, re-order both CONSERVE statements") {
473  auto result = run_kinetic_block_visitor(input_nmodl_text);
474  CAPTURE(input_nmodl_text);
475  REQUIRE(result[0] == reindent_text(output_nmodl_text));
476  }
477  }
478  GIVEN("KINETIC block with one reaction statement & clashing local") {
479  std::string input_nmodl_text = R"(
480  ASSIGNED {
481  kf0_
482  }
483  STATE {
484  x y
485  }
486  KINETIC states {
487  LOCAL kf0_0000, kb0_0000
488  ~ x <-> y (kf0_, b)
489  })";
490  std::string output_nmodl_text = R"(
491  DERIVATIVE states {
492  LOCAL kf0_0000, kb0_0000, kf0_0001, kb0_
493  kf0_0001 = kf0_
494  kb0_ = b
495  x' = (-1*(kf0_0001*x-kb0_*y))
496  y' = (1*(kf0_0001*x-kb0_*y))
497  })";
498  THEN("Convert to equivalent DERIVATIVE block") {
499  auto result = run_kinetic_block_visitor(input_nmodl_text);
500  CAPTURE(input_nmodl_text);
501  REQUIRE(result[0] == reindent_text(output_nmodl_text));
502  }
503  }
504  GIVEN("KINETIC block with one reaction statement & 2 COMPARTMENT statements") {
505  std::string input_nmodl_text = R"(
506  STATE {
507  x y
508  }
509  KINETIC states {
510  COMPARTMENT cx {x}
511  COMPARTMENT cy {y}
512  ~ x <-> y (a, b)
513  })";
514  std::string output_nmodl_text = R"(
515  DERIVATIVE states {
516  LOCAL kf0_, kb0_
517  kf0_ = a
518  kb0_ = b
519  x' = ((-1*(kf0_*x-kb0_*y)))/(cx)
520  y' = ((1*(kf0_*x-kb0_*y)))/(cy)
521  })";
522  THEN("Convert to equivalent DERIVATIVE block") {
523  auto result = run_kinetic_block_visitor(input_nmodl_text);
524  CAPTURE(input_nmodl_text);
525  REQUIRE(result[0] == reindent_text(output_nmodl_text));
526  }
527  }
528  GIVEN("KINETIC block with two independent reaction statements") {
529  std::string input_nmodl_text = R"(
530  STATE {
531  w x y z
532  }
533  KINETIC states {
534  ~ x <-> y (a, b)
535  ~ w <-> z (c, d)
536  })";
537  std::string output_nmodl_text = R"(
538  DERIVATIVE states {
539  LOCAL kf0_, kb0_, kf1_, kb1_
540  kf0_ = a
541  kb0_ = b
542  kf1_ = c
543  kb1_ = d
544  w' = (-1*(kf1_*w-kb1_*z))
545  x' = (-1*(kf0_*x-kb0_*y))
546  y' = (1*(kf0_*x-kb0_*y))
547  z' = (1*(kf1_*w-kb1_*z))
548  })";
549  THEN("Convert to equivalent DERIVATIVE block") {
550  auto result = run_kinetic_block_visitor(input_nmodl_text);
551  CAPTURE(input_nmodl_text);
552  REQUIRE(result[0] == reindent_text(output_nmodl_text));
553  }
554  }
555  GIVEN("KINETIC block with two dependent reaction statements") {
556  std::string input_nmodl_text = R"(
557  STATE {
558  x y z
559  }
560  KINETIC states {
561  ~ x <-> y (a, b)
562  ~ y <-> z (c, d)
563  })";
564  std::string output_nmodl_text = R"(
565  DERIVATIVE states {
566  LOCAL kf0_, kb0_, kf1_, kb1_
567  kf0_ = a
568  kb0_ = b
569  kf1_ = c
570  kb1_ = d
571  x' = (-1*(kf0_*x-kb0_*y))
572  y' = (1*(kf0_*x-kb0_*y))+(-1*(kf1_*y-kb1_*z))
573  z' = (1*(kf1_*y-kb1_*z))
574  })";
575  THEN("Convert to equivalent DERIVATIVE block") {
576  auto result = run_kinetic_block_visitor(input_nmodl_text);
577  CAPTURE(input_nmodl_text);
578  REQUIRE(result[0] == reindent_text(output_nmodl_text));
579  }
580  }
581  GIVEN("KINETIC block with two dependent reaction statements, flux vars") {
582  std::string input_nmodl_text = R"(
583  STATE {
584  x y z
585  }
586  KINETIC states {
587  c0 = f_flux
588  ~ x <-> y (a, b)
589  c1 = f_flux + b_flux
590  ~ y <-> z (c, d)
591  c2 = f_flux - 2*b_flux
592  })";
593  std::string output_nmodl_text = R"(
594  DERIVATIVE states {
595  LOCAL kf0_, kb0_, kf1_, kb1_
596  c0 = 0
597  kf0_ = a
598  kb0_ = b
599  c1 = kf0_*x+kb0_*y
600  kf1_ = c
601  kb1_ = d
602  c2 = kf1_*y-2*kb1_*z
603  x' = (-1*(kf0_*x-kb0_*y))
604  y' = (1*(kf0_*x-kb0_*y))+(-1*(kf1_*y-kb1_*z))
605  z' = (1*(kf1_*y-kb1_*z))
606  })";
607  THEN("Convert to equivalent DERIVATIVE block") {
608  auto result = run_kinetic_block_visitor(input_nmodl_text);
609  CAPTURE(input_nmodl_text);
610  REQUIRE(result[0] == reindent_text(output_nmodl_text));
611  }
612  }
613  GIVEN("KINETIC block with a stoch coeff of 2") {
614  std::string input_nmodl_text = R"(
615  STATE {
616  x y
617  }
618  KINETIC states {
619  ~ 2x <-> y (a, b)
620  })";
621  std::string output_nmodl_text = R"(
622  DERIVATIVE states {
623  LOCAL kf0_, kb0_
624  kf0_ = a
625  kb0_ = b
626  x' = (-2*(kf0_*x*x-kb0_*y))
627  y' = (1*(kf0_*x*x-kb0_*y))
628  })";
629  THEN("Convert to equivalent DERIVATIVE block") {
630  auto result = run_kinetic_block_visitor(input_nmodl_text);
631  CAPTURE(input_nmodl_text);
632  REQUIRE(result[0] == reindent_text(output_nmodl_text));
633  }
634  }
635  GIVEN("KINETIC block with duplicate state vars") {
636  std::string input_nmodl_text = R"(
637  STATE {
638  x y
639  }
640  KINETIC states {
641  ~ x + x <-> y (a, b)
642  })";
643  std::string output_nmodl_text = R"(
644  DERIVATIVE states {
645  LOCAL kf0_, kb0_
646  kf0_ = a
647  kb0_ = b
648  x' = (-2*(kf0_*x*x-kb0_*y))
649  y' = (1*(kf0_*x*x-kb0_*y))
650  })";
651  THEN("Convert to equivalent DERIVATIVE block") {
652  auto result = run_kinetic_block_visitor(input_nmodl_text);
653  CAPTURE(input_nmodl_text);
654  REQUIRE(result[0] == reindent_text(output_nmodl_text));
655  }
656  }
657  GIVEN("KINETIC block with functions for reaction rates") {
658  // Example from sec 9.8, p238 of NEURON book
659  std::string input_nmodl_text = R"(
660  STATE {
661  mc m
662  }
663  KINETIC states {
664  ~ mc <-> m (a(v), b(v))
665  })";
666  std::string output_nmodl_text = R"(
667  DERIVATIVE states {
668  LOCAL kf0_, kb0_
669  kf0_ = a(v)
670  kb0_ = b(v)
671  mc' = (-1*(kf0_*mc-kb0_*m))
672  m' = (1*(kf0_*mc-kb0_*m))
673  })";
674  THEN("Convert to equivalent DERIVATIVE block") {
675  auto result = run_kinetic_block_visitor(input_nmodl_text);
676  CAPTURE(input_nmodl_text);
677  REQUIRE(result[0] == reindent_text(output_nmodl_text));
678  }
679  }
680  GIVEN("KINETIC block with stoch coeff 2, coupled pair of statements") {
681  // Example from sec 9.8, p239 of NEURON book
682  std::string input_nmodl_text = R"(
683  STATE {
684  A B C D
685  }
686  KINETIC states {
687  ~ 2A + B <-> C (k1, k2)
688  ~ C + D <-> A + 2B (k3, k4)
689  })";
690  std::string output_nmodl_text = R"(
691  DERIVATIVE states {
692  LOCAL kf0_, kb0_, kf1_, kb1_
693  kf0_ = k1
694  kb0_ = k2
695  kf1_ = k3
696  kb1_ = k4
697  A' = (-2*(kf0_*A*A*B-kb0_*C))+(1*(kf1_*C*D-kb1_*A*B*B))
698  B' = (-1*(kf0_*A*A*B-kb0_*C))+(2*(kf1_*C*D-kb1_*A*B*B))
699  C' = (1*(kf0_*A*A*B-kb0_*C))+(-1*(kf1_*C*D-kb1_*A*B*B))
700  D' = (-1*(kf1_*C*D-kb1_*A*B*B))
701  })";
702 
703  THEN("Convert to equivalent DERIVATIVE block") {
704  auto result = run_kinetic_block_visitor(input_nmodl_text);
705  CAPTURE(input_nmodl_text);
706  REQUIRE(result[0] == reindent_text(output_nmodl_text));
707  }
708  }
709  GIVEN("KINETIC block with loop over array variable") {
710  std::string input_nmodl_text = R"(
711  DEFINE N 5
712  ASSIGNED {
713  a
714  b[N]
715  c[N]
716  d
717  }
718  STATE {
719  x[N]
720  }
721  KINETIC kin {
722  ~ x[0] << (a)
723  FROM i=0 TO N-2 {
724  ~ x[i] <-> x[i+1] (b[i], c[i])
725  }
726  ~ x[N-1] -> (d)
727  })";
728  std::string output_nmodl_text = R"(
729  DERIVATIVE kin {
730  LOCAL kf0_, kb0_, kf1_, kb1_, kf2_, kb2_, kf3_, kb3_, source4_, kf5_
731  source4_ = a
732  {
733  kf0_ = b[0]
734  kb0_ = c[0]
735  kf1_ = b[1]
736  kb1_ = c[1]
737  kf2_ = b[2]
738  kb2_ = c[2]
739  kf3_ = b[3]
740  kb3_ = c[3]
741  }
742  kf5_ = d
743  x'[0] = (source4_)+(-1*(kf0_*x[0]-kb0_*x[1]))
744  x'[1] = (1*(kf0_*x[0]-kb0_*x[1]))+(-1*(kf1_*x[1]-kb1_*x[2]))
745  x'[2] = (1*(kf1_*x[1]-kb1_*x[2]))+(-1*(kf2_*x[2]-kb2_*x[3]))
746  x'[3] = (1*(kf2_*x[2]-kb2_*x[3]))+(-1*(kf3_*x[3]-kb3_*x[4]))
747  x'[4] = (1*(kf3_*x[3]-kb3_*x[4]))+(-1*(kf5_*x[4]))
748  })";
749  THEN("Convert to equivalent DERIVATIVE block") {
750  auto result = run_kinetic_block_visitor(input_nmodl_text);
751  CAPTURE(input_nmodl_text);
752  REQUIRE(result[0] == reindent_text(output_nmodl_text));
753  }
754  }
755 }
test_utils.hpp
nmodl::parser::NmodlDriver
Class that binds all pieces together for parsing nmodl file.
Definition: nmodl_driver.hpp:67
nmodl::to_nmodl
std::string to_nmodl(const ast::Ast &node, const std::set< ast::AstNodeType > &exclude_types)
Given AST node, return the NMODL string representation.
Definition: visitor_utils.cpp:234
nmodl::test_utils::reindent_text
std::string reindent_text(const std::string &text, int indent_level)
Reindent nmodl text for text-to-text comparison.
Definition: test_utils.cpp:53
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
loop_unroll_visitor.hpp
Unroll for loop in the AST.
constant_folder_visitor.hpp
Perform constant folding of integer/float/double expressions.
nmodl::ast::AstNodeType
AstNodeType
Enum type for every AST node type.
Definition: ast_decl.hpp:166
kinetic_block_visitor.hpp
Visitor for kinetic block statements
visitor_utils.hpp
Utility functions for visitors implementation.
program.hpp
Auto generated AST classes declaration.
driver
nmodl::parser::UnitDriver driver
Definition: parser.cpp:28
nmodl::parser::UnitDriver::parse_string
bool parse_string(const std::string &input)
parser Units provided as string (used for testing)
Definition: unit_driver.cpp:40
SCENARIO
SCENARIO("Convert KINETIC to DERIVATIVE using KineticBlock visitor", "[kinetic][visitor]")
Definition: kinetic_block.cpp:63
nmodl::collect_nodes
std::vector< std::shared_ptr< const ast::Ast > > collect_nodes(const ast::Ast &node, const std::vector< ast::AstNodeType > &types)
traverse node recursively and collect nodes of given types
Definition: visitor_utils.cpp:206
run_kinetic_block_visitor
std::vector< std::string > run_kinetic_block_visitor(const std::string &text)
Definition: kinetic_block.cpp:32
checkparent_visitor.hpp
Visitor for checking parents of ast nodes
nmodl_driver.hpp
symtab_visitor.hpp
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.