UnitDimension.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 //----------------------------------------------------------------------------
19 //----------------------------------------------------------------------------
20 #pragma once
21 #include <algorithm>
22 #include <vector>
23 #include <cmath>
24 #include <functional>
25 
26 #include <biogears/cdm/Macros.h>
27 #include <biogears/cdm/utils/unitconversion/CompoundUnitElement.h>
28 
29 // The nth element in our array contains the total exponent of the nth fundamental
30 // quantity type in the transitive closure of the expansion of each unit in
31 // a compound unit structure. The purpose of this object is to determine whether
32 // two CompoundUnits are dimensionally compatible (i.e. whether one can be converted
33 // to the other and vice-versa).
34 namespace biogears {
36 public:
38  using ExponentList = std::vector<ExponentType>;
39 
40  // Default ctor: size the array according to the number of fundamental quantities
42 
43  // Copy ctor.
45  : m_EList(src.m_EList){
46 
47  };
48 
49  size_t size() const
50  {
51  return m_EList.size();
52  }
53 
54  // Vectors of ints are probably initialized to zero anyway upon creation,
55  // but I'd rather not rely on that.
56  void InitElems();
57 
58  // Operators that allow CCompoundUnit to construct our elements
59  // Always a good idea to overload operator= if providing a copy constructor
61  {
62  m_EList = rhs.m_EList;
63  return *this;
64  };
65 
66  // Provide interface to indexing to the underlying vector elements. Alternative
67  // is exposing the vector pointer, which would be somewhat ugly.
69  {
70  return m_EList[pos];
71  };
72 
73  // Not sure if we'd ever need to index into a const CCompoundUnitElement, but
74  // completeness is always a good thing.
76  {
77  return m_EList[pos];
78  };
79 
80  // Test two CUnitDimension objects for equality. Here's where we want behavior
81  // that goes above and beyond what a vector does all by itself. It's conceivable,
82  // though unlikely, that application code may add new fundamental quantities
83  // after CUnitDimension objects have already been created. Thus, two CUnitDimension
84  // objects can be "equal" even if they're not the same size, so long as the longer
85  // one has zeros in its elements that are beyond the length of the shorter one.
86  bool operator==(const CUnitDimension& rhs) const
87  {
88  if (m_EList.size() == rhs.m_EList.size()) {
89  return (m_EList == rhs.m_EList);
90  } else {
91  size_t shorter = std::min<size_t>(m_EList.size(), rhs.m_EList.size());
92  size_t longer = std::max<size_t>(m_EList.size(), rhs.m_EList.size());
93  // First compare over elements they have in common
94  for (size_t i = 0; i < shorter; ++i) {
95  if (m_EList[i] != rhs.m_EList[i]) {
96  return false;
97  }
98  }
99  // Now figure out which vector is the longer one and check its extra
100  // elements to make sure they're all zero
101  const ExponentList& longerList = (m_EList.size() == longer ? m_EList : rhs.m_EList);
102  for (size_t i = shorter; i < longer; ++i) {
103  if (longerList[i] != 0.0) {
104  return false;
105  }
106  }
107  return true;
108  }
109  };
110 
111  // Implement operator!= for sake of completeness
112  bool operator!=(const CUnitDimension& rhs) const
113  {
114  return !(*this == rhs);
115  }
116 
117  // Adding and subtracting dimension objects allows us to take shortcuts
118  // when multiplying and dividing CCompoundUnit objects whose CUnitDimension
119  // objects have already been built
121  {
122  size_t shorter = std::min<size_t>(m_EList.size(), rhs.m_EList.size());
123  size_t longer = std::max<size_t>(m_EList.size(), rhs.m_EList.size());
124  // First combine over elements they have in common
125  for (size_t i = 0; i < shorter; ++i) {
126  m_EList[i] += rhs.m_EList[i];
127  }
128 
129  // If the right hand side is longer than *this, then resize *this and copy
130  // the extra elements
131  if ((longer > shorter) && (rhs.m_EList.size() == longer)) {
132  m_EList.resize(longer);
133  for (size_t i = shorter; i < longer; ++i) {
134  m_EList[i] = rhs.m_EList[i];
135  }
136  } // if we need to copy more
137  return *this;
138  }
139 
141  {
142  size_t shorter = std::min<size_t>(m_EList.size(), rhs.m_EList.size());
143  size_t longer = std::max<size_t>(m_EList.size(), rhs.m_EList.size());
144  // First combine over elements they have in common
145  for (size_t i = 0; i < shorter; ++i) {
146  m_EList[i] -= rhs.m_EList[i];
147  }
148 
149  // If the right hand side is longer than *this, then resize *this and copy
150  // the extra elements
151  if ((longer > shorter) && (rhs.m_EList.size() == longer)) {
152  m_EList.resize(longer);
153  for (size_t i = shorter; i < longer; ++i) {
154  m_EList[i] = -rhs.m_EList[i];
155  }
156  } // if we need to copy more
157  return *this;
158  }
159 
160  const CUnitDimension operator+(const CUnitDimension& rhs) const
161  {
162  return CUnitDimension(*this) += rhs;
163  }
164 
165  const CUnitDimension operator-(const CUnitDimension& rhs) const
166  {
167  return CUnitDimension(*this) -= rhs;
168  }
169 
170  // Multiplying CUnitDimension by a scalar allows us to take shortcuts when
171  // raising to a power a CCompoundUnit object whose CUnitDimension object
172  // has already been computed
173  CUnitDimension& operator*=(const double& rhs)
174  {
175  size_t len = m_EList.size();
176  for (size_t i = 0; i < len; ++i) {
177  m_EList[i] *= rhs;
178  }
179  return *this;
180  }
181 
182  const CUnitDimension operator*(const double& rhs) const
183  {
184  return CUnitDimension(*this) *= rhs;
185  }
186 
187  // Determine if this UnitDimension object corresponds to a single
188  // specified fundamental quantity
189  bool IsFundamentalQuantity(size_t fundIdx) const
190  {
191  size_t len = m_EList.size();
192 
193  // Rule out invalid params
194  if (fundIdx >= len) {
195  return false;
196  }
197 
198  int requiredVal;
199  for (size_t i = 0; i < len; ++i) {
200  //Must be 1 at the fundamental quantity index location, and zero everywhere else
201  requiredVal = ((i == fundIdx) ? 1 : 0);
202  if (m_EList[i] != requiredVal) {
203  return false;
204  }
205  }
206  return true;
207  }
208 
209  // Determine if this UnitDimension object is dimensionless
210  bool IsDimensionless() const
211  {
212  size_t len = m_EList.size();
213  for (size_t i = 0; i < len; ++i) {
214  if (m_EList[i] != 0) {
215  return false;
216  }
217  }
218  return true;
219  }
220 
221  // Since we use CUnitDimension objects as the basis for a hashmap key in
222  // CQuantityConversionKey, we need to implement a couple of methods for
223  // use by the CQuantityConversionKey object to help it implement the
224  // methods it needs in order for the default hash_map templated hash_compare
225  // object to work properly. Specifically, we need "operator<" to be defined
226  // properly, and we need a hash_value function.
227 
228  bool operator<(const CUnitDimension& rhs) const
229  {
230  size_t shorter = std::min<size_t>(m_EList.size(), rhs.m_EList.size());
231  size_t longer = std::max<size_t>(m_EList.size(), rhs.m_EList.size());
232  // First compare over elements they have in common
233  for (size_t i = 0; i < shorter; ++i) {
234  if (m_EList[i] > rhs.m_EList[i]) {
235  return false;
236  } else if (m_EList[i] < rhs.m_EList[i]) {
237  return true;
238  }
239  }
240 
241  // If we got through the above loop without exiting, then all the
242  // elements in common are equal. If the vector lengths are the same,
243  // then *all* the elements are equal, and the less-than relationship
244  // is false
245  if (shorter == longer) {
246  return false;
247  }
248 
249  // The shorter vector is considered to be padded with zeros.
250  // Thus, compare the longer vector's elements to the imaginary
251  // zeros on the shorter
252  if (m_EList.size() > rhs.m_EList.size()) {
253  // ok, the left side is longer. Compare the excess left-side elements
254  // to the imaginary padded zeros
255  for (size_t i = shorter; i < longer; ++i) {
256  if (m_EList[i] > 0.0) {
257  return false;
258  } else if (m_EList[i] < 0.0) {
259  return true;
260  }
261  }
262  } // left side is longer
263  else {
264  // ok, the right side is longer. Compare the excess right-side elements
265  // to the imaginary padded zeros
266  for (size_t i = shorter; i < longer; ++i) {
267  if (rhs.m_EList[i] > 0.0) {
268  return true;
269  } else if (rhs.m_EList[i] < 0.0) {
270  return false;
271  }
272  }
273  } // right side is longer
274 
275  // If we get to this point, then the excess elements are all zeros,
276  // which means that the two CUnitDimension objects are equal, which
277  // means the less-than relationship is false.
278  return false;
279  }
280 
281  // For the sake of completeness only, implement the other relational operators
282  bool operator>(const CUnitDimension& rhs) const
283  {
284  return rhs < *this;
285  }
286 
287  bool operator<=(const CUnitDimension& rhs) const
288  {
289  return !(*this > rhs);
290  }
291 
292  bool operator>=(const CUnitDimension& rhs) const
293  {
294  return !(*this < rhs);
295  }
296 
297  // This hash value must be invariant with respect to trailing zeros
298  // at the end of the vector. See comment above operator==.
299  size_t hash_value() const
300  {
301  ExponentList::const_iterator iend = m_EList.end();
302  ExponentList::const_iterator ibgn = m_EList.begin();
303  // Find first trailing zero as new "end"
304  while ((iend != ibgn) && *--iend == 0)
305  ;
306  iend++;
307 
308  // Now invoke the collection hash routine provided in <xhash>
309  return _Hash_value(ibgn, iend);
310  }
311 
312 private:
314 };
315 
316 // The member function operators work when the right operand is a double, but not
317 // when the left operand is a double. So write a non-member operator* that
318 // computes the result by invoking the member operator* with operands in the
319 // reverse order. This is fine because multiplication of a CUnitDimension and
320 // a scalar double val is an abelian operation.
321 inline const CUnitDimension operator*(const double& lhs, const CUnitDimension& rhs)
322 {
323  return rhs * lhs;
324 }
325 
326 // Overload non_member hash_value on CUnitDimension so that the
327 // templated hash_compare used by hash_map can call it.
328 inline size_t hash_value(const CUnitDimension& ref)
329 {
330  return ref.hash_value();
331 }
332 }
333 namespace std {
334 template <>
335 struct hash<biogears::CUnitDimension> {
336  size_t operator()(const biogears::CUnitDimension& ref) const
337  {
338  return ref.hash_value();
339  }
340 };
341 }
CUnitDimension()
Definition: UnitDimension.cpp:23
size_t hash_value(const CQuantityConversionKey &ref)
Definition: QuantityConversionKey.h:86
const CUnitDimension operator*(const double &rhs) const
Definition: UnitDimension.h:182
bool IsDimensionless() const
Definition: UnitDimension.h:210
CUnitDimension & operator-=(const CUnitDimension &rhs)
Definition: UnitDimension.h:140
CUnitDimension & operator*=(const double &rhs)
Definition: UnitDimension.h:173
CCompoundUnitElement::ExponentType & operator[](size_t pos)
Definition: UnitDimension.h:68
STL namespace.
size_t operator()(const biogears::CUnitDimension &ref) const
Definition: UnitDimension.h:336
size_t size() const
Definition: UnitDimension.h:49
CUnitDimension(const CUnitDimension &src)
Definition: UnitDimension.h:44
bool operator==(const CUnitDimension &rhs) const
Definition: UnitDimension.h:86
bool IsFundamentalQuantity(size_t fundIdx) const
Definition: UnitDimension.h:189
const CUnitDimension operator-(const CUnitDimension &rhs) const
Definition: UnitDimension.h:165
SEScalar operator*(double lhs, const SEScalar &rhs)
Definition: SEScalar.h:123
bool operator!=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:112
bool operator<=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:287
CSnapValue ExponentType
Definition: CompoundUnitElement.h:42
size_t hash_value() const
Definition: UnitDimension.h:299
const CUnitDimension operator+(const CUnitDimension &rhs) const
Definition: UnitDimension.h:160
CCompoundUnitElement::ExponentType const & operator[](size_t pos) const
Definition: UnitDimension.h:75
std::vector< ExponentType > ExponentList
Definition: UnitDimension.h:38
CUnitDimension & operator=(const CUnitDimension &rhs)
Definition: UnitDimension.h:60
bool operator>=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:292
Definition: SnapValue.h:46
Definition: UnitDimension.h:35
CUnitDimension & operator+=(const CUnitDimension &rhs)
Definition: UnitDimension.h:120
size_t _Hash_value(_InIt _Begin, _InIt _End)
Definition: Macros.h:25
bool operator>(const CUnitDimension &rhs) const
Definition: UnitDimension.h:282
bool operator<(const CUnitDimension &rhs) const
Definition: UnitDimension.h:228
ExponentList m_EList
Definition: UnitDimension.h:313
Definition: SEElectricalCircuit.h:18
void InitElems()
Definition: UnitDimension.cpp:32