Building Medical Digital Twins with Pulse: Open Source Simulation Tools for Developers and Researchers
×
CompoundUnit.h
1 /* Distributed under the Apache License, Version 2.0.
2  See accompanying NOTICE file for details.*/
3 
4 //----------------------------------------------------------------------------
11 //----------------------------------------------------------------------------
12 #pragma once
13 
14 // Let's try to make this as lightweight as possible so that CCompoundUnit
15 // objects can be efficiently manipulated within the context of an enclosing
16 // physical quantity object (name TBD: PhysicsQuantity? DimensionedQuantity?
17 // How about just Quantity?) when arithmetic with them needs to be done. This
18 // means maximizing use of inline functions, even those that manipulate the
19 // CUEVecType elements, which in turn requires dealing with Microsoft's BS for
20 // exporting STL classes and classes containing STL data objects as data members.
21 // (See http://support.microsoft.com/kb/168958) The only STL class that you can do
22 // this with is vector<>, but fortunately that's all we need here. Usually, I get
23 // around this by declaring the data objects as pointers to STL template
24 // instantiations that I dynamically allocate and manipulate in non-inline functions,
25 // but since we're going for inline methods here, doing so wouldn't buy me anything,
26 // so I may as well make them first class data members. That may have the added
27 // benefit of coaxing out any helpful warning messages that the compiler might
28 // mistakenly think are not relevant when the data members are merely pointers to
29 // STL classes. -CRV
30 
31 // Note: The ordering of the following two declarations is vital. The above-mentioned
32 // knowledge base article only talks about exporting the vector class that I'm
33 // explicitly instantiating, but that results in an error message about the allocator
34 // needing to be exported. Adding the allocator export after the vector export didn't
35 // solve the problem. But the paragraph about nested classes in the KB article alludes to
36 // a circular dependency that manifests itself as different error messages depending
37 // on which thing you export first. So, on a hunch, I reversed the order, and voila!
38 
39 class CDM_DECL CCompoundUnit
40 {
41  // Define the vector type that holds our individual components of a CompoundUnit
42  typedef std::vector<CCompoundUnitElement> CUEVecType;
43 
44 public:
45  // Default ctor
46  CCompoundUnit() : m_dBigness(1.0),
47  m_CUD(nullptr),
48  m_bStaleBigness(true),
49  m_bStaleDimension(true),
50  m_bExplicitNonDBFlag(false),
51  m_bExplicitDBFlag(false),
52  m_bDBFlag(false)
53  {
54  };
55 
56  // Construct directly from a unit string specification
57  CCompoundUnit(const std::string &unitString) : m_dBigness(1.0),
58  m_CUD(nullptr),
59  m_bStaleBigness(true),
60  m_bStaleDimension(true),
61  m_bExplicitNonDBFlag(false),
62  m_bExplicitDBFlag(false),
63  m_bDBFlag(false)
64  {
65  ParseString(unitString);
66  }
67 
68  // Copy ctor
69  CCompoundUnit(const CCompoundUnit &src) : m_strUnit(src.m_strUnit),
70  m_dBigness(src.m_dBigness),
71  m_CUEVec(src.m_CUEVec),
72  m_bStaleBigness(src.m_bStaleBigness),
73  m_bStaleDimension(src.m_bStaleDimension),
74  m_bExplicitNonDBFlag(src.m_bExplicitNonDBFlag),
75  m_bExplicitDBFlag(src.m_bExplicitDBFlag),
76  m_bDBFlag(src.m_bDBFlag)
77  {
78  // In the initializer list, I'm assuming that initializing one vector with another
79  // copies the vector correctly. For the CUnitDimension object, we can't just
80  // initialize our pointer with the src's pointer, because each CCompoundUnit owns its
81  // CUnitDimension object, so we need to invoke the copy constructor on the
82  // CUnitDimension. Unfortunately, since the pointer might be nullptr, we can't simply
83  // do this in the initializer list.
84  if (src.m_CUD == nullptr)
85  {
86  m_CUD = nullptr;
87  }
88  else
89  {
90  m_CUD = new CUnitDimension(*src.m_CUD);
91  }
92  };
93 
94  // dtor
95  virtual ~CCompoundUnit()
96  {
97  if (m_CUD)
98  {
99  delete m_CUD;
100  }
101  };
102 
103  // Obtain a dimension object that uniquely represents the dimensions of this
104  // compound unit in terms of fundamental quantities
105  const CUnitDimension *GetDimension() const;
106 
107  // Obtain the relative magnitude of this compound unit in relation to
108  // an equivalently-dimensioned compound unit comprised of the base units
109  // of each fundamental quantity type. The bigness is the key to unit conversion.
110  // For any compound unit of a given dimension, the value of a quantity in that
111  // unit times the bigness of that unit is an invariant quantity.
112  const double &GetBigness() const;
113 
114  // Obtain the bias of this compound unit. Biases denote additive offsets of one unit
115  // with respect to another unit of the same type. I am currently operating under the
116  // assumption that it is nonsensical to process biases in a compound unit consisting
117  // of more than one fundamental quantity type raised to the power of 1.0. If I come
118  // across a counter-example to this, I may have to re-think things, but for now, I am
119  // assuming that when multiplying a biased unit by other quantities, the bias must be
120  // irrelevant. Currently, the only quantity type that has biased units is temperature,
121  // and this principle seems to hold. E.g., J/degC means the same thing as J/degK even
122  // though there's a bias in converting from degC to degK.
123  double GetBias() const;
124 
125  // We can do arithmetic with these, so overload a few operators
126  // But do *NOT* define operator+, operator-, or the assignment
127  // versions of these operators. You cannot "add" two CompoundUnit
128  // objects. You can add two *values* if they have equivalent
129  // compound unit objects, but the only operations that
130  // can be performed on a compound unit object itself are multiplication,
131  // division, and raising to a power.
133  {
134  if (this != &rhs)
135  {
136  m_dBigness = rhs.m_dBigness;
137  m_bDBFlag = rhs.m_bDBFlag;
138  m_bExplicitDBFlag = rhs.m_bExplicitDBFlag;
139  m_bExplicitNonDBFlag = rhs.m_bExplicitNonDBFlag;
140 
141  // Vector objects should know how to deep-copy themselves
142  m_CUEVec = rhs.m_CUEVec;
143  // Free my dimension if I have one, whether it's stale or now
144  if (m_CUD)
145  {
146  delete m_CUD;
147  m_CUD = nullptr;
148  }
149  // We need to deep-copy the CUnitDimension, not copy pointers
150  // But only copy if RHS is not stale. No sense duplicating
151  // something that's stale. Also, if it's nullptr, it's stale, so
152  // no need for a separate nullptr check.
153  if (! rhs.m_bStaleDimension)
154  {
155  m_CUD = new CUnitDimension(*rhs.m_CUD);
156  }
157  m_bStaleBigness = rhs.m_bStaleBigness;
158  m_bStaleDimension = rhs.m_bStaleDimension;
159  }
160  return *this;
161  };
162 
163  CCompoundUnit & operator*=(const CCompoundUnit &rhs);
164  CCompoundUnit & operator/=(const CCompoundUnit &rhs);
165 
167  {
168  return CCompoundUnit(*this)*= rhs;
169  }
170 
172  {
173  return CCompoundUnit(*this)/= rhs;
174  }
175 
176  // There's no "raise to a power" operator, but this is the next best thing.
178 
179  // Compare two CompoundUnits
180  // We don't care about how the units are precisely represented, whether
181  // one contains "J" and the other contains "kg m^2 s^-2", or "s^-2 m^2 kg"
182  // for that matter. We care about whether they are equivalent. And they are
183  // equivalent if and only if they are equivalent in both dimension and bigness.
184  bool operator==(const CCompoundUnit &rhs) const
185  {
186  return ( (*this->GetDimension() == *rhs.GetDimension()) &&
187  (this->GetBigness() == rhs.GetBigness()) &&
188  (this->GetBias() == rhs.GetBias()));
189  }
190 
191  bool operator!=(const CCompoundUnit &rhs) const
192  {
193  return !(*this == rhs);
194  }
195 
196  // Is this CompoundUnit dimensionally-compatible with a pre-defined
197  // quantity type?
198  bool IsOfType(int quantityTypeID);
199  bool IsOfType(const std::string &quantityName);
200 
201  // Is this CompoundUnit dimensionless
202  bool IsDimensionless() const
203  {
204  return this->GetDimension()->IsDimensionless();
205  }
206 
207  // Is this CompoundUnit in "decibel" mode
208  bool IsDecibel() const;
209 
210  bool IsUnitEmpty() const
211  {
212  return this->m_strUnit.empty();
213  }
214 
215  // Returns either 10 or 20, depending on whether this compound unit is of a
216  // Quantity Type that obeys the 20-Log-Rule
217  double GetDecibelLogScaleFactor() const;
218 
220  {
221  // Make this explicitly decibel mode, in case we need to do a conversion to/from dB.
222  // We need explicit flags for each condition, so that with both of them off, the
223  // dB state is driven by the presence of a dB mode in the Compound Unit Elements
224  // (through the CompoundUnit expansion of a Unit Descriptor).
225  m_bExplicitDBFlag = true;
226  m_bExplicitNonDBFlag = false;
227  m_bDBFlag = true;
228  }
229 
231  {
232  // Make this explicitly not-decibel mode
233  m_bExplicitDBFlag = false;
234  m_bExplicitNonDBFlag = true;
235  m_bDBFlag = false;
236  }
237 
238  // Used for incrementally building up a CompoundUnit
239  CCompoundUnitElement & AddElement(const CCompoundUnitElement &);
240 
241  // Erase everything
242  void clear()
243  {
244  if (m_CUD)
245  {
246  delete m_CUD;
247  m_CUD = nullptr;
248  }
249  m_dBigness = 1.0;
250  m_bStaleBigness = true;
251  m_bStaleDimension = true;
252  // The explicit dB flag is true if the CompoundUnit explicitly contained a "dB" token.
253  // The regular dB flag is true if either the explicit dB flag is true or if any of the
254  // Compound Unit Elements refer to Unit Descriptors whose Compound Unit expansion has
255  // its dB flag set
256  m_bExplicitDBFlag = false;
257  m_bDBFlag = false;
258  m_CUEVec.clear();
259  m_strUnit.clear();
260  }
261 
262  // Build up my internals from a string specification.
263  void ParseString(const std::string &unitString);
264 
265  std::string GetString() const {return m_strUnit;}
266 
267  // Auxiliary output routine
268  std::ostream & PrintSelf(std::ostream &output) const;
269 
270 protected:
271  void BuildDimension() const;
272  void ComputeBigness() const;
273 
274 private:
275  // Many of these are declared "mutable" so that they can be changed on a "const"
276  // object. Ordinarily we don't let const objects be modified, but this allows us to
277  // declare certain member function "const" when the function is supposed to be "read-only",
278  // but in reality the "read" causes the update of cached values, and those must be
279  // declared "mutable"
280  mutable std::string m_strUnit;
281  mutable double m_dBigness;
284  mutable bool m_bStaleBigness;
285  mutable bool m_bStaleDimension;
286  mutable bool m_bExplicitNonDBFlag;
287  mutable bool m_bExplicitDBFlag;
288  mutable bool m_bDBFlag;
289 };
290 
291 inline CCompoundUnit pow(const CCompoundUnit &baseref, CCompoundUnitElement::ExponentType exp)
292 {
293  // Construct a "C++ temporary" that we modify by raising to the power.
294  // It's the same principle that guides the implementation of operator*
295  // (see above) as described by Myers, except that there is no operator
296  // that corresponds to raising to a power, so we use an ordinary
297  // function instead.
298 
299  return (CCompoundUnit(baseref)).Raise(exp);
300 }
301 
302 inline CCompoundUnit sqrt(const CCompoundUnit &argref)
303 {
304  return pow(argref, 0.5);
305 }
306 
307 inline std::ostream & operator<<(std::ostream &out, const CCompoundUnit &ccu)
308 {
309  //return ccu.PrintSelf(output);
310  out << ccu.GetString();
311  return out;
312 }
313 inline std::ostream& operator<< (std::ostream& out, const CCompoundUnit* ccu)
314 {
315  if (ccu == nullptr)
316  out << "";
317  else
318  out << ccu->GetString();
319  return out;
320 }
Definition: CompoundUnitElement.h:16
Definition: CompoundUnit.h:40
void DecibelModeOn()
Definition: CompoundUnit.h:219
void clear()
Definition: CompoundUnit.h:242
double m_dBigness
Definition: CompoundUnit.h:281
std::string m_strUnit
Definition: CompoundUnit.h:280
CCompoundUnit operator/(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:171
CCompoundUnit & operator=(const CCompoundUnit &rhs)
Definition: CompoundUnit.h:132
std::vector< CCompoundUnitElement > CUEVecType
Definition: CompoundUnit.h:42
CUnitDimension * m_CUD
Definition: CompoundUnit.h:283
bool m_bExplicitDBFlag
Definition: CompoundUnit.h:287
CUEVecType m_CUEVec
Definition: CompoundUnit.h:282
CCompoundUnit(const CCompoundUnit &src)
Definition: CompoundUnit.h:69
CCompoundUnit & Raise(CCompoundUnitElement::ExponentType)
Definition: CompoundUnit.cpp:594
bool m_bStaleBigness
Definition: CompoundUnit.h:284
const CUnitDimension * GetDimension() const
Definition: CompoundUnit.cpp:235
const double & GetBigness() const
Definition: CompoundUnit.cpp:251
void DecibelModeOff()
Definition: CompoundUnit.h:230
bool IsUnitEmpty() const
Definition: CompoundUnit.h:210
CCompoundUnit()
Definition: CompoundUnit.h:46
bool operator==(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:184
CCompoundUnit(const std::string &unitString)
Definition: CompoundUnit.h:57
virtual ~CCompoundUnit()
Definition: CompoundUnit.h:95
std::string GetString() const
Definition: CompoundUnit.h:265
bool m_bStaleDimension
Definition: CompoundUnit.h:285
bool m_bDBFlag
Definition: CompoundUnit.h:288
bool m_bExplicitNonDBFlag
Definition: CompoundUnit.h:286
CCompoundUnit operator*(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:166
bool operator!=(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:191
bool IsDimensionless() const
Definition: CompoundUnit.h:202
double GetBias() const
Definition: CompoundUnit.cpp:290
Definition: SnapValue.h:38
Definition: UnitDimension.h:22

Distributed under the Apache License, Version 2.0.

See accompanying NOTICE file for details.