User Guide
units.hpp
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 #pragma once
9 
10 /**
11  * \dir
12  * \brief Data structures for storing units
13  *
14  * \file
15  * \brief Classes for storing different units specification
16  */
17 
18 #include <array>
19 #include <cmath>
20 #include <fstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <regex>
24 #include <sstream>
25 #include <string>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29 
30 
31 namespace nmodl {
32 /// encapsulates unit database and tables implementation
33 namespace units {
34 
35 /**
36  * @defgroup units Unit Implementation
37  * @brief Units handling implementation details
38  * @{
39  */
40 
41 /// Maximum number of dimensions of units (maximum number of base units)
42 static constexpr int MAX_DIMS = 10;
43 
44 /**
45  * \class Unit
46  * \brief Class that stores all the data of a Unit
47  *
48  * The Unit class encapsulates all the variables and containers that are related to
49  * the definition of a unit. Those are:
50  * - Unit factor
51  * - Unit dimensions
52  * - Unit name
53  * - Nominator units
54  * - Denominator units
55  *
56  * The unit factor is calculated based on the unit factor if it is stated and the
57  * factors of the units that describe the units and are on the nominator or the
58  * denominator of the unit. The calculation of the factor is done by the UnitTable
59  * class, as it is needed to check the factors and dimensions of the units that
60  * this unit is based upon and are stored into the UnitTable table that keeps all
61  * the units parsed from a units file (nrnunits.lib by default) or the mod files.
62  * The dimensions of the unit represent which basic units are used to define this
63  * unit. They are represented by an array of ints of size MAX_DIMS. An array was
64  * used, as the base units do not change based on the SI units. If a base unit is
65  * used in this unit's definition then there is a 1 in the responding or else 0.
66  * If a unit is on the nominator of the unit definition, then its factor is
67  * multiplied by the unit's factor, while if it is on the denominator, its factor
68  * is divided by the unit's factor. The dimensions of the nominator units are added
69  * to the dimensions of the units and the dimensions of the denominator are
70  * subtracted from the unit's dimensions. The Unit representation is designed based
71  * on the units representation of the MOD2C parser.
72  */
73 class Unit {
74  private:
75  /// Double factor of the Unit
76  double unit_factor = 1.0;
77 
78  /// Array of MAX_DIMS size that keeps the Unit's dimensions
79  std::array<int, MAX_DIMS> unit_dimensions{{0}};
80 
81  /// Name of the Unit
82  std::string unit_name;
83 
84  /// Vector of nominators of the Unit
85  std::vector<std::string> nominator;
86 
87  /// Vector of denominators of the Unit
88  std::vector<std::string> denominator;
89 
90  public:
91  /// \name Ctor & dtor
92  /// \{
93 
94  /// Default constructor of Unit
95  Unit() = default;
96 
97  /// Constructor for simply creating a Unit with a given name
98  explicit Unit(std::string name)
99  : unit_name(std::move(name)) {}
100 
101  /// Constructor that instantiates a Unit with its factor, dimensions and name
102  Unit(const double factor, const std::array<int, MAX_DIMS>& dimensions, std::string name)
103  : unit_factor(factor)
104  , unit_dimensions(dimensions)
105  , unit_name(std::move(name)) {}
106 
107  /// \}
108 
109  /// Add unit name to the Unit
110  void add_unit(const std::string& name);
111 
112  /// If the Unit is a base unit the dimensions of the Unit should be calculated
113  /// based on the name of the base unit (ex. m \*a\* => m has dimension 0)
114  void add_base_unit(const std::string& name);
115 
116  /// Takes as argument a double as string, parses it as double and stores it to
117  /// the Unit factor
118  void add_nominator_double(const std::string& double_string);
119 
120  /// Add the dimensions of a nominator of the unit to the dimensions of the Unit
121  void add_nominator_dims(const std::array<int, MAX_DIMS>& dimensions);
122 
123  /// Subtract the dimensions of a nominator of the unit to the dimensions of the Unit
124  void add_denominator_dims(const std::array<int, MAX_DIMS>& dimensions);
125 
126  /// Add a unit to the vector of nominator strings of the Unit, so it can be processed
127  /// later
128  void add_nominator_unit(const std::string& nom);
129 
130  /// Add a vector of units to the vector of nominator strings of the Unit, so they can
131  /// be processed later
132  void add_nominator_unit(const std::shared_ptr<std::vector<std::string>>& nom);
133 
134  /// Add a unit to the vector of denominator strings of the Unit, so it can be processed
135  /// later
136  void add_denominator_unit(const std::string& denom);
137 
138  /// Add a vector of units to the vector of denominator strings of the Unit, so they can
139  /// be processed later
140  void add_denominator_unit(const std::shared_ptr<std::vector<std::string>>& denom);
141 
142  /// Multiply Unit's factor with a double factor
143  void mul_factor(double double_factor);
144 
145  /// Parse a fraction given as string and store the result to the factor of the Unit
146  void add_fraction(const std::string& nominator, const std::string& denominator);
147 
148  /// Parse a double number given as string. The double can be positive or negative and
149  /// have all kinds of representations
150  static double parse_double(std::string double_string);
151 
152  /// Getter for the vector of nominators of the Unit
153  const std::vector<std::string>& get_nominator_unit() const noexcept {
154  return nominator;
155  }
156 
157  /// Getter for the vector of denominators of the Unit
158  const std::vector<std::string>& get_denominator_unit() const noexcept {
159  return denominator;
160  }
161 
162  /// Getter for the name of the Unit
163  const std::string& get_name() const noexcept {
164  return unit_name;
165  }
166 
167  /// Getter for the double factor of the Unit
168  double get_factor() const {
169  return unit_factor;
170  }
171 
172  /// Getter for the array of Unit's dimensions
173  const std::array<int, MAX_DIMS>& get_dimensions() const noexcept {
174  return unit_dimensions;
175  }
176 };
177 
178 /**
179  * \class Prefix
180  * \brief Class that stores all the data of a prefix
181  *
182  * Prefixes of units can also be defined in the units file. Those
183  * prefixes are then checked during a unit's insertion to the UnitTable
184  * to multiply their factor to the unit's factor.
185  *
186  */
187 class Prefix {
188  private:
189  /// Prefix's double factor
190  double prefix_factor = 1;
191 
192  /// Prefix's name
193  std::string prefix_name;
194 
195  public:
196  /// \name Ctor & dtor
197  /// \{
198 
199  /// Default constructor for Prefix
200  Prefix() = delete;
201 
202  /// Constructor that instantiates a Prefix with its name and factor
203  Prefix(std::string name, const std::string& factor);
204 
205  /// \}
206 
207  /// Getter for the name of the Prefix
208  const std::string& get_name() const noexcept {
209  return prefix_name;
210  }
211 
212  /// Getter for the factor of the Prefix
213  double get_factor() const noexcept {
214  return prefix_factor;
215  }
216 };
217 
218 /**
219  * \class UnitTable
220  * \brief Class that stores all the units, prefixes and names of base units used
221  *
222  * The UnitTable encapsulates all the containers that are needed to store all the
223  * Units defined. Those containers are:
224  * - A table (hash map) that stores all the Units
225  * - A hash map that stores all the Prefixes
226  * - An array to store the base units names
227  *
228  * The names and the design is based on the corresponding ones of MOD2C.
229  * The table is a hash map that uses as key the name of the Unit and as objects
230  * a shared_ptr to a Unit. This is needed as the parser passes to the UnitTable
231  * shared_ptrs, so that there is no need to allocate more space for the units or
232  * take care of the deallocation of the memory, which is done automatically by
233  * the smart pointer.
234  * A shared_ptr to a UnitTable is kept by the UnitDriver, to be able to populate the
235  * UnitTable by using multiple sources (files and strings).
236  * The UnitTable takes care of inserting Units, Prefixes and base units to the table
237  * calculating or the needed factors and dimensions.
238  *
239  */
240 class UnitTable {
241  private:
242  /// Hash map that stores all the Units
243  std::unordered_map<std::string, std::shared_ptr<Unit>> table;
244 
245  /// Hash map that stores all the Prefixes
246  std::unordered_map<std::string, double> prefixes;
247 
248  /// Hash map that stores all the base units' names
249  std::array<std::string, MAX_DIMS> base_units_names;
250 
251  public:
252  /// Default constructor for UnitTable
253  UnitTable() = default;
254 
255  /// Calculate unit's dimensions based on its nominator unit named nominator_name which is
256  /// stored in the UnitTable's table
257  void calc_nominator_dims(const std::shared_ptr<Unit>& unit, std::string nominator_name);
258 
259  /// Calculate unit's dimensions based on its denominator unit named denominator_name which is
260  /// stored in the UnitTable's table
261  void calc_denominator_dims(const std::shared_ptr<Unit>& unit, std::string denominator_name);
262 
263  /// Insert a unit to the UnitTable table and calculate its dimensions and factor based on the
264  /// previously stored units in the UnitTable
265  /// The unit can be a normal unit or unit based on just a base unit. In the latter case, the
266  /// base unit is also added to the base_units_name array of UnitTable
267  void insert(const std::shared_ptr<Unit>& unit);
268 
269  /// Insert a prefix to the prefixes of the UnitTable
270  void insert_prefix(const std::shared_ptr<Prefix>& prfx);
271 
272  /// Get the unit_name of the UnitTable's table
273  /// \throw std::out_of_range if \a unit_name is not found
274  const std::shared_ptr<Unit>& get_unit(const std::string& unit_name) const {
275  return table.at(unit_name);
276  }
277 
278  /// Print the details of the units that are stored in the UnitTable
279  /// to the output stream units_details in ascending order to be printed
280  /// in tests in specific order
281  void print_units_sorted(std::ostream& units_details) const;
282 
283  /// Print the base units that are stored in the UnitTable to the
284  /// output stream base_units_details
285  void print_base_units(std::ostream& base_units_details) const;
286 
287  /// Get base unit name based on the ID number of the dimension
288  const std::string& get_base_unit_name(int id) const noexcept {
289  return base_units_names[id];
290  }
291 };
292 
293 /** @} */ // end of units
294 
295 } // namespace units
296 } // namespace nmodl
nmodl::units::Unit::add_nominator_unit
void add_nominator_unit(const std::string &nom)
Add a unit to the vector of nominator strings of the Unit, so it can be processed later.
Definition: units.cpp:72
nmodl::units::Unit
Class that stores all the data of a Unit.
Definition: units.hpp:73
nmodl::units::UnitTable::UnitTable
UnitTable()=default
Default constructor for UnitTable.
nmodl::units::UnitTable::get_base_unit_name
const std::string & get_base_unit_name(int id) const noexcept
Get base unit name based on the ID number of the dimension.
Definition: units.hpp:288
nmodl::units::Unit::get_denominator_unit
const std::vector< std::string > & get_denominator_unit() const noexcept
Getter for the vector of denominators of the Unit.
Definition: units.hpp:158
nmodl::units::Unit::get_factor
double get_factor() const
Getter for the double factor of the Unit.
Definition: units.hpp:168
nmodl::units::MAX_DIMS
static constexpr int MAX_DIMS
Maximum number of dimensions of units (maximum number of base units)
Definition: units.hpp:42
nmodl::units::UnitTable::table
std::unordered_map< std::string, std::shared_ptr< Unit > > table
Hash map that stores all the Units.
Definition: units.hpp:243
nmodl::units::Unit::unit_dimensions
std::array< int, MAX_DIMS > unit_dimensions
Array of MAX_DIMS size that keeps the Unit's dimensions.
Definition: units.hpp:79
nmodl::units::UnitTable::prefixes
std::unordered_map< std::string, double > prefixes
Hash map that stores all the Prefixes.
Definition: units.hpp:246
nmodl::units::Prefix::prefix_factor
double prefix_factor
Prefix's double factor.
Definition: units.hpp:190
nmodl::units::UnitTable::insert
void insert(const std::shared_ptr< Unit > &unit)
Insert a unit to the UnitTable table and calculate its dimensions and factor based on the previously ...
Definition: units.cpp:267
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
nmodl::units::Unit::add_nominator_dims
void add_nominator_dims(const std::array< int, MAX_DIMS > &dimensions)
Add the dimensions of a nominator of the unit to the dimensions of the Unit.
Definition: units.cpp:56
nmodl::units::Unit::get_nominator_unit
const std::vector< std::string > & get_nominator_unit() const noexcept
Getter for the vector of nominators of the Unit.
Definition: units.hpp:153
nmodl::units::Prefix::get_factor
double get_factor() const noexcept
Getter for the factor of the Prefix.
Definition: units.hpp:213
nmodl::units::Unit::add_denominator_dims
void add_denominator_dims(const std::array< int, MAX_DIMS > &dimensions)
Subtract the dimensions of a nominator of the unit to the dimensions of the Unit.
Definition: units.cpp:64
nmodl::units::Unit::unit_factor
double unit_factor
Double factor of the Unit.
Definition: units.hpp:76
nmodl::units::Unit::mul_factor
void mul_factor(double double_factor)
Multiply Unit's factor with a double factor.
Definition: units.cpp:88
nmodl::units::Unit::nominator
std::vector< std::string > nominator
Vector of nominators of the Unit.
Definition: units.hpp:85
nmodl::units::Prefix::Prefix
Prefix()=delete
Default constructor for Prefix.
nmodl::units::Unit::add_unit
void add_unit(const std::string &name)
Add unit name to the Unit.
Definition: units.cpp:38
nmodl::units::UnitTable::base_units_names
std::array< std::string, MAX_DIMS > base_units_names
Hash map that stores all the base units' names.
Definition: units.hpp:249
nmodl::units::Unit::unit_name
std::string unit_name
Name of the Unit.
Definition: units.hpp:82
nmodl::units::UnitTable::calc_denominator_dims
void calc_denominator_dims(const std::shared_ptr< Unit > &unit, std::string denominator_name)
Calculate unit's dimensions based on its denominator unit named denominator_name which is stored in t...
Definition: units.cpp:192
nmodl::units::Prefix::prefix_name
std::string prefix_name
Prefix's name.
Definition: units.hpp:193
nmodl::units::Unit::Unit
Unit(const double factor, const std::array< int, MAX_DIMS > &dimensions, std::string name)
Constructor that instantiates a Unit with its factor, dimensions and name.
Definition: units.hpp:102
nmodl::units::UnitTable::print_units_sorted
void print_units_sorted(std::ostream &units_details) const
Print the details of the units that are stored in the UnitTable to the output stream units_details in...
Definition: units.cpp:311
nmodl::units::Unit::Unit
Unit()=default
Default constructor of Unit.
nmodl::units::UnitTable::calc_nominator_dims
void calc_nominator_dims(const std::shared_ptr< Unit > &unit, std::string nominator_name)
Calculate unit's dimensions based on its nominator unit named nominator_name which is stored in the U...
Definition: units.cpp:115
nmodl::units::UnitTable::print_base_units
void print_base_units(std::ostream &base_units_details) const
Print the base units that are stored in the UnitTable to the output stream base_units_details.
Definition: units.cpp:323
nmodl::units::Unit::Unit
Unit(std::string name)
Constructor for simply creating a Unit with a given name.
Definition: units.hpp:98
nmodl::units::UnitTable::get_unit
const std::shared_ptr< Unit > & get_unit(const std::string &unit_name) const
Get the unit_name of the UnitTable's table.
Definition: units.hpp:274
nmodl::units::UnitTable
Class that stores all the units, prefixes and names of base units used.
Definition: units.hpp:240
nmodl::units::Unit::add_denominator_unit
void add_denominator_unit(const std::string &denom)
Add a unit to the vector of denominator strings of the Unit, so it can be processed later.
Definition: units.cpp:80
nmodl::units::UnitTable::insert_prefix
void insert_prefix(const std::shared_ptr< Prefix > &prfx)
Insert a prefix to the prefixes of the UnitTable.
Definition: units.cpp:307
nmodl::units::Unit::denominator
std::vector< std::string > denominator
Vector of denominators of the Unit.
Definition: units.hpp:88
nmodl::units::Unit::get_name
const std::string & get_name() const noexcept
Getter for the name of the Unit.
Definition: units.hpp:163
nmodl::units::Unit::add_fraction
void add_fraction(const std::string &nominator, const std::string &denominator)
Parse a fraction given as string and store the result to the factor of the Unit.
Definition: units.cpp:92
nmodl::units::Unit::parse_double
static double parse_double(std::string double_string)
Parse a double number given as string.
Definition: units.cpp:105
nmodl::units::Unit::add_nominator_double
void add_nominator_double(const std::string &double_string)
Takes as argument a double as string, parses it as double and stores it to the Unit factor.
Definition: units.cpp:52
nmodl::units::Unit::add_base_unit
void add_base_unit(const std::string &name)
If the Unit is a base unit the dimensions of the Unit should be calculated based on the name of the b...
Definition: units.cpp:42
nmodl::units::Unit::get_dimensions
const std::array< int, MAX_DIMS > & get_dimensions() const noexcept
Getter for the array of Unit's dimensions.
Definition: units.hpp:173
nmodl::units::Prefix::get_name
const std::string & get_name() const noexcept
Getter for the name of the Prefix.
Definition: units.hpp:208
nmodl::units::Prefix
Class that stores all the data of a prefix.
Definition: units.hpp:187