diff --git a/modcc/module.cpp b/modcc/module.cpp index 0e5c4de5eda7f42df867146f96dee8e86fb6bacb..df34279f5953ab90f6ef6c526563c7e984d1e2c5 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -375,7 +375,21 @@ bool Module::semantic() { auto deriv = solve_expression->procedure(); if (deriv->kind()==procedureKind::kinetic) { - kinetic_rewrite(deriv->body())->accept(solver.get()); + auto rewrite_body = kinetic_rewrite(deriv->body()); + bool linear_kinetic = true; + + for (auto& s: rewrite_body->is_block()->statements()) { + if(s->is_assignment() && !state_vars.empty()) { + linear_test_result r = linear_test(s->is_assignment()->rhs(), state_vars); + linear_kinetic &= r.is_linear; + } + } + + if (!linear_kinetic) { + solver = std::make_unique<SparseNonlinearSolverVisitor>(); + } + + rewrite_body->accept(solver.get()); } else if (deriv->kind()==procedureKind::linear) { solver = std::make_unique<LinearSolverVisitor>(state_vars); diff --git a/modcc/solvers.cpp b/modcc/solvers.cpp index 4a53c0d9eb4081c82f4ca0644f0f283840794853..19e3700ae872df4eea6bd53a2bcbd715df869150 100644 --- a/modcc/solvers.cpp +++ b/modcc/solvers.cpp @@ -206,7 +206,8 @@ void SparseSolverVisitor::visit(CompartmentExpression *e) { return; } auto idx = it - dvars_.begin(); - scale_factor_[idx] = e->scale_factor()->clone(); + scale_factor_[idx] = make_expression<DivBinaryExpression>( + loc, make_expression<NumberExpression>(loc, 1.0), e->scale_factor()->clone()); } } @@ -272,7 +273,7 @@ void SparseSolverVisitor::visit(AssignmentExpression *e) { make_expression<MulBinaryExpression>(loc, r.coef[dvars_[j]]->clone(), dt_expr->clone()); if (scale_factor_[j]) { - expr = make_expression<DivBinaryExpression>(loc, std::move(expr), scale_factor_[j]->clone()); + expr = make_expression<MulBinaryExpression>(loc, std::move(expr), scale_factor_[j]->clone()); } } @@ -328,8 +329,8 @@ void SparseSolverVisitor::visit(ConserveExpression *e) { } } else { - error({"ICE: coefficient in state variable is not an identifier", loc}); - return; + error({"ICE: coefficient in state variable is not an identifier", loc}); + return; } // Replace that row with the conserve statement @@ -345,7 +346,7 @@ void SparseSolverVisitor::visit(ConserveExpression *e) { if (it != terms.end()) { auto expr = (*it)->is_stoich_term()->coeff()->clone(); if (scale_factor_[j]) { - expr = make_expression<MulBinaryExpression>(loc, scale_factor_[j]->clone(), std::move(expr)); + expr = make_expression<DivBinaryExpression>(loc, std::move(expr), scale_factor_[j]->clone()); } auto local_a_term = make_unique_local_assign(scope, expr.get(), "a_"); @@ -477,6 +478,7 @@ void LinearSolverVisitor::visit(LinearExpression *e) { rhs_.push_back(symtbl_.define(e->rhs()->is_identifier()->spelling())); ++deq_index_; } + void LinearSolverVisitor::finalize() { A_.augment(rhs_); @@ -528,6 +530,273 @@ void LinearSolverVisitor::finalize() { BlockRewriterBase::finalize(); } +void SparseNonlinearSolverVisitor::visit(BlockExpression* e) { + // Do a first pass to initialize some local variables and extract state variables + + for (auto& stmt: e->statements()) { + if (stmt && stmt->is_assignment() && stmt->is_assignment()->lhs()->is_derivative()) { + auto id = stmt->is_assignment()->lhs()->is_derivative(); + + // Save the state variables + dvars_.push_back(id->name()); + + // Create identifiers out of the state variables, they will be used to initialize some values + auto dvar_ident = make_expression<IdentifierExpression>(e->location(), id->name()); + + // Create two sets of local variables and assign them to the initial values of the state variables + // The first set doesn't change across iterations + auto init_dvar_term = make_unique_local_assign(e->scope(), dvar_ident.get(), "p_"); + auto init_dvar = init_dvar_term.id->is_identifier()->spelling(); + + // The second set is updated in every iteration of Newton's method + auto temp_dvar_term = make_unique_local_assign(e->scope(), dvar_ident.get(), "t_"); + auto temp_dvar = temp_dvar_term.id->is_identifier()->spelling(); + + // Save the variable names + dvar_init_.push_back(init_dvar); + dvar_temp_.push_back(temp_dvar); + + // Add local declarations and assignment statements + statements_.push_back(std::move(init_dvar_term.local_decl)); + statements_.push_back(std::move(init_dvar_term.assignment)); + + statements_.push_back(std::move(temp_dvar_term.local_decl)); + statements_.push_back(std::move(temp_dvar_term.assignment)); + } + } + scale_factor_.resize(dvars_.size()); + + BlockRewriterBase::visit(e); +} + +void SparseNonlinearSolverVisitor::visit(CompartmentExpression *e) { + auto loc = e->location(); + + for (auto& s: e->is_compartment()->state_vars()) { + auto it = std::find(dvars_.begin(), dvars_.end(), s->is_identifier()->spelling()); + if (it == dvars_.end()) { + error({"COMPARTMENT variable is not used", loc}); + return; + } + auto idx = it - dvars_.begin(); + + auto scale_inv = make_expression<DivBinaryExpression>( + loc, make_expression<NumberExpression>(loc, 1.0), e->scale_factor()->clone()); + auto local_s_term = make_unique_local_assign(e->scope(), scale_inv.get(), "s_"); + + statements_.push_back(std::move(local_s_term.local_decl)); + statements_.push_back(std::move(local_s_term.assignment)); + scale_factor_[idx] = std::move(local_s_term.id); + } +} + +void SparseNonlinearSolverVisitor::visit(AssignmentExpression *e) { + if (A_.empty()) { + unsigned n = dvars_.size(); + A_ = symge::sym_matrix(n, n); + } + + auto loc = e->location(); + scope_ptr scope = e->scope(); + + auto lhs = e->lhs(); + auto rhs = e->rhs(); + auto deriv = lhs->is_derivative(); + + if (!deriv) { + statements_.push_back(e->clone()); + + auto id = lhs->is_identifier(); + if (id) { + auto expand = substitute(rhs, local_expr_); + if (involves_identifier(expand, dvars_)) { + local_expr_[id->spelling()] = std::move(expand); + } + } + return; + } + + auto s = deriv->name(); + auto expanded_rhs = substitute(rhs, local_expr_); + + // Populate sparse symbolic matrix for GE. + if (s!=dvars_[deq_index_]) { + error({"ICE: inconsistent ordering of derivative assignments", loc}); + return; + } + + auto dt_expr = make_expression<IdentifierExpression>(loc, "dt"); + auto one_expr = make_expression<NumberExpression>(loc, 1.0); + + // Form and save F(y) = y - x(t) - dt G(y) + // y are stored in dvar_temp_ and are updated at every iteration of Newton's method + // x(t) are stored in dvar_init_ and are constant across iterations of Newton's method + // G(y) is the rhs of the derivative assignment expression + // Newton's method is used to find x(t+1) = y s.t. F(y) = 0. + + // `scale_factors` multiply the lhs of a differential equation. + // After applying Backward Euler and Newton's method, the new F(y) is + // F(y) = y - x(t) - dt * diag(s^-1) * G(y) + + expression_ptr F_x; + F_x = make_expression<MulBinaryExpression>(loc, expanded_rhs->clone(), dt_expr->clone()); + if (scale_factor_[deq_index_]) { + F_x = make_expression<MulBinaryExpression>(loc, std::move(F_x), scale_factor_[deq_index_]->clone()); + } + F_x = make_expression<AddBinaryExpression>(loc, make_expression<IdentifierExpression>(loc, dvar_init_[deq_index_]), std::move(F_x)); + F_x = make_expression<SubBinaryExpression>(loc, make_expression<IdentifierExpression>(loc, dvar_temp_[deq_index_]), std::move(F_x)); + + for (unsigned k = 0; k < dvars_.size(); ++k) { + F_x = substitute(F_x, dvars_[k], make_expression<IdentifierExpression>(loc, dvar_temp_[k])); + } + + auto local_f_term = make_unique_local_assign(scope, F_x.get(), "f_"); + auto a_ = local_f_term.id->is_identifier()->spelling(); + + statements_.push_back(std::move(local_f_term.local_decl)); + F_.push_back(std::move(local_f_term.assignment)); + + // Form and save the Jacobian J(x) of F(x) + // J(x) = I - dt * ∂G(x)/∂x + // If scale_factor[x] exists + // J(x) = I - dt * diag(s^-1) * ∂G(x)/∂x + + linear_test_result r = linear_test(expanded_rhs, dvars_); + for (unsigned j = 0; j<dvars_.size(); ++j) { + expression_ptr J_x; + + // For zero coefficient and diagonal element, the matrix entry is 1. + // For non-zero coefficient c and diagonal element, the entry is 1-c*dt. + // Otherwise, for non-zero coefficient c, the entry is -c*dt. + + if (r.coef.count(dvars_[j])) { + J_x = make_expression<MulBinaryExpression>(loc, + r.coef[dvars_[j]]->clone(), + dt_expr->clone()); + + if (scale_factor_[deq_index_]) { + J_x = make_expression<MulBinaryExpression>(loc, std::move(J_x), scale_factor_[deq_index_]->clone()); + } + } + + if (j==deq_index_) { + if (J_x) { + J_x = make_expression<SubBinaryExpression>(loc, one_expr->clone(), std::move(J_x)); + } + else { + J_x = one_expr->clone(); + } + } + else if (J_x) { + J_x = make_expression<NegUnaryExpression>(loc, std::move(J_x)); + } + + if (J_x) { + for (unsigned k = 0; k < dvars_.size(); ++k) { + J_x = substitute(J_x, dvars_[k], make_expression<IdentifierExpression>(loc, dvar_temp_[k])); + } + } + + if (!J_x) continue; + + auto local_j_term = make_unique_local_assign(scope, J_x.get(), "j_"); + auto j_ = local_j_term.id->is_identifier()->spelling(); + + statements_.push_back(std::move(local_j_term.local_decl)); + J_.push_back(std::move(local_j_term.assignment)); + + A_[deq_index_].push_back({j, symtbl_.define(j_)}); + } + ++deq_index_; +} + +void SparseNonlinearSolverVisitor::finalize() { + + // Create rhs of A_ + std::vector<symge::symbol> rhs; + for (const auto& var: F_) { + auto id = var->is_assignment()->lhs()->is_identifier()->spelling(); + rhs.push_back(symtbl_.define(id)); + } + + A_.augment(rhs); + symge::gj_reduce(A_, symtbl_); + + // Create and assign intermediate variables. + // Save gaussian elimination solution statements in S_ + std::vector<expression_ptr> S_; + for (unsigned i = 0; i < symtbl_.size(); ++i) { + symge::symbol s = symtbl_[i]; + + if (primitive(s)) continue; + + auto expr = as_expression(definition(s)); + auto local_t_term = make_unique_local_assign(block_scope_, expr.get(), "s_"); + auto t_ = local_t_term.id->is_identifier()->spelling(); + symtbl_.name(s, t_); + + statements_.push_back(std::move(local_t_term.local_decl)); + S_.push_back(std::move(local_t_term.assignment)); + } + + // Create the statements that update the temporary state variables + // (dvar_temp) after a Newton's iteration and save them in U_ + std::vector<expression_ptr> U_; + + Location loc; + for (unsigned i = 0; i < A_.nrow(); ++i) { + const symge::sym_row &row = A_[i]; + unsigned rhs_col = A_.augcol(); + int lhs_col = -1; + for (unsigned r = 0; r < A_.nrow(); r++) { + if (row[r]) { + lhs_col = r; + break; + } + } + + if (lhs_col == -1) { + error({"Something went wrong in solving the sparse matrix", loc}); + return; + } + + auto var = make_expression<IdentifierExpression>(loc, dvar_temp_[lhs_col]); + auto expr = make_expression<AssignmentExpression>(loc, var->clone(), + make_expression<SubBinaryExpression>(loc, var->clone(), + make_expression<DivBinaryExpression>(loc, make_expression<IdentifierExpression>(loc, symge::name(A_[i][rhs_col])), + make_expression<IdentifierExpression>(loc, symge::name(A_[i][lhs_col]))))); + + U_.push_back(std::move(expr)); + } + + // Do 3 Newton iterations + for (unsigned n = 0; n < 3; n++) { + // Print out the statements that calulate F(xn), J(xn), solve J(xn)^-1*F(xn), update xn -> xn+1 + for (auto &s: F_) { + statements_.push_back(s->clone()); + } + for (auto &s: J_) { + statements_.push_back(s->clone()); + } + for (auto &s: S_) { + statements_.push_back(s->clone()); + } + for (auto &s: U_) { + statements_.push_back(s->clone()); + } + } + + // Finally update the state variables, dvars_ = dvar_temp_ + for (unsigned i = 0; i < dvars_.size(); ++i) { + auto expr = make_expression<AssignmentExpression>(loc, + make_expression<IdentifierExpression>(loc, dvars_[i]), + make_expression<IdentifierExpression>(loc, dvar_temp_[i])); + statements_.push_back(std::move(expr)); + } + + BlockRewriterBase::finalize(); +} + // Implementation for `remove_unused_locals`: uses two visitors, // `UnusedVisitor` and `RemoveVariableVisitor` below. @@ -599,7 +868,7 @@ public: std::set<std::string> unused_locals() { if (!computed_) { for (auto& id: used_ids) { - remove_deps_from_unused(id); + remove_deps_from_unused(id, {}); } computed_ = true; } @@ -616,11 +885,12 @@ public: private: bool computed_ = false; - void remove_deps_from_unused(const std::string& id) { + void remove_deps_from_unused(const std::string& id, std::set<std::string> visited) { auto range = deps.equal_range(id); for (auto i = range.first; i != range.second; ++i) { - if (unused_ids.count(i->second)) { - remove_deps_from_unused(i->second); + if (unused_ids.count(i->second) && visited.find(i->second) == visited.end()) { + visited.insert(i->second); + remove_deps_from_unused(i->second, visited); } } unused_ids.erase(id); diff --git a/modcc/solvers.hpp b/modcc/solvers.hpp index 01bdba33a3499a61c5d6dac8bc28c0e624bc68fd..b375edc9b88f93dfb98c50236b6c006b16783b13 100644 --- a/modcc/solvers.hpp +++ b/modcc/solvers.hpp @@ -121,6 +121,55 @@ public: } }; +class SparseNonlinearSolverVisitor : public SolverVisitorBase { +protected: + // 'Current' differential equation is for variable with this + // index in `dvars`. + unsigned deq_index_ = 0; + + // Expanded local assignments that need to be substituted in for derivative + // calculations. + substitute_map local_expr_; + + // F(x) and the Jacobian J(x) for every state variable + // Needed for Newton's method + std::vector<expression_ptr> F_; + std::vector<expression_ptr> J_; + + std::vector<std::string> dvar_temp_; + std::vector<std::string> dvar_init_; + + // Symbolic matrix for backwards Euler step. + symge::sym_matrix A_; + + // 'Symbol table' for symbolic manipulation. + symge::symbol_table symtbl_; + + // State variable multiplier/divider + std::vector<expression_ptr> scale_factor_; + +public: + using SolverVisitorBase::visit; + + SparseNonlinearSolverVisitor() {} + SparseNonlinearSolverVisitor(scope_ptr enclosing): SolverVisitorBase(enclosing) {} + + virtual void visit(BlockExpression* e) override; + virtual void visit(AssignmentExpression *e) override; + virtual void visit(CompartmentExpression *e) override; + virtual void visit(ConserveExpression *e) override {}; + virtual void finalize() override; + virtual void reset() override { + deq_index_ = 0; + local_expr_.clear(); + F_.clear(); + J_.clear(); + symtbl_.clear(); + scale_factor_.clear(); + SolverVisitorBase::reset(); + } +}; + class LinearSolverVisitor : public SolverVisitorBase { protected: // 'Current' differential equation is for variable with this diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 29e8591e08876e5c1c946e17f46e89d546a53ad5..6791ccabe9b9de6c12e0a218787edeff5c31020d 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,6 +13,9 @@ set(test_mechanisms test1_kin_diff test1_kin_conserve test1_kin_compartment + test2_kin_diff + test3_kin_diff + test4_kin_compartment test1_kin_steadystate fixed_ica_current point_ica_current diff --git a/test/unit/mod/test2_kin_diff.mod b/test/unit/mod/test2_kin_diff.mod new file mode 100644 index 0000000000000000000000000000000000000000..6b2ef3b88817ec8a44f62521b3edc9fd68ad76df --- /dev/null +++ b/test/unit/mod/test2_kin_diff.mod @@ -0,0 +1,25 @@ +NEURON { + SUFFIX test2_kin_diff +} + +STATE { + a b c +} + +BREAKPOINT { + SOLVE state METHOD sparse +} + +KINETIC state { + LOCAL f, r + f = 2 + r = 1 + + ~ 2a + b <-> c (f, r) +} + +INITIAL { + a = 0.2 + b = 0.3 + c = 0.5 +} diff --git a/test/unit/mod/test3_kin_diff.mod b/test/unit/mod/test3_kin_diff.mod new file mode 100644 index 0000000000000000000000000000000000000000..286def99ce9b635fadd1fdada126cb5f1eb63d1d --- /dev/null +++ b/test/unit/mod/test3_kin_diff.mod @@ -0,0 +1,28 @@ +NEURON { + SUFFIX test3_kin_diff +} + +STATE { + a b c +} + +BREAKPOINT { + SOLVE state METHOD sparse +} + +KINETIC state { + LOCAL f0, f1, r0, r1 + f0 = 2 + r0 = 1 + f1 = 3 + r1 = 0 + + ~ a + b <-> c (f0, r0) + ~ c <-> b (f1, r1) +} + +INITIAL { + a = 0.2 + b = 0.3 + c = 0.5 +} diff --git a/test/unit/mod/test4_kin_compartment.mod b/test/unit/mod/test4_kin_compartment.mod new file mode 100644 index 0000000000000000000000000000000000000000..28bc29897eced1dcd00828639eee5623f3262150 --- /dev/null +++ b/test/unit/mod/test4_kin_compartment.mod @@ -0,0 +1,41 @@ +NEURON { + SUFFIX test4_kin_compartment +} + +STATE { + A + B + C + d + e +} + +PARAMETER { + x = 4 + y = 0.4 + z = 1.8 + w = 21 + s0 = 27 + s1 = 5.9 +} + +BREAKPOINT { + SOLVE state METHOD sparse +} + +INITIAL { + A = 4.5 + B = 6.6 + C = 0.28 + d = 2 + e = 0 + +} + +KINETIC state { + COMPARTMENT s0 {A B C} + COMPARTMENT s1 {d e} + + ~ A + B <-> C ( x, y ) + ~ A + d <-> e ( z, w ) +} \ No newline at end of file diff --git a/test/unit/test_kinetic_linear.cpp b/test/unit/test_kinetic_linear.cpp index e4bd84035be2b504be96dc69468edef99b389218..5a9d00965d1e6740da9c89cf3974af686df87f51 100644 --- a/test/unit/test_kinetic_linear.cpp +++ b/test/unit/test_kinetic_linear.cpp @@ -30,7 +30,8 @@ void run_test(std::string mech_name, std::vector<std::string> state_variables, std::unordered_map<std::string, fvm_value_type> assigned_variables, std::vector<fvm_value_type> t0_values, - std::vector<fvm_value_type> t1_values) { + std::vector<fvm_value_type> t1_values, + fvm_value_type dt) { auto cat = make_unit_test_catalogue(); @@ -75,7 +76,7 @@ void run_test(std::string mech_name, } } - shared_state->update_time_to(0.5, 0.5); + shared_state->update_time_to(dt, dt); shared_state->set_dt(); test->nrn_state(); @@ -89,89 +90,135 @@ void run_test(std::string mech_name, } } -TEST(mech_kinetic, kintetic_scaled) { +TEST(mech_kinetic, kintetic_linear_scaled) { std::vector<std::string> state_variables = {"s", "h", "d"}; std::vector<fvm_value_type> t0_values = {0.5, 0.2, 0.3}; std::vector<fvm_value_type> t1_0_values = {0.373297, 0.591621, 0.0350817}; std::vector<fvm_value_type> t1_1_values = {0.329897, 0.537371, 0.132732}; - run_test<multicore::backend>("test0_kin_compartment", state_variables, {}, t0_values, t1_0_values); - run_test<multicore::backend>("test1_kin_compartment", state_variables, {}, t0_values, t1_1_values); + run_test<multicore::backend>("test0_kin_compartment", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<multicore::backend>("test1_kin_compartment", state_variables, {}, t0_values, t1_1_values, 0.5); + } -TEST(mech_kinetic, kintetic_1_conserve) { +TEST(mech_kinetic, kintetic_linear_1_conserve) { std::vector<std::string> state_variables = {"s", "h", "d"}; std::vector<fvm_value_type> t0_values = {0.5, 0.2, 0.3}; std::vector<fvm_value_type> t1_0_values = {0.380338, 0.446414, 0.173247}; std::vector<fvm_value_type> t1_1_values = {0.218978, 0.729927, 0.0510949}; - run_test<multicore::backend>("test0_kin_diff", state_variables, {}, t0_values, t1_0_values); - run_test<multicore::backend>("test0_kin_conserve", state_variables, {}, t0_values, t1_0_values); - run_test<multicore::backend>("test0_kin_steadystate", state_variables, {}, t0_values, t1_1_values); + run_test<multicore::backend>("test0_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<multicore::backend>("test0_kin_conserve", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<multicore::backend>("test0_kin_steadystate", state_variables, {}, t0_values, t1_1_values, 0.5); } -TEST(mech_kinetic, kintetic_2_conserve) { +TEST(mech_kinetic, kintetic_linear_2_conserve) { std::vector<std::string> state_variables = {"a", "b", "x", "y"}; std::vector<fvm_value_type> t0_values = {0.2, 0.8, 0.6, 0.4}; std::vector<fvm_value_type> t1_0_values = {0.217391304, 0.782608696, 0.33333333, 0.66666666}; std::vector<fvm_value_type> t1_1_values = {0.230769, 0.769231, 0.189189, 0.810811}; - run_test<multicore::backend>("test1_kin_diff", state_variables, {}, t0_values, t1_0_values); - run_test<multicore::backend>("test1_kin_conserve", state_variables, {}, t0_values, t1_0_values); - run_test<multicore::backend>("test1_kin_steadystate", state_variables, {}, t0_values, t1_1_values); + run_test<multicore::backend>("test1_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<multicore::backend>("test1_kin_conserve", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<multicore::backend>("test1_kin_steadystate", state_variables, {}, t0_values, t1_1_values, 0.5); +} + +TEST(mech_kinetic, kintetic_nonlinear) { + std::vector<std::string> state_variables = {"a", "b", "c"}; + std::vector<fvm_value_type> t0_values = {0.2, 0.3, 0.5}; + std::vector<fvm_value_type> t1_0_values = {0.222881, 0.31144, 0.48856}; + std::vector<fvm_value_type> t1_1_values = {0.2078873133, 0.34222075, 0.45777925}; + + run_test<multicore::backend>("test2_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.025); + run_test<multicore::backend>("test3_kin_diff", state_variables, {}, t0_values, t1_1_values, 0.025); + +} + +TEST(mech_kinetic, kintetic_nonlinear_scaled) { + std::vector<std::string> state_variables = {"A", "B", "C", "d", "e"}; + std::vector<fvm_value_type> t0_values = {4.5, 6.6, 0.28, 2, 0}; + std::vector<fvm_value_type> t1_values = {4.087281958014442, + 6.224088678118931, + 0.6559113218810689, + 1.8315624742412617, + 0.16843752575873824}; + + run_test<multicore::backend>("test4_kin_compartment", state_variables, {}, t0_values, t1_values, 0.1); } -TEST(mech_linear, linear) { +TEST(mech_linear, linear_system) { std::vector<std::string> state_variables = {"h", "s", "d"}; std::vector<fvm_value_type> values = {0.5, 0.2, 0.3}; std::unordered_map<std::string, fvm_value_type> assigned_variables = {{"a0", 2.5}, {"a1",0.5}, {"a2",3}, {"a3",2.3}}; - run_test<multicore::backend>("test_linear_state", state_variables, assigned_variables, {}, values); - run_test<multicore::backend>("test_linear_init", state_variables, assigned_variables, values, {}); - run_test<multicore::backend>("test_linear_init_shuffle", state_variables, assigned_variables, values, {}); + run_test<multicore::backend>("test_linear_state", state_variables, assigned_variables, {}, values, 0.5); + run_test<multicore::backend>("test_linear_init", state_variables, assigned_variables, values, {}, 0.5); + run_test<multicore::backend>("test_linear_init_shuffle", state_variables, assigned_variables, values, {}, 0.5); } #ifdef ARB_GPU_ENABLED -TEST(mech_kinetic_gpu, kintetic_scaled) { +TEST(mech_kinetic_gpu, kintetic_linear_scaled) { std::vector<std::string> state_variables = {"s", "h", "d"}; std::vector<fvm_value_type> t0_values = {0.5, 0.2, 0.3}; std::vector<fvm_value_type> t1_0_values = {0.373297, 0.591621, 0.0350817}; std::vector<fvm_value_type> t1_1_values = {0.329897, 0.537371, 0.132732}; - run_test<gpu::backend>("test0_kin_compartment", state_variables, {}, t0_values, t1_0_values); - run_test<gpu::backend>("test1_kin_compartment", state_variables, {}, t0_values, t1_1_values); + run_test<gpu::backend>("test0_kin_compartment", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<gpu::backend>("test1_kin_compartment", state_variables, {}, t0_values, t1_1_values, 0.5); } -TEST(mech_kinetic_gpu, kintetic_1_conserve) { +TEST(mech_kinetic_gpu, kintetic_linear_1_conserve) { std::vector<std::string> state_variables = {"s", "h", "d"}; std::vector<fvm_value_type> t0_values = {0.5, 0.2, 0.3}; std::vector<fvm_value_type> t1_0_values = {0.380338, 0.446414, 0.173247}; std::vector<fvm_value_type> t1_1_values = {0.218978, 0.729927, 0.0510949}; - run_test<gpu::backend>("test0_kin_diff", state_variables, {}, t0_values, t1_0_values); - run_test<gpu::backend>("test0_kin_conserve", state_variables, {}, t0_values, t1_0_values); - run_test<gpu::backend>("test0_kin_steadystate", state_variables, {}, t0_values, t1_1_values); + run_test<gpu::backend>("test0_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<gpu::backend>("test0_kin_conserve", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<gpu::backend>("test0_kin_steadystate", state_variables, {}, t0_values, t1_1_values, 0.5); } -TEST(mech_kinetic_gpu, kintetic_2_conserve) { +TEST(mech_kinetic_gpu, kintetic_linear_2_conserve) { std::vector<std::string> state_variables = {"a", "b", "x", "y"}; std::vector<fvm_value_type> t0_values = {0.2, 0.8, 0.6, 0.4}; std::vector<fvm_value_type> t1_0_values = {0.217391304, 0.782608696, 0.33333333, 0.66666666}; std::vector<fvm_value_type> t1_1_values = {0.230769, 0.769231, 0.189189, 0.810811}; - run_test<gpu::backend>("test1_kin_diff", state_variables, {}, t0_values, t1_0_values); - run_test<gpu::backend>("test1_kin_conserve", state_variables, {}, t0_values, t1_0_values); - run_test<gpu ::backend>("test1_kin_steadystate", state_variables, {}, t0_values, t1_1_values); + run_test<gpu::backend>("test1_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<gpu::backend>("test1_kin_conserve", state_variables, {}, t0_values, t1_0_values, 0.5); + run_test<gpu::backend>("test1_kin_steadystate", state_variables, {}, t0_values, t1_1_values, 0.5); +} + +TEST(mech_kinetic, kintetic_nonlinear) { + std::vector<std::string> state_variables = {"a", "b", "c"}; + std::vector<fvm_value_type> t0_values = {0.2, 0.3, 0.5}; + std::vector<fvm_value_type> t1_0_values = {0.222881, 0.31144, 0.48856}; + std::vector<fvm_value_type> t1_1_values = {0.2078873133, 0.34222075, 0.45777925}; + + run_test<gpu::backend>("test2_kin_diff", state_variables, {}, t0_values, t1_0_values, 0.025); + run_test<gpu::backend>("test3_kin_diff", state_variables, {}, t0_values, t1_1_values, 0.025); +} + +TEST(mech_kinetic, kintetic_nonlinear_scaled) { + std::vector<std::string> state_variables = {"A", "B", "C", "d", "e"}; + std::vector<fvm_value_type> t0_values = {4.5, 6.6, 0.28, 2, 0}; + std::vector<fvm_value_type> t1_values = {4.087281958014442, + 6.224088678118931, + 0.6559113218810689, + 1.8315624742412617, + 0.16843752575873824}; + + run_test<gpu::backend>("test4_kin_compartment", state_variables, {}, t0_values, t1_values, 0.1); } -TEST(mech_linear_gpu, linear) { +TEST(mech_linear_gpu, linear_system) { std::vector<std::string> state_variables = {"h", "s", "d"}; std::vector<fvm_value_type> values = {0.5, 0.2, 0.3}; std::unordered_map<std::string, fvm_value_type> assigned_variables = {{"a0", 2.5},{"a1",0.5},{"a2",3},{"a3",2.3}}; - run_test<gpu::backend>("test_linear_state", state_variables, assigned_variables, {}, values); - run_test<gpu::backend>("test_linear_init", state_variables, assigned_variables, values, {}); - run_test<gpu::backend>("test_linear_init_shuffle", state_variables, assigned_variables, values, {}); + run_test<gpu::backend>("test_linear_state", state_variables, assigned_variables, {}, values, 0.5); + run_test<gpu::backend>("test_linear_init", state_variables, assigned_variables, values, {}, 0.5); + run_test<gpu::backend>("test_linear_init_shuffle", state_variables, assigned_variables, values, {}, 0.5); } #endif diff --git a/test/unit/unit_test_catalogue.cpp b/test/unit/unit_test_catalogue.cpp index f86219ac8efb48e8ba71973c0eea89e2a2396d33..e68bbdfbc056b62a83d861adadffb8fe32e8066d 100644 --- a/test/unit/unit_test_catalogue.cpp +++ b/test/unit/unit_test_catalogue.cpp @@ -19,6 +19,9 @@ #include "mechanisms/test1_kin_compartment.hpp" #include "mechanisms/test1_kin_diff.hpp" #include "mechanisms/test1_kin_conserve.hpp" +#include "mechanisms/test2_kin_diff.hpp" +#include "mechanisms/test3_kin_diff.hpp" +#include "mechanisms/test4_kin_compartment.hpp" #include "mechanisms/test1_kin_steadystate.hpp" #include "mechanisms/fixed_ica_current.hpp" #include "mechanisms/point_ica_current.hpp" @@ -58,8 +61,11 @@ mechanism_catalogue make_unit_test_catalogue() { ADD_MECH(cat, test0_kin_compartment) ADD_MECH(cat, test1_kin_diff) ADD_MECH(cat, test1_kin_conserve) + ADD_MECH(cat, test2_kin_diff) + ADD_MECH(cat, test3_kin_diff) ADD_MECH(cat, test1_kin_steadystate) ADD_MECH(cat, test1_kin_compartment) + ADD_MECH(cat, test4_kin_compartment) ADD_MECH(cat, fixed_ica_current) ADD_MECH(cat, point_ica_current) ADD_MECH(cat, linear_ca_conc)