diff --git a/arborio/cableio.cpp b/arborio/cableio.cpp index 5fde6ff6986d9927cb528d9feac2e0bad42382d4..469fe96d4036708351ff5013e738237fa4d59e2d 100644 --- a/arborio/cableio.cpp +++ b/arborio/cableio.cpp @@ -8,6 +8,7 @@ #include <arborio/label_parse.hpp> #include <arborio/cableio.hpp> +#include <arborio/cv_policy_parse.hpp> #include "parse_helpers.hpp" #include "parse_s_expr.hpp" @@ -88,7 +89,7 @@ s_expr mksexp(const msegment& seg) { s_expr mksexp(const cv_policy& c) { std::stringstream s; s << c; - return parse_s_expr(s.str()); + return slist("cv-policy"_symbol, parse_s_expr(s.str())); } s_expr mksexp(const decor& d) { auto round_trip = [](auto& x) { @@ -217,6 +218,9 @@ arb::i_clamp make_i_clamp_pulse(pulse_tuple p, double freq, double phase) { arb::gap_junction_site make_gap_junction_site() { return arb::gap_junction_site{}; } +arb::cv_policy make_cv_policy(const cv_policy& p) { + return p; +} arb::ion_reversal_potential_method make_ion_reversal_potential_method(const std::string& ion, const arb::mechanism_desc& mech) { return ion_reversal_potential_method{ion, mech}; } @@ -536,17 +540,19 @@ parse_hopefully<std::any> eval(const s_expr& e, const eval_map& map, const eval_ } } - // If it's not in the provided map, maybe it's a label expression - // the corresponding parser is provided by the arbor lib + // If it's not in the provided map, it could be a label expression if (auto l = parse_label_expression(e)) { if (match<region>(l->type())) return eval_cast<region>(l.value()); if (match<locset>(l->type())) return eval_cast<locset>(l.value()); } + // Or it could be a cv-policy expression + if (auto p = parse_cv_policy_expression(e)) return p.value(); + // Unable to find a match: try to return a helpful error message. const auto nc = std::distance(matches.first, matches.second); std::string msg = "No matches for found for "+name+" with "+std::to_string(args->size())+" arguments.\n" - "There are "+std::to_string(nc)+" potential candiates"+(nc?":":"."); + "There are "+std::to_string(nc)+" potential candidates"+(nc?":":"."); int count = 0; for (auto i=matches.first; i!=matches.second; ++i) { msg += "\n Candidate "+std::to_string(++count)+": "+i->second.message; @@ -558,34 +564,36 @@ parse_hopefully<std::any> eval(const s_expr& e, const eval_map& map, const eval_ eval_map named_evals{ {"membrane-potential", make_call<double>(make_init_membrane_potential, - "'membrane-potential' with 1 argument (val:real)")}, + "'membrane-potential' with 1 argument (val:real)")}, {"temperature-kelvin", make_call<double>(make_temperature_K, - "'temperature-kelvin' with 1 argument (val:real)")}, + "'temperature-kelvin' with 1 argument (val:real)")}, {"axial-resistivity", make_call<double>(make_axial_resistivity, - "'axial-resistivity' with 1 argument (val:real)")}, + "'axial-resistivity' with 1 argument (val:real)")}, {"membrane-capacitance", make_call<double>(make_membrane_capacitance, - "'membrane-capacitance' with 1 argument (val:real)")}, + "'membrane-capacitance' with 1 argument (val:real)")}, {"ion-internal-concentration", make_call<std::string, double>(make_init_int_concentration, - "'ion_internal_concentration' with 2 arguments (ion:string val:real)")}, + "'ion_internal_concentration' with 2 arguments (ion:string val:real)")}, {"ion-external-concentration", make_call<std::string, double>(make_init_ext_concentration, - "'ion_external_concentration' with 2 arguments (ion:string val:real)")}, + "'ion_external_concentration' with 2 arguments (ion:string val:real)")}, {"ion-reversal-potential", make_call<std::string, double>(make_init_reversal_potential, - "'ion_reversal_potential' with 2 arguments (ion:string val:real)")}, + "'ion_reversal_potential' with 2 arguments (ion:string val:real)")}, {"envelope", make_arg_vec_call<envelope_tuple>(make_envelope, - "`envelope` with one or more pairs of start time and amplitude (start:real amplitude:real)")}, + "'envelope' with one or more pairs of start time and amplitude (start:real amplitude:real)")}, {"envelope-pulse", make_call<double, double, double>(make_envelope_pulse, - "'envelope-pulse' with 3 arguments (delay:real duration:real amplitude:real)")}, + "'envelope-pulse' with 3 arguments (delay:real duration:real amplitude:real)")}, {"current-clamp", make_call<std::vector<arb::i_clamp::envelope_point>, double, double>(make_i_clamp, - "`current-clamp` with 3 arguments (env:envelope freq:real phase:real)")}, + "'current-clamp' with 3 arguments (env:envelope freq:real phase:real)")}, {"current-clamp", make_call<pulse_tuple, double, double>(make_i_clamp_pulse, - "`current-clamp` with 3 arguments (env:envelope_pulse freq:real phase:real)")}, + "'current-clamp' with 3 arguments (env:envelope_pulse freq:real phase:real)")}, {"threshold-detector", make_call<double>(make_threshold_detector, - "'threshold-detector' with 1 argument (threshold:real)")}, + "'threshold-detector' with 1 argument (threshold:real)")}, {"gap-junction-site", make_call<>(make_gap_junction_site, - "'gap-junction-site' with 0 arguments")}, + "'gap-junction-site' with 0 arguments")}, {"ion-reversal-potential-method", make_call<std::string, arb::mechanism_desc>( - make_ion_reversal_potential_method, - "'ion-reversal-potential-method' with 2 arguments (ion:string mech:mechanism)")}, + make_ion_reversal_potential_method, + "'ion-reversal-potential-method' with 2 arguments (ion:string mech:mechanism)")}, + {"cv-policy", make_call<cv_policy>(make_cv_policy, + "'cv-policy' with 1 argument (p:policy)")}, {"mechanism", make_mech_call("'mechanism' with a name argument, and 0 or more parameter settings" "(name:string (param:string val:real))")}, {"place", make_call<locset, gap_junction_site, std::string>(make_place, "'place' with 3 arguments (ls:locset gj:gap-junction-site name:string)")}, @@ -610,28 +618,29 @@ eval_map named_evals{ {"default", make_call<init_ext_concentration>(make_default, "'default' with 1 argument (v:ion-external-concentration)")}, {"default", make_call<init_reversal_potential>(make_default, "'default' with 1 argument (v:ion-reversal-potential)")}, {"default", make_call<ion_reversal_potential_method>(make_default, "'default' with 1 argument (v:ion-reversal-potential-method)")}, + {"default", make_call<cv_policy>(make_default, "'default' with 1 argument (v:cv-policy)")}, {"locset-def", make_call<std::string, locset>(make_locset_pair, - "'locset-def' with 2 arguments (name:string ls:locset)")}, + "'locset-def' with 2 arguments (name:string ls:locset)")}, {"region-def", make_call<std::string, region>(make_region_pair, - "'region-def' with 2 arguments (name:string reg:region)")}, + "'region-def' with 2 arguments (name:string reg:region)")}, {"point", make_call<double, double, double, double>(make_point, - "'point' with 4 arguments (x:real y:real z:real radius:real)")}, + "'point' with 4 arguments (x:real y:real z:real radius:real)")}, {"segment", make_call<int, mpoint, mpoint, int>(make_segment, - "'segment' with 4 arguments (parent:int prox:point dist:point tag:int)")}, + "'segment' with 4 arguments (parent:int prox:point dist:point tag:int)")}, {"branch", make_branch_call( - "'branch' with 2 integers and 1 or more segment arguments (id:int parent:int s0:segment s1:segment ..)")}, + "'branch' with 2 integers and 1 or more segment arguments (id:int parent:int s0:segment s1:segment ..)")}, {"decor", make_arg_vec_call<place_tuple, paint_pair, defaultable>(make_decor, - "'decor' with 1 or more `paint`, `place` or `default` arguments")}, + "'decor' with 1 or more `paint`, `place` or `default` arguments")}, {"label-dict", make_arg_vec_call<locset_pair, region_pair>(make_label_dict, - "'label-dict' with 1 or more `locset-def` or `region-def` arguments")}, + "'label-dict' with 1 or more `locset-def` or `region-def` arguments")}, {"morphology", make_arg_vec_call<branch_tuple>(make_morphology, - "'morphology' 1 or more `branch` arguments")}, + "'morphology' 1 or more `branch` arguments")}, {"cable-cell", make_unordered_call<morphology, label_dict, decor>(make_cable_cell, - "'cable-cell' with 3 arguments: `morphology`, `label-dict`, and `decor` in any order")}, + "'cable-cell' with 3 arguments: `morphology`, `label-dict`, and `decor` in any order")}, {"version", make_call<std::string>(make_version, "'version' with one argment (val:std::string)")}, {"meta-data", make_call<version_tuple>(make_meta_data, "'meta-data' with one argument (v:version)")}, diff --git a/arborio/cv_policy_parse.cpp b/arborio/cv_policy_parse.cpp index c013efe751499b4c34b3b0f1dba052cdb6ad8656..16ad8160861eae2b714f860e5a415052398c07d0 100644 --- a/arborio/cv_policy_parse.cpp +++ b/arborio/cv_policy_parse.cpp @@ -188,8 +188,8 @@ parse_hopefully<std::any> eval(const s_expr& e) { } } -parse_cv_policy_hopefully parse_cv_policy_expression(const std::string& s) { - if (auto e = eval(parse_s_expr(s))) { +parse_cv_policy_hopefully parse_cv_policy_expression(const arb::s_expr& s) { + if (auto e = eval(s)) { if (e->type() == typeid(cv_policy)) { return {std::move(std::any_cast<cv_policy&>(*e))}; } @@ -200,5 +200,7 @@ parse_cv_policy_hopefully parse_cv_policy_expression(const std::string& s) { return util::unexpected(cv_policy_parse_error(std::string() + e.error().what())); } } - +parse_cv_policy_hopefully parse_cv_policy_expression(const std::string& s) { + return parse_cv_policy_expression(parse_s_expr(s)); +} } // namespace arb diff --git a/arborio/include/arborio/cv_policy_parse.hpp b/arborio/include/arborio/cv_policy_parse.hpp index 8a899d468312426b02cfaf1558cf7da4735933cf..6069740a9f509097356682ac5cd478642b1368d8 100644 --- a/arborio/include/arborio/cv_policy_parse.hpp +++ b/arborio/include/arborio/cv_policy_parse.hpp @@ -18,6 +18,7 @@ struct cv_policy_parse_error: arb::arbor_exception { using parse_cv_policy_hopefully = arb::util::expected<arb::cv_policy, cv_policy_parse_error>; parse_cv_policy_hopefully parse_cv_policy_expression(const std::string& s); +parse_cv_policy_hopefully parse_cv_policy_expression(const arb::s_expr& s); namespace literals { @@ -29,4 +30,4 @@ arb::cv_policy operator "" _cvp(const char* s, std::size_t) { } // namespace literals -} // namespace arb +} // namespace arborio diff --git a/test/unit/test_s_expr.cpp b/test/unit/test_s_expr.cpp index 477fc8e94aaf19f6b6d25230ea517c79bbc51cac..fcae3e4ee2c29aca6872ebde02f6122da1d6e47f 100644 --- a/test/unit/test_s_expr.cpp +++ b/test/unit/test_s_expr.cpp @@ -6,8 +6,8 @@ #include <arbor/morph/region.hpp> #include <arbor/morph/locset.hpp> #include <arbor/cv_policy.hpp> - #include <arbor/s_expr.hpp> +#include <arbor/util/any_visitor.hpp> #include <arborio/cv_policy_parse.hpp> #include <arborio/cableio.hpp> @@ -514,8 +514,11 @@ std::ostream& operator<<(std::ostream& o, const place_tuple& p) { return o << " \"" << std::get<2>(p) << "\")"; } std::ostream& operator<<(std::ostream& o, const defaultable& p) { + auto default_visitor = arb::util::overload( + [&](const cv_policy& p) { o << "(cv-policy " << p << ")"; }, + [&](const auto& p){ o << p; }); o << "(default "; - std::visit([&](auto&& x) {o << x;}, p); + std::visit(default_visitor, p); return o << ")"; } std::ostream& operator<<(std::ostream& o, const locset_pair& p) { @@ -531,6 +534,11 @@ std::string to_string(const T& obj) { s << obj; return s.str(); } +std::string to_string(const cv_policy& p) { + std::stringstream s; + s << "(cv-policy " << p << ')'; + return s.str(); +} std::string to_string(const arborio::cable_cell_component& c) { std::stringstream s; arborio::write_component(s, c); @@ -598,7 +606,9 @@ TEST(decor_literals, round_tripping) { "(mechanism \"pas\" (\"g\" 0.02))", }; auto default_literals = { - "(ion-reversal-potential-method \"ca\" (mechanism \"nernst/ca\"))"}; + "(ion-reversal-potential-method \"ca\" (mechanism \"nernst/ca\"))", + "(cv-policy (single (segment 0)))" + }; auto place_literals = { "(current-clamp (envelope (10 0.5) (110 0.5) (110 0)) 10 0.25)", "(threshold-detector -10)", @@ -658,7 +668,8 @@ TEST(decor_expressions, round_tripping) { "(default (ion-internal-concentration \"ca\" 75.1))", "(default (ion-external-concentration \"h\" -50.1))", "(default (ion-reversal-potential \"na\" 30))", - "(default (ion-reversal-potential-method \"ca\" (mechanism \"nernst/ca\")))" + "(default (ion-reversal-potential-method \"ca\" (mechanism \"nernst/ca\")))", + "(default (cv-policy (max-extent 2 (region \"soma\") 2)))" }; auto decorate_place_literals = { "(place (location 3 0.2) (current-clamp (envelope (10 0.5) (110 0.5) (110 0)) 0.5 0.25) \"clamp\")", @@ -724,6 +735,11 @@ TEST(decor, round_tripping) { " (default \n" " (ion-reversal-potential-method \"na\" \n" " (mechanism \"nernst\")))\n" + " (default \n" + " (cv-policy \n" + " (fixed-per-branch 10 \n" + " (all)\n" + " 1)))\n" " (paint \n" " (region \"dend\")\n" " (mechanism \"pas\"))\n"