SST  15.1.0
StructuralSimulationToolkit
unitAlgebra.h
1 // -*- c++ -*-
2 
3 // Copyright 2009-2025 NTESS. Under the terms
4 // of Contract DE-NA0003525 with NTESS, the U.S.
5 // Government retains certain rights in this software.
6 //
7 // Copyright (c) 2009-2025, NTESS
8 // All rights reserved.
9 //
10 // This file is part of the SST software package. For license
11 // information, see the LICENSE file in the top level directory of the
12 // distribution.
13 
14 #ifndef SST_CORE_UNITALGEBRA_H
15 #define SST_CORE_UNITALGEBRA_H
16 
17 #include "sst/core/decimal_fixedpoint.h"
18 #include "sst/core/serialization/objectMap.h"
19 #include "sst/core/serialization/serialize.h"
20 #include "sst/core/serialization/serializer.h"
21 #include "sst/core/sst_types.h"
22 #include "sst/core/warnmacros.h"
23 
24 #include <cstdint>
25 #include <map>
26 #include <mutex>
27 #include <ostream>
28 #include <string>
29 #include <vector>
30 
31 namespace SST {
32 
33 using sst_big_num = decimal_fixedpoint<3, 3>;
34 
35 /**
36  * Helper class internal to UnitAlgebra.
37  *
38  * Contains information on valid units
39  */
40 class Units
41 {
42 
43  using unit_id_t = uint8_t;
44 
45 private:
46  friend class UnitAlgebra;
47 
48  // Static data members and functions
49  static std::recursive_mutex unit_lock;
50  static std::map<std::string, unit_id_t> valid_base_units;
51  static std::map<std::string, std::pair<Units, sst_big_num>> valid_compound_units;
52  static std::map<unit_id_t, std::string> unit_strings;
53  static unit_id_t count;
54 
55  // Non-static data members and functions
56  std::vector<unit_id_t> numerator;
57  std::vector<unit_id_t> denominator;
58 
59  void reduce();
60  // Used in constructor to incrementally build up unit from string
61  void addUnit(const std::string&, sst_big_num& multiplier, bool invert);
62 
63 public:
64  // Static data members and functions
65  /** Create a new Base Unit type */
66  static void registerBaseUnit(const std::string& u);
67  /** Create a new Compound Unit type */
68  static void registerCompoundUnit(const std::string& u, const std::string& v);
69 
70  // Non-static data members and functions
71  /** Create a new instantiation of a Units with a base unit string, and multiplier
72  * @param units String representing the new unit
73  * @param multiplier Value by which to multiply to get to this unit
74  */
75  Units(const std::string& units, sst_big_num& multiplier);
76  Units() {}
77  virtual ~Units() {}
78 
79  /** Copy constructor */
80  Units(const Units&) = default;
81 
82  /** Assignment operator */
83  Units& operator=(const Units& v);
84  /** Self-multiplication operator */
85  Units& operator*=(const Units& v);
86  /** Self-division operator */
87  Units& operator/=(const Units& v);
88  /** Equality Operator */
89  bool operator==(const Units& lhs) const;
90  /** Inequality Operator */
91  bool operator!=(const Units& lhs) const { return !(*this == lhs); }
92  /** Perform a reciprocal operation. Numerator and Denominator swap. */
93  Units& invert();
94 
95  /** Return a String representation if this Unit */
96  std::string toString() const;
97 };
98 
99 /**
100  * Performs Unit math in full precision
101  *
102  * Allows operations such as multiplying a frequency by 2.
103  *
104  */
106 {
107 private:
108  Units unit;
109  sst_big_num value;
110 
111  static std::string trim(const std::string& str);
112 
113 public:
114  void init(const std::string& val);
115 
116  UnitAlgebra() = default;
117  /**
118  Create a new UnitAlgebra instance, and pre-populate with a parsed value.
119 
120  @param val Value to parse. It is of the following format:
121  @code
122  val := NUMBER( )?UNITS
123  NUMBER := (-)?[0-9]+(.[0-9]+)?
124  UNITS := UNITGROUP(/UNITGROUP)
125  UNITGROUP := UNIT(-UNIT)*
126  UNIT := (SIPREFIX)?(BASEUNIT|COMPUNIT)
127  SIPREFIX := {a,f,p,n,u,m,[kKMGTPE]i?}
128  BASEUNIT := {s,B,b,events}
129  COMPUNIT := {Hz,hz,Bps,bps,event}
130  @endcode
131  */
132  UnitAlgebra(const std::string& val);
133  virtual ~UnitAlgebra() = default;
134 
135  /** Copy constructor */
136  UnitAlgebra(const UnitAlgebra&) = default;
137 
138  /** Print to an ostream the value
139  * @param stream Output stream
140  * @param precision Number of digits to print. Default is 6. <= 0 is full precision.
141  */
142  void print(std::ostream& stream, int32_t precision = 6);
143  /** Print to an ostream the value
144  * Formats the number using SI-prefixes
145  * @param stream Output stream
146  * @param precision Number of digits to print. Default is 6. <= 0 is full precision.
147  */
148  void printWithBestSI(std::ostream& stream, int32_t precision = 6);
149  /** Return a string representation of this value
150  * @param precision Number of digits to print. Default is 6. <= 0 is full precision.
151  */
152  std::string toString(int32_t precision = 6) const;
153  /** Return a string representation of this value
154  * Formats the number using SI-prefixes
155  * @param precision Number of digits to print. Default is 6. <= 0 is full precision.
156  */
157  std::string toStringBestSI(int32_t precision = 6) const;
158 
159  UnitAlgebra& operator=(const std::string& v);
160 
161  /** Multiply by an argument; */
163  /** Multiply by an argument; */
164  template <typename T>
165  UnitAlgebra& operator*=(const T& v)
166  {
167  value *= v;
168  return *this;
169  }
170 
171  /** Divide by an argument; */
173  /** Divide by an argument; */
174  template <typename T>
175  UnitAlgebra& operator/=(const T& v)
176  {
177  value /= v;
178  return *this;
179  }
180 
181  /** Add an argument; */
183  /** Multiply by an argument; */
184  template <typename T>
185  UnitAlgebra& operator+=(const T& v)
186  {
187  value += v;
188  return *this;
189  }
190 
191  /** Subtract an argument; */
193  /** Divide by an argument; */
194  template <typename T>
195  UnitAlgebra& operator-=(const T& v)
196  {
197  value -= v;
198  return *this;
199  }
200 
201  /** Compare if this object is greater than the argument */
202  bool operator>(const UnitAlgebra& v) const;
203  /** Compare if this object is greater than, or equal to, the argument */
204  bool operator>=(const UnitAlgebra& v) const;
205  /** Compare if this object is less than the argument */
206  bool operator<(const UnitAlgebra& v) const;
207  /** Compare if this object is less than, or equal to, the argument */
208  bool operator<=(const UnitAlgebra& v) const;
209  /** Compare if this object is equal to, the argument */
210  bool operator==(const UnitAlgebra& v) const;
211  /** Compare if this object is not equal to, the argument */
212  bool operator!=(const UnitAlgebra& v) const;
213  /** Apply a reciprocal operation to the object */
214  UnitAlgebra& invert();
215 
216  /** Returns true if the units in the parameter string are found
217  * in this object.
218  */
219  bool hasUnits(const std::string& u) const;
220  /** Return the raw value */
221  sst_big_num getValue() const { return value; }
222  /** @return Rounded value as a 64bit integer */
223  int64_t getRoundedValue() const;
224  double getDoubleValue() const;
225  bool isValueZero() const;
226 
227  void serialize_order(SST::Core::Serialization::serializer& ser) /* override */
228  {
229  // Do the unit
230  SST_SER(unit.numerator);
231  SST_SER(unit.denominator);
232 
233  // For value, need to convert cpp_dec_float to string and
234  // reinit from string
235  switch ( ser.mode() ) {
236  case SST::Core::Serialization::serializer::SIZER:
237  case SST::Core::Serialization::serializer::PACK:
238  {
239  // std::string s = value.str(40, std::ios_base::fixed);
240  std::string s = value.toString(0);
241  SST_SER(s);
242  break;
243  }
244  case SST::Core::Serialization::serializer::UNPACK:
245  {
246  std::string s;
247  SST_SER(s);
248  value = sst_big_num(s);
249  break;
250  }
251  case SST::Core::Serialization::serializer::MAP:
252  // Add your code here
253  break;
254  }
255  }
256 
257 public:
258  /** Base exception for all exception classes in UnitAlgebra
259  *
260  * This exception inherits from std::logic_error, as all exceptions
261  * thrown in UnitAlgebra are considered configuration errors occurring
262  * prior to simulation start, rather than runtime errors.
263  */
264  class UnitAlgebraException : public std::logic_error
265  {
266  public:
267  /**
268  * @param msg exception message displayed as-is without modification
269  */
270  explicit UnitAlgebraException(const std::string& msg);
271  };
272 
273  /** Exception for when units are not recognized or are invalid
274  */
276  {
277  public:
278  /**
279  * @param type string containing invalid type
280  */
281  explicit InvalidUnitType(const std::string& type);
282  };
283 
284  /** Exception for when number couldn't be parsed
285  */
287  {
288  public:
289  /**
290  * @param number string containing invalid number
291  */
292  explicit InvalidNumberString(const std::string& number);
293  };
294 
295  /** Exception for when attempting operations between objects that do not have matching base units
296  */
298  {
299  public:
300  /**
301  * @param lhs units for UnitAlgebra on left-hand side of operation
302  * @param rhs units for UnitAlgebra on right-hand side of operation
303  * @param operation representation of operation attempted between units
304  */
305  NonMatchingUnits(const std::string& lhs, const std::string& rhs, const std::string& operation);
306  };
307 };
308 
309 // template <typename T>
310 // UnitAlgebra operator* (UnitAlgebra lhs, const T& rhs);
311 
312 // template <typename T>
313 // UnitAlgebra operator* (const T& lhs, UnitAlgebra rhs);
314 
315 // UnitAlgebra operator* (UnitAlgebra& lhs, const UnitAlgebra rhs);
316 
317 // template <typename T>
318 // UnitAlgebra operator/ (UnitAlgebra lhs, const T& rhs);
319 
320 // std::ostream& operator<< (std::ostream& os, const UnitAlgebra& r);
321 
322 // std::ostream& operator<< (std::ostream& os, const Units& r);
323 
324 template <typename T>
326 operator*(UnitAlgebra lhs, const T& rhs)
327 {
328  lhs *= rhs;
329  return lhs;
330 }
331 
332 // template <typename T>
333 // UnitAlgebra operator* (const T& lhs, UnitAlgebra rhs)
334 // {
335 // rhs *= lhs;
336 // return rhs;
337 // }
338 
339 inline UnitAlgebra
340 operator*(UnitAlgebra lhs, const UnitAlgebra& rhs)
341 {
342  lhs *= rhs;
343  return lhs;
344 }
345 
346 template <typename T>
347 UnitAlgebra
348 operator/(UnitAlgebra lhs, const T& rhs)
349 {
350  lhs /= rhs;
351  return lhs;
352 }
353 
354 inline UnitAlgebra
355 operator/(UnitAlgebra lhs, const UnitAlgebra& rhs)
356 {
357  lhs /= rhs;
358  return lhs;
359 }
360 
361 template <typename T>
362 UnitAlgebra
363 operator+(UnitAlgebra lhs, const T& rhs)
364 {
365  lhs += rhs;
366  return lhs;
367 }
368 
369 inline UnitAlgebra
370 operator+(UnitAlgebra lhs, const UnitAlgebra& rhs)
371 {
372  lhs += rhs;
373  return lhs;
374 }
375 
376 template <typename T>
377 UnitAlgebra
378 operator-(UnitAlgebra lhs, const T& rhs)
379 {
380  lhs -= rhs;
381  return lhs;
382 }
383 
384 inline UnitAlgebra
385 operator-(UnitAlgebra lhs, const UnitAlgebra& rhs)
386 {
387  lhs -= rhs;
388  return lhs;
389 }
390 
391 inline std::ostream&
392 operator<<(std::ostream& os, const UnitAlgebra& r)
393 {
394  os << r.toString();
395  return os;
396 }
397 
398 inline std::ostream&
399 operator<<(std::ostream& os, const Units& r)
400 {
401  os << r.toString();
402  return os;
403 }
404 
405 
406 namespace Core::Serialization {
407 
408 template <>
410 {
411 protected:
412  /**
413  Address of the variable for reading and writing
414  */
415  UnitAlgebra* addr_ = nullptr;
416 
417 public:
418  std::string get() override { return addr_->toStringBestSI(); }
419  void set_impl(const std::string& value) override { addr_->init(value); }
420 
421  // We'll act like we're a fundamental type
422  bool isFundamental() override { return true; }
423 
424  /**
425  Get the address of the variable represented by the ObjectMap
426 
427  @return Address of varaible
428  */
429  void* getAddr() override { return addr_; }
430 
431  explicit ObjectMapFundamental(UnitAlgebra* addr) :
432  ObjectMap(),
433  addr_(addr)
434  {}
435 
436  std::string getType() override { return demangle_name(typeid(UnitAlgebra).name()); }
437 };
438 
439 template <>
441 {
442  void operator()(UnitAlgebra& ua, serializer& ser, ser_opt_t options)
443  {
444  switch ( ser.mode() ) {
445  case serializer::SIZER:
446  case serializer::PACK:
447  case serializer::UNPACK:
448  ua.serialize_order(ser);
449  break;
450  case serializer::MAP:
451  {
452  ObjectMap* obj_map = new ObjectMapFundamental<UnitAlgebra>(&ua);
453  if ( options & SerOption::map_read_only ) {
454  ser.mapper().setNextObjectReadOnly();
455  }
456  ser.mapper().map_primitive(ser.getMapName(), obj_map);
457  break;
458  }
459  }
460  }
461 
462  SST_FRIEND_SERIALIZE();
463 };
464 
465 
466 } // namespace Core::Serialization
467 
468 } // namespace SST
469 
470 #endif // SST_CORE_UNITALGEBRA_H
Units & operator*=(const Units &v)
Self-multiplication operator.
Definition: unitAlgebra.cc:227
void * getAddr() override
Get the address of the variable represented by the ObjectMap.
Definition: unitAlgebra.h:429
UnitAlgebra & operator/=(const UnitAlgebra &v)
Divide by an argument;.
Definition: unitAlgebra.cc:419
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:42
std::string toStringBestSI(int32_t precision=6) const
Return a string representation of this value Formats the number using SI-prefixes.
Definition: unitAlgebra.cc:380
bool operator<=(const UnitAlgebra &v) const
Compare if this object is less than, or equal to, the argument.
Definition: unitAlgebra.cc:475
bool operator>=(const UnitAlgebra &v) const
Compare if this object is greater than, or equal to, the argument.
Definition: unitAlgebra.cc:456
std::string toString(int32_t precision=6) const
Create a string representation of this decimal_fixedpoint.
Definition: decimal_fixedpoint.h:446
std::string toString(int32_t precision=6) const
Return a string representation of this value.
Definition: unitAlgebra.cc:372
ObjectMap representing fundamental types, and classes treated as fundamental types.
Definition: objectMap.h:1264
T * addr_
Address of the variable for reading and writing.
Definition: objectMap.h:1270
bool isFundamental() override
Check to see if this ObjectMap represents a fundamental or a class treated as a fundamental.
Definition: unitAlgebra.h:422
bool operator>(const UnitAlgebra &v) const
Compare if this object is greater than the argument.
Definition: unitAlgebra.cc:447
Base serialize class.
Definition: serialize.h:113
Definition: action.cc:18
UnitAlgebra & operator+=(const T &v)
Multiply by an argument;.
Definition: unitAlgebra.h:185
Units & operator=(const Units &v)
Assignment operator.
Definition: unitAlgebra.cc:219
virtual void set_impl(const std::string &value) override
Set the value of the object represented as a string.
Definition: objectMap.h:1279
static void registerBaseUnit(const std::string &u)
Create a new Base Unit type.
Definition: unitAlgebra.cc:166
sst_big_num getValue() const
Return the raw value.
Definition: unitAlgebra.h:221
Base class for objects created by the serializer mapping mode used to map the variables for objects...
Definition: objectMap.h:158
Exception for when number couldn&#39;t be parsed.
Definition: unitAlgebra.h:286
Units & invert()
Perform a reciprocal operation.
Definition: unitAlgebra.cc:261
bool operator<(const UnitAlgebra &v) const
Compare if this object is less than the argument.
Definition: unitAlgebra.cc:466
UnitAlgebra & operator*=(const T &v)
Multiply by an argument;.
Definition: unitAlgebra.h:165
void print(std::ostream &stream, int32_t precision=6)
Print to an ostream the value.
Definition: unitAlgebra.cc:360
bool operator==(const UnitAlgebra &v) const
Compare if this object is equal to, the argument.
Definition: unitAlgebra.cc:484
Base exception for all exception classes in UnitAlgebra.
Definition: unitAlgebra.h:264
bool operator!=(const Units &lhs) const
Inequality Operator.
Definition: unitAlgebra.h:91
NonMatchingUnits(const std::string &lhs, const std::string &rhs, const std::string &operation)
Definition: unitAlgebra.cc:551
InvalidUnitType(const std::string &type)
Definition: unitAlgebra.cc:543
Exception for when attempting operations between objects that do not have matching base units...
Definition: unitAlgebra.h:297
UnitAlgebra & operator-=(const T &v)
Divide by an argument;.
Definition: unitAlgebra.h:195
InvalidNumberString(const std::string &number)
Definition: unitAlgebra.cc:547
UnitAlgebraException(const std::string &msg)
Definition: unitAlgebra.cc:539
bool operator==(const Units &lhs) const
Equality Operator.
Definition: unitAlgebra.cc:247
Exception for when units are not recognized or are invalid.
Definition: unitAlgebra.h:275
UnitAlgebra & invert()
Apply a reciprocal operation to the object.
Definition: unitAlgebra.cc:498
std::string toString() const
Return a String representation if this Unit.
Definition: unitAlgebra.cc:270
std::string getType() override
Get the type of the variable represented by the ObjectMap.
Definition: unitAlgebra.h:436
bool operator!=(const UnitAlgebra &v) const
Compare if this object is not equal to, the argument.
Definition: unitAlgebra.cc:491
static std::string demangle_name(const char *name)
Static function to demangle type names returned from typeid(T).name()
Definition: objectMap.cc:161
UnitAlgebra & operator/=(const T &v)
Divide by an argument;.
Definition: unitAlgebra.h:175
UnitAlgebra & operator*=(const UnitAlgebra &v)
Multiply by an argument;.
Definition: unitAlgebra.cc:411
UnitAlgebra & operator+=(const UnitAlgebra &v)
Add an argument;.
Definition: unitAlgebra.cc:427
static void registerCompoundUnit(const std::string &u, const std::string &v)
Create a new Compound Unit type.
Definition: unitAlgebra.cc:177
UnitAlgebra & operator-=(const UnitAlgebra &v)
Subtract an argument;.
Definition: unitAlgebra.cc:437
bool hasUnits(const std::string &u) const
Returns true if the units in the parameter string are found in this object.
Definition: unitAlgebra.cc:507
Performs Unit math in full precision.
Definition: unitAlgebra.h:105
void printWithBestSI(std::ostream &stream, int32_t precision=6)
Print to an ostream the value Formats the number using SI-prefixes.
Definition: unitAlgebra.cc:366
Units & operator/=(const Units &v)
Self-division operator.
Definition: unitAlgebra.cc:238
int64_t getRoundedValue() const
Definition: unitAlgebra.cc:515
Helper class internal to UnitAlgebra.
Definition: unitAlgebra.h:40