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