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