Alamo
Unit.H
Go to the documentation of this file.
1#ifndef UNIT_UNIT_H
2#define UNIT_UNIT_H
3
4
5#include <array>
6#include <charconv>
7#include <cstdlib>
8#include <iostream>
9#include <system_error>
10#include <unordered_map>
11#include <string>
12#include <stdexcept>
13#include <cmath>
14#include <regex>
15
16//#include "Set/Base.H"
17//#include "String.H"
18
19struct Unit : std::pair<double,std::array<int,7>>
20{
21
22 //
23 // STATIC CONST DEFINITIONS, TRUE EVERYWHERE
24 //
25 enum class Type {
27 };
28 // Fundamental units
29 inline static const std::map<std::string, std::pair<double,std::array<int,7>>> base_units =
30 {
31 {"1", {1.0, {0,0,0,0,0,0,0}}},
32 {"m", {1.0, {1,0,0,0,0,0,0}}},
33 {"s", {1.0, {0,1,0,0,0,0,0}}},
34 {"kg", {1.0, {0,0,1,0,0,0,0}}},
35 {"K", {1.0, {0,0,0,1,0,0,0}}},
36 {"A", {1.0, {0,0,0,0,1,0,0}}},
37 {"mol",{1.0, {0,0,0,0,0,1,0}}},
38 {"cd", {1.0, {0,0,0,0,0,0,1}}}
39 };
40 //
41 // Compound unit relationships and nomenclature
42 // Any desired unit relationship can be added to this list.
43 // The unit conversion can be specified in any desired format, as long as
44 // all of the units on the right hand side are resolvable.
45 //
46 inline static const std::map<std::string, std::pair<double,std::string>> compound = {
47 // Lengths
48 {"km", {1e+3, "m" }},
49 {"hm", {1e+2, "m" }},
50 {"dam", {1e+1, "m" }},
51 {"dm", {1e-1, "m" }},
52 {"cm", {1e-2, "m" }},
53 {"mm", {1e-3, "m" }},
54 {"um", {1e-6, "m" }},
55 {"nm", {1e-9, "m" }},
56 {"pm", {1e-12, "m" }},
57 {"in", {0.0254, "m" }},
58 {"ft", {0.3048, "m" }},
59 {"yd", {0.9144, "m" }},
60 {"mi", {5280.0, "ft" }},
61 {"furlong", {660.0, "ft" }},
62 {"au", {1.495978707e11, "m" }}, // astronomical unit
63 {"ly", {9.4607304725808e15, "m"}}, // lightyear
64 {"pc", {3.08567758149137e16, "m"}}, // parsec
65
66 // Times
67 {"ms", {1e-3, "s" }},
68 {"us", {1e-6, "s" }},
69 {"ns", {1e-9, "s" }},
70 {"ps", {1e-12, "s" }},
71 {"min", {60.0, "s" }},
72 {"hr", {60.0, "min" }},
73 {"day", {24.0, "hr" }},
74 {"week", {7.0, "day" }},
75 {"fortnight", {14.0, "day" }},
76 {"year", {365.25, "day" }},
77 {"decade", {10.0, "year" }},
78
79 // Mass
80 {"g", {1e-3, "kg" }},
81 {"mg", {1e-6, "kg" }},
82 {"ug", {1e-9, "kg" }},
83 {"tonne", {1000.0, "kg" }},
84 {"lb", {0.45359237, "kg" }},
85 {"oz", {0.0283495, "kg" }},
86 {"slug", {14.5939, "kg" }},
87
88 // Speed
89 {"mph", {1.0, "mi/hr" }},
90 {"kph", {1.0, "km/hr" }},
91 {"mps", {1.0, "m/s" }},
92 {"fps", {1.0, "ft/s" }},
93 {"knot", {1852.0 / 3600.0, "m/s"}},
94
95 // Area
96 {"ha", {1e4, "m^2" }},
97 {"acre", {4046.85642, "m^2" }},
98 {"sqft", {1.0, "ft^2" }},
99 {"sqin", {1.0, "in^2" }},
100 {"sqkm", {1.0, "km^2" }},
101 {"sqmi", {1.0, "mi^2" }},
102
103 // Volume
104 {"L", {1e-3, "m^3" }},
105 {"mL", {1e-6, "m^3" }},
106 {"cc", {1e-6, "m^3" }},
107 {"gal", {3.78541e-3, "m^3" }},
108 {"qt", {0.946353e-3, "m^3" }},
109 {"pt", {0.473176e-3, "m^3" }},
110 {"cup", {0.24e-3, "m^3" }},
111 {"floz", {29.5735e-6, "m^3" }},
112 {"tbsp", {14.7868e-6, "m^3" }},
113 {"tsp", {4.92892e-6, "m^3" }},
114
115 // Force
116 {"N", {1.0, "kg*m/s^2" }},
117 {"kN", {1e3, "N" }},
118 {"lbf", {4.44822162, "N" }},
119 {"dyn", {1e-5, "N" }},
120
121 // Energy
122 {"J", {1.0, "N*m" }},
123 {"kJ", {1e3, "J" }},
124 {"MJ", {1e6, "J" }},
125 {"eV", {1.602176634e-19, "J"}},
126 {"cal", {4.184, "J" }},
127 {"kcal", {4184.0, "J" }},
128 {"BTU", {1055.06, "J" }},
129 {"ft*lbf", {1.35582, "J" }},
130
131 // Power
132 {"W", {1.0, "J/s" }},
133 {"kW", {1e3, "W" }},
134 {"MW", {1e6, "W" }},
135 {"hp", {745.7, "W" }},
136
137 // Pressure
138 {"Pa", {1.0, "N/m^2" }},
139 {"kPa", {1e3, "Pa" }},
140 {"MPa", {1e6, "Pa" }},
141 {"GPa", {1e9, "Pa" }},
142 {"bar", {1e5, "Pa" }},
143 {"atm", {101325.0,"Pa" }},
144 {"psi", {6894.76, "Pa" }},
145 {"mmHg", {133.322, "Pa" }},
146 {"torr", {133.322, "Pa" }},
147
148 // Temperature differences (not absolute temperatures)
149 {"K", {1.0, "K" }},
150 {"degR", {5.0/9.0, "K" }},
151
152 // Charge
153 {"C", {1.0, "A*s" }},
154 {"mAh", {3.6, "C" }},
155
156 // Voltage
157 {"V", {1.0, "J/C" }},
158
159 // Capacitance
160 {"F", {1.0, "C/V" }},
161
162 // Resistance
163 {"ohm", {1.0, "V/A" }},
164 {"kOhm", {1e3, "ohm" }},
165 {"MOhm", {1e6, "ohm" }},
166
167 // Conductance
168 {"S", {1.0, "1/ohm" }},
169
170 // Magnetic
171 {"T", {1.0, "kg/(A*s^2)"}},
172 {"G", {1e-4, "T" }}, // gauss
173
174 // Frequency
175 {"Hz", {1.0, "1/s" }},
176 {"kHz", {1e3, "Hz" }},
177 {"MHz", {1e6, "Hz" }},
178 {"GHz", {1e9, "Hz" }},
179 };
180
181
182 //
183 // These static factory functions return unit types.
184 // They are used to check unit compliance, mainly at the parsing stage.
185 //
186 // Primary
187 static Unit Less() { return Unit{1.0, {0,0,0,0,0,0,0}}; } // 1
188 static Unit Length() { return Unit{1.0, {1,0,0,0,0,0,0}}; } // m
189 static Unit Time() { return Unit{1.0, {0,1,0,0,0,0,0}}; } // s
190 static Unit Mass() { return Unit{1.0, {0,0,1,0,0,0,0}}; } // kg
191 static Unit Temperature() { return Unit{1.0, {0,0,0,1,0,0,0}}; } // K
192 static Unit Current() { return Unit{1.0, {0,0,0,0,1,0,0}}; } // A
193 static Unit Amount() { return Unit{1.0, {0,0,0,0,0,1,0}}; } // mol
194 static Unit LuminousIntensity() { return Unit{1.0, {0,0,0,0,0,0,1}}; } // cd
195
196 // Geometry and kinematics
197 static Unit Area() { return Length()*Length(); }
198 static Unit Volume() { return Length()*Length()*Length(); }
199 static Unit Velocity() { return Length() / Time();}
200 static Unit Acceleration(){ return Velocity() / Time(); }
201 // Mechanics
202 static Unit Force() { return Mass() * Acceleration(); } // kg·m/s² = N
203 static Unit Momentum() { return Mass() * Velocity(); }
204 static Unit Impulse() { return Force() * Time(); }
205 static Unit Pressure() { return Force() / Area(); } // N/m² = Pa
206 static Unit Energy() { return Force() * Length(); } // N·m = J
207 static Unit Power() { return Energy() / Time(); } // J/s = W
208 static Unit Density() { return Mass() / Volume(); }
209 static Unit SpecificWeight() { return Force() / Volume(); }
210 static Unit Work() { return Energy(); }
211 // Thermodynamics
212 static Unit SpecificHeatCapacity() {return Energy() / Mass() / Temperature() ;}
215 static Unit HeatFlux() { return Power() / Area(); }
217 // Electromagnetism
218
219 static Unit Charge() { return Current() * Time(); } // A·s = C
220 static Unit Voltage() { return Power() / Current(); } // W/A = V
221 static Unit Resistance() { return Voltage() / Current(); } // V/A = Ω
222 static Unit Capacitance() { return Charge() / Voltage(); } // C/V = F
223 static Unit Conductance() { return Less() / Resistance(); } // S = 1/Ω
224 static Unit MagneticFlux(){ return Voltage() * Time(); } // V·s = Wb
225 static Unit MagneticField(){ return MagneticFlux() / Area(); } // Wb/m² = T
226 static Unit Inductance() { return MagneticFlux() / Current(); } // Wb/A = H
227 // Chemistry
228 static Unit MolarMass() { return Mass() / Amount(); } // kg/mol
229 static Unit Concentration(){ return Amount() / Volume(); } // mol/m³
230 static Unit MolarEnergy() { return Energy() / Amount(); } // J/mol
231 // Optics and radiation
232 static Unit LuminousFlux() { return LuminousIntensity(); } // for scalar usage (lm)
233 static Unit Illuminance() { return LuminousFlux() / Area(); } // lux = lm/m²
234
235
236 //
237 // This is where SYSTEM NORMALIZATION is stored.
238 // Should usually be set once (at the beginning of the simulation) and then
239 // left constant throughout the simulation.
240 // All normalizations are in terms of standard SI units: m,s,kg,K,A,mol,cd.
241 //
242 inline static std::array<std::pair<std::string,double>, 7> normalization = {
243 std::pair{"m", 1.0},
244 std::pair{"s", 1.0},
245 std::pair{"kg", 1.0},
246 std::pair{"K", 1.0},
247 std::pair{"A", 1.0},
248 std::pair{"mol",1.0},
249 std::pair{"cd", 1.0}
250 };
251
252 using std::pair<double, std::array<int,7>>::pair;
253 Unit(const std::pair<double, std::array<int, 7>>& p) : std::pair<double, std::array<int, 7>>(p) {}
254 static std::map<std::string, int> ParseUnitString(const std::string& input);
255
256
257 static Unit Parse(double val, std::string unitstring)
258 {
259 Unit ret = StringParse(unitstring);
260 ret.first *= val;
261 return ret;
262 }
263 static Unit Parse(std::string unit)
264 {
265 if (unit.size() == 0) return Unit::Less();
266 std::vector<std::string> split;
267 std::stringstream ss(unit);
268 std::string item;
269 while (std::getline(ss, item, '_')) {
270 split.push_back(item);
271 }
272 if (split.size() == 2)
273 {
274 return Parse(std::stod(split[0]),split[1]);
275 }
276 else if (split.size() == 1)
277 {
278 // Attempt to parse the string as if it's a unitless number
279 double value = NAN;
280
281 // auto first = unit.data();
282 // auto last = unit.data() + unit.size();
283 // auto result = std::from_chars(first, last, value); // apparently this fails on mac
284
285 char *end = nullptr;
286 value = std::strtod(unit.c_str(), &end);
287
288 // If it is a untless number, then return a unitless result
289 //if (result.ec == std::errc() && result.ptr == last) // fails on mac
290 if (end != unit.data() && *end == '\0')
291 {
292 Unit ret = Unit::Less();
293 ret.first = value;
294 return ret;
295 }
296 // Otherwise, it must be a pure unit, so return that.
297 else
298 {
299 return StringParse(unit);
300 }
301 }
302 else
303 {
304 throw std::runtime_error("Invalid unit string "+unit);
305 }
306 }
307
308
309 static Unit StringParse(std::string unitstring)
310 {
311 Unit type = Unit::Less();
312 std::map<std::string, int> map = ParseUnitString(unitstring);
313 for (auto it = map.begin(); it != map.end(); it++)
314 {
315 auto [str, exp] = *it;
316
317 if (Unit::base_units.find(str) != base_units.end())
318 {
319 Unit t = Unit::base_units.at(str);
320 type = type * (t^exp);
321 }
322 else if (compound.find(it->first) != compound.end())
323 {
324 auto [subfac, substr] = compound.at(str);
325 Unit t = StringParse(substr);
326 t.first *= subfac;
327 type = type * t^exp;
328 }
329 else
330 {
331 throw std::runtime_error("Unknown unit: " + unitstring);
332 }
333 }
334 return type;
335 }
336
337
338 static void setLengthUnit(std::string unit)
339 {
340 Unit parsed = StringParse(unit);
341 if (parsed.second != Length().second) throw std::runtime_error("Not a unit of length: " + unit);
342 normalization[int(Type::Length)].first = unit;
343 normalization[int(Type::Length)].second = 1.0 / parsed.first;
344 }
345 static void setTimeUnit(std::string unit)
346 {
347 Unit parsed = StringParse(unit);
348 if (parsed.second != Time().second) throw std::runtime_error("Not a unit of time: " + unit);
349 normalization[int(Type::Time)].first = unit;
350 normalization[int(Type::Time)].second = 1.0 / parsed.first;
351 }
352
353 bool isType(const Unit &test) const
354 {
355 return this->second == test.second;
356 }
357
359 {
360 Unit ret;
361 ret.first = this->first * rhs.first;
362 for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i] + rhs.second[i];
363 return ret;
364 }
365 friend Unit operator*(double lhs, const Unit& rhs) {
366 Unit ret;
367 ret.first = lhs * rhs.first;
368 ret.second = rhs.second;
369 return ret;
370 }
371 friend Unit operator / (double lhs, const Unit& rhs) {
372 Unit ret = Unit::Less() / rhs;
373 ret.first = lhs * rhs.first;
374 return ret;
375 }
377 {
378 Unit ret;
379 ret.first = this->first / rhs.first;
380 for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i] - rhs.second[i];
381 return ret;
382 }
383 Unit operator ^ (const int &rhs)
384 {
385 Unit ret;
386 ret.first = std::pow(this->first,rhs);
387 for (unsigned int i = 0; i < this->second.size(); i++)
388 {
389 ret.second[i] = (this->second)[i]*rhs;
390 }
391 return ret;
392 }
393
394 friend std::ostream& operator<<(std::ostream &out, const Unit &a)
395 {
396 out << a.normalized_value() << "_";
397 out << a.normalized_unitstring();
398 return out;
399 }
400
401 double normalized_value() const
402 {
403 double fac = 1.0;
404 for (unsigned int i = 0 ; i < this->second.size(); i++)
405 fac *= std::pow(normalization[i].second, this->second[i]);
406
407 return this->first * fac;
408 }
409
410 std::string normalized_unitstring() const
411 {
412 // little utility to join strings with delimiter
413 auto join = [] (std::vector<std::string>vec,std::string sep){
414 std::ostringstream oss;
415 for (size_t i = 0; i < vec.size(); ++i) {
416 oss << vec[i];
417 if (i < vec.size() - 1) {
418 oss << sep;
419 }
420 }
421 return oss.str();
422 };
423
424 std::vector<std::string> num, den;
425 for (unsigned int i = 0 ; i < this->second.size(); i++)
426 {
427 if (this->second[i] > 1)
428 num.push_back(normalization[i].first + "^" + std::to_string(this->second[i]));
429 else if (this->second[i] == 1)
430 num.push_back(normalization[i].first);
431 else if (this->second[i] == -1)
432 den.push_back(normalization[i].first);
433 else if (this->second[i] < -1)
434 den.push_back(normalization[i].first + "^" + std::to_string(std::abs(this->second[i])));
435 }
436
437 std::string ret;
438 if (num.size()) ret = join(num,"*");
439 else ret = "1";
440 if (den.size()) ret += "/" + join(den,"/");
441
442 return ret;
443 }
444
445
446
447
448
449
450};
451
452
453inline std::map<std::string, int>
454Unit::ParseUnitString(const std::string& input) {
455 std::map<std::string, int> result;
456 enum class Op { Mul = 1, Div = -1 };
457 Op currentOp = Op::Mul;
458 std::string token;
459 size_t i = 0;
460 auto parse_unit = [&](const std::string& t, Op op) {
461 if (t.empty()) return;
462
463 size_t caret = t.find('^');
464 std::string base = t;
465 int exp = 1;
466
467 if (caret != std::string::npos) {
468 base = t.substr(0, caret);
469 std::string exp_str = t.substr(caret + 1);
470 try {
471 exp = std::stoi(exp_str);
472 } catch (...) {
473 throw std::runtime_error("Invalid exponent: " + exp_str);
474 }
475 }
476 int signed_exp = (op == Op::Mul ? +1 : -1) * exp;
477 result[base] += signed_exp;
478 };
479
480 while (i < input.size()) {
481 char c = input[i];
482
483 if (c == '*')
484 {
485 parse_unit(token, currentOp);
486 token.clear();
487 currentOp = Op::Mul;
488 ++i;
489 }
490 else if (c == '/')
491 {
492 parse_unit(token, currentOp);
493 token.clear();
494 currentOp = Op::Div;
495 ++i;
496 }
497 else
498 {
499 token += c;
500 ++i;
501 }
502 }
503
504 // Parse final token
505 parse_unit(token, currentOp);
506
507 return result;
508}
509
510
511
512
513
514
515
516
517
518
519#endif
std::time_t t
Definition Unit.H:20
Unit operator^(const int &rhs)
Definition Unit.H:383
static Unit Parse(std::string unit)
Definition Unit.H:263
static Unit Resistance()
Definition Unit.H:221
static void setTimeUnit(std::string unit)
Definition Unit.H:345
bool isType(const Unit &test) const
Definition Unit.H:353
static Unit Inductance()
Definition Unit.H:226
static Unit Area()
Definition Unit.H:197
static Unit Voltage()
Definition Unit.H:220
Unit(const std::pair< double, std::array< int, 7 > > &p)
Definition Unit.H:253
static Unit Impulse()
Definition Unit.H:204
friend Unit operator*(double lhs, const Unit &rhs)
Definition Unit.H:365
static Unit Energy()
Definition Unit.H:206
static Unit Conductance()
Definition Unit.H:223
static Unit MolarMass()
Definition Unit.H:228
static Unit Amount()
Definition Unit.H:193
static Unit HeatTransferCoefficient()
Definition Unit.H:216
friend std::ostream & operator<<(std::ostream &out, const Unit &a)
Definition Unit.H:394
static std::array< std::pair< std::string, double >, 7 > normalization
Definition Unit.H:242
static Unit Density()
Definition Unit.H:208
std::string normalized_unitstring() const
Definition Unit.H:410
static const std::map< std::string, std::pair< double, std::string > > compound
Definition Unit.H:46
static Unit ThermalDiffusivity()
Definition Unit.H:214
static std::map< std::string, int > ParseUnitString(const std::string &input)
Definition Unit.H:454
static Unit Time()
Definition Unit.H:189
double normalized_value() const
Definition Unit.H:401
static Unit Force()
Definition Unit.H:202
static Unit ThermalConductivity()
Definition Unit.H:213
static Unit Mass()
Definition Unit.H:190
static Unit Temperature()
Definition Unit.H:191
static Unit Volume()
Definition Unit.H:198
static void setLengthUnit(std::string unit)
Definition Unit.H:338
static Unit Charge()
Definition Unit.H:219
static Unit Pressure()
Definition Unit.H:205
static Unit HeatFlux()
Definition Unit.H:215
static Unit Current()
Definition Unit.H:192
static Unit Illuminance()
Definition Unit.H:233
friend Unit operator/(double lhs, const Unit &rhs)
Definition Unit.H:371
static Unit LuminousFlux()
Definition Unit.H:232
static Unit Capacitance()
Definition Unit.H:222
static Unit Work()
Definition Unit.H:210
static Unit Power()
Definition Unit.H:207
static Unit Velocity()
Definition Unit.H:199
static Unit Momentum()
Definition Unit.H:203
static Unit LuminousIntensity()
Definition Unit.H:194
static Unit Acceleration()
Definition Unit.H:200
static const std::map< std::string, std::pair< double, std::array< int, 7 > > > base_units
Definition Unit.H:29
static Unit MolarEnergy()
Definition Unit.H:230
static Unit Length()
Definition Unit.H:188
static Unit Concentration()
Definition Unit.H:229
static Unit SpecificWeight()
Definition Unit.H:209
static Unit StringParse(std::string unitstring)
Definition Unit.H:309
static Unit SpecificHeatCapacity()
Definition Unit.H:212
static Unit MagneticFlux()
Definition Unit.H:224
static Unit Less()
Definition Unit.H:187
static Unit MagneticField()
Definition Unit.H:225
Type
Definition Unit.H:25
static Unit Parse(double val, std::string unitstring)
Definition Unit.H:257