File: units_visitor.cpp

package info (click to toggle)
nmodl 0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,016 kB
  • sloc: cpp: 28,492; javascript: 9,841; yacc: 2,804; python: 1,971; lex: 1,674; xml: 181; sh: 136; ansic: 37; makefile: 17; pascal: 7
file content (144 lines) | stat: -rw-r--r-- 5,617 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*************************************************************************
 * Copyright (C) 2018-2022 Blue Brain Project
 *
 * This file is part of NMODL distributed under the terms of the GNU
 * Lesser General Public License. See top-level LICENSE file for details.
 *************************************************************************/

#include "visitors/units_visitor.hpp"
#include "utils/string_utils.hpp"

#include "ast/all.hpp"

/**
 * \file
 * \brief AST Visitor to parse the ast::UnitDefs and ast::FactorDefs from the mod file
 * by the Units Parser used to parse the \c nrnunits.lib file
 */

namespace nmodl {
namespace visitor {

void UnitsVisitor::visit_program(ast::Program& node) {
    units_driver.parse_file(units_dir);
    node.visit_children(*this);
}

/**
 * \details units::Unit definition is based only on pre-defined units, parse only the
 * new unit and the pre-defined units. <br>
 * Example:
 * \code
 *      (nA)    = (nanoamp) => nA  nanoamp)
 * \endcode
 * The ast::UnitDef is converted to a string that is able to be parsed by the
 * unit parser which was used for parsing the \c nrnunits.lib file.
 * On \c nrnunits.lib constant "1" is defined as "fuzz", so it must be converted.
 */
void UnitsVisitor::visit_unit_def(ast::UnitDef& node) {
    std::ostringstream ss;
    /*
     * In nrnunits.lib file "1" is defined as "fuzz", so there
     * must be a conversion to be able to parse "1" as unit
     */
    if (node.get_unit2()->get_node_name() == "1") {
        ss << node.get_unit1()->get_node_name() << '\t';
        ss << UNIT_FUZZ;
    } else {
        ss << node.get_unit1()->get_node_name() << '\t' << node.get_unit2()->get_node_name();
    }

    // Parse the generated string for the defined unit using the units::UnitParser
    units_driver.parse_string(ss.str());
}

/**
 * \details The new unit definition is based on a factor combined with
 * units or other defined units.
 * In the first case the factor saved to the ast::FactorDef node and
 * printed to \c .cpp file is the one defined on the modfile. The factor
 * and the dimensions saved to the units::UnitTable are based on the
 * factor and the units defined in the modfile, so this factor will be
 * calculated based on the base units of the units::UnitTable. <br>
 * Example:
 * \code
 *      R = 8.314 (volt-coul/degC))
 * \endcode
 * In the second case, the factor and the dimensions that are inserted
 * to the units::UnitTable are based on the ast::FactorDef::unit1, like
 * in MOD2C.
 * \b However, the factor that is saved in the ast::FactorDef and printed
 * in the \c .cpp file is the factor of the ast::FactorDef::unit1 divided
 * by the factor of ast::FactorDef::unit2. <br>
 * Example:
 * \code
 *      R = (mole k) (mV-coulomb/degC)
 * \endcode
 * `unit1` is `mole k` and unit2 is `mV-coulomb/degC` <br>
 * To parse the units defined in modfiles there are stringstreams
 * created that are passed to the string parser, to be parsed by the
 * unit parser used for parsing the \c nrnunits.lib file, which takes
 * care of all the units calculations.
 */
void UnitsVisitor::visit_factor_def(ast::FactorDef& node) {
    const auto node_has_value_defined_in_modfile = node.get_value() != nullptr;
    if (!node_has_value_defined_in_modfile) {
        std::ostringstream ss_unit1, ss_unit2;
        std::string unit1_name, unit2_name;
        /*
         * In nrnunits.lib file "1" is defined as "fuzz", so
         * there must be a conversion to be able to parse "1" as unit
         */
        if (node.get_unit1()->get_node_name() == "1") {
            unit1_name = UNIT_FUZZ;
        } else {
            unit1_name = node.get_unit1()->get_node_name();
        }
        if (node.get_unit2()->get_node_name() == "1") {
            unit2_name = UNIT_FUZZ;
        } else {
            unit2_name = node.get_unit2()->get_node_name();
        }
        /*
         * Create dummy unit "node.get_node_name()_unit1" and parse
         * it to calculate its factor
         */
        ss_unit1 << node.get_node_name() << "_unit1\t" << unit1_name;
        units_driver.parse_string(ss_unit1.str());
        /*
         * Create dummy unit "node.get_node_name()_unit2" and parse
         * it to calculate its factor
         */
        ss_unit2 << node.get_node_name() << "_unit2\t" << unit2_name;
        units_driver.parse_string(ss_unit2.str());

        /**
         * \note If the ast::FactorDef was made by using two units (second case),
         * the factors of both of them must be calculated based on the
         * units::UnitTable and then they must be divided to produce the unit's
         * factor that will be printed to the \c .cpp file. <br>
         * Example:
         * \code
         *      FARADAY = (faraday) (10000 coulomb)
         * \endcode
         * In the \c .cpp file the printed factor will be `9.64853090` but in the
         * units::UnitTable the factor of `FARADAY` will be `96485.30900000`
         */

        auto node_unit_name = node.get_node_name();
        auto unit1_factor = units_driver.table->get_unit(node_unit_name + "_unit1")->get_factor();
        auto unit2_factor = units_driver.table->get_unit(node_unit_name + "_unit2")->get_factor();

#ifdef USE_LEGACY_UNITS
        auto unit_factor = stringutils::to_string(unit1_factor / unit2_factor, "{:g}");
#else
        auto unit_factor = stringutils::to_string(unit1_factor / unit2_factor, "{:a}");
#endif

        auto double_value_ptr = std::make_shared<ast::Double>(ast::Double(unit_factor));
        node.set_value(std::move(double_value_ptr));
    }
}

}  // namespace visitor
}  // namespace nmodl