8 #include <catch2/catch_test_macros.hpp>
20 using namespace nmodl;
21 using namespace visitor;
22 using namespace codegen;
34 SymtabVisitor().visit_program(*ast);
35 CodegenHelperVisitor v;
38 const auto& info = v.analyze(*ast);
41 std::string variables;
44 for (
const auto& var: info.range_parameter_vars) {
45 variables += var->get_name() +
";";
47 for (
const auto& var: info.range_assigned_vars) {
48 variables += var->get_name() +
";";
50 for (
const auto& var: info.range_state_vars) {
51 variables += var->get_name() +
";";
53 for (
const auto& var: info.assigned_vars) {
54 variables += var->get_name() +
";";
63 SymtabVisitor{}.visit_program(*ast);
64 KineticBlockVisitor{}.visit_program(*ast);
65 SymtabVisitor{}.visit_program(*ast);
66 SteadystateVisitor{}.visit_program(*ast);
67 SymtabVisitor{}.visit_program(*ast);
68 NeuronSolveVisitor{}.visit_program(*ast);
69 SolveBlockVisitor{}.visit_program(*ast);
70 SymtabVisitor{
true}.visit_program(*ast);
72 bool enable_cvode =
true;
73 CodegenHelperVisitor v(enable_cvode);
74 const auto info = v.analyze(*ast);
79 SCENARIO(
"unusual / failing mod files",
"[codegen][var_order]") {
80 GIVEN(
"cal_mig.mod : USEION variables declared as RANGE") {
81 std::string nmodl_text = R
"(
83 gcalbar=.003 (mho/cm2)
92 USEION ca READ cai,cao WRITE ica
93 RANGE gcalbar, cai, ica, gcal, ggk
113 THEN("ionic current variable declared as RANGE appears first") {
114 std::string expected =
"gcalbar;ica;gcal;minf;tau;ggk;m;cai;cao;";
116 REQUIRE(result == expected);
120 GIVEN(
"CaDynamics_E2.mod : USEION variables declared as STATE variable") {
121 std::string nmodl_text = R
"(
124 USEION ca READ ica WRITE cai
125 RANGE decay, gamma, minCai, depth
129 gamma = 0.05 : percent of free calcium (not buffered)
130 decay = 80 (ms) : rate of removal of calcium
131 depth = 0.1 (um) : depth of shell
135 ASSIGNED {ica (mA/cm2)}
142 cai' = -(10000)*(ica*gamma/(2*FARADAY*depth)) - (cai - minCai)/decay
146 THEN("ion state variable is ordered after parameter and assigned ionic current") {
147 std::string expected =
"gamma;decay;depth;minCai;ica;cai;";
149 REQUIRE(result == expected);
153 GIVEN(
"cadyn.mod : same USEION variables used for read as well as write") {
154 std::string nmodl_text = R
"(
157 USEION ca READ cai,ica WRITE cai
159 GLOBAL depth,cainf,taur
164 taur = 200 (ms) : rate of calcium removal
165 cainf = 50e-6(mM) :changed oct2
171 drive_channel (mM/ms)
179 SOLVE state METHOD euler
183 ca' = drive_channel/18 + (cainf -ca)/taur*11
188 THEN("ion variables are ordered correctly") {
189 std::string expected =
"ca;cai;ica;drive_channel;";
191 REQUIRE(result == expected);
196 SCENARIO(
"Check global variable setup",
"[codegen][global_variables]") {
197 GIVEN(
"SH_na8st.mod: modfile from reduced_dentate model") {
198 std::string
const nmodl_text{R
"(
204 SOLVE kin METHOD derivimplicit
207 SOLVE kin STEADYSTATE derivimplicit
217 SymtabVisitor{}.visit_program(*ast);
218 KineticBlockVisitor{}.visit_program(*ast);
219 SymtabVisitor{}.visit_program(*ast);
220 SteadystateVisitor{}.visit_program(*ast);
221 SymtabVisitor{}.visit_program(*ast);
222 NeuronSolveVisitor{}.visit_program(*ast);
223 SolveBlockVisitor{}.visit_program(*ast);
224 SymtabVisitor{
true}.visit_program(*ast);
226 CodegenHelperVisitor v;
227 const auto info = v.analyze(*ast);
229 THEN(
"Checking that primes_size and prime_variables_by_order have the expected size") {
230 REQUIRE(info.primes_size == 2);
231 REQUIRE(info.prime_variables_by_order.size() == 2);
240 SymtabVisitor().visit_program(*ast);
241 CodegenHelperVisitor v;
243 return v.analyze(*ast);
246 TEST_CASE(
"Check ion write/read checks") {
247 std::string input_nmodl = R
"(
250 USEION ca READ cai WRITE cai, eca
251 USEION na WRITE nao, ena
279 for (
const auto& ion: info.ions) {
280 if (ion.name ==
"ca") {
281 REQUIRE(ion.is_conc_read());
282 REQUIRE(ion.is_interior_conc_read());
283 REQUIRE(!ion.is_exterior_conc_read());
284 REQUIRE(!ion.is_rev_read());
286 REQUIRE(ion.is_conc_written());
287 REQUIRE(ion.is_interior_conc_written());
288 REQUIRE(!ion.is_exterior_conc_written());
289 REQUIRE(ion.is_rev_written());
291 if (ion.name ==
"na") {
292 REQUIRE(!ion.is_conc_read());
293 REQUIRE(!ion.is_interior_conc_read());
294 REQUIRE(!ion.is_exterior_conc_read());
295 REQUIRE(!ion.is_rev_read());
297 REQUIRE(ion.is_conc_written());
298 REQUIRE(!ion.is_interior_conc_written());
299 REQUIRE(ion.is_exterior_conc_written());
300 REQUIRE(ion.is_rev_written());
302 if (ion.name ==
"K") {
303 REQUIRE(ion.is_conc_read());
304 REQUIRE(ion.is_interior_conc_read());
305 REQUIRE(!ion.is_exterior_conc_read());
306 REQUIRE(ion.is_rev_read());
308 REQUIRE(!ion.is_conc_written());
309 REQUIRE(!ion.is_interior_conc_written());
310 REQUIRE(!ion.is_exterior_conc_written());
311 REQUIRE(!ion.is_rev_written());
317 GIVEN(
"a mod file with a single KINETIC block") {
318 std::string input_nmodl = R
"(
326 SOLVE states METHOD cnexp
331 REQUIRE(info.emit_cvode);
334 GIVEN(
"a mod file with a single DERIVATIVE block") {
335 std::string input_nmodl = R
"(
340 SOLVE state METHOD derivimplicit
349 REQUIRE(info.emit_cvode);
352 GIVEN(
"a mod file with a single PROCEDURE block solved with method `after_cvode`") {
353 std::string input_nmodl = R
"(
355 SOLVE state METHOD after_cvode
363 REQUIRE(info.emit_cvode);
366 GIVEN(
"a mod file with a single PROCEDURE block NOT solved with method `after_cvode`") {
367 std::string input_nmodl = R
"(
369 SOLVE state METHOD cnexp
376 THEN(
"Do not emit CVODE") {
377 REQUIRE(!info.emit_cvode);
380 GIVEN(
"a mod file with a DERIVATIVE and a KINETIC block") {
381 std::string input_nmodl = R
"(
387 SOLVE der METHOD derivimplicit
388 SOLVE kin METHOD cnexp
400 THEN(
"Do not emit CVODE") {
401 REQUIRE(!info.emit_cvode);
404 GIVEN(
"a mod file with a PROCEDURE and a DERIVATIVE block") {
405 std::string input_nmodl = R
"(
410 SOLVE der METHOD derivimplicit
411 SOLVE func METHOD cnexp
422 THEN(
"Do not emit CVODE") {
423 REQUIRE(!info.emit_cvode);