1 // Written in the D programming language
2 /++
3 This module defines the SI units and prefixes.
4 
5 All the quantities and units defined in this module store a value
6 of type double intenally. So the predefined parsers can only parse
7 double values.
8 
9 Copyright: Copyright 2013-2014, Nicolas Sicard
10 Authors: Nicolas Sicard
11 License: $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12 Standards: $(LINK http://www.bipm.org/en/si/si_brochure/)
13 Source: $(LINK https://github.com/biozic/quantities)
14 +/
15 module quantities.si;
16 
17 import quantities.base;
18 import quantities.math;
19 import quantities.parsing;
20 import std.conv;
21 import std.math : PI;
22 import std.typetuple;
23 import core.time : Duration, dur;
24 
25 version (unittest) import std.math : approxEqual;
26 
27 /++
28 Predefined SI units.
29 +/
30 enum meter = unit!(double, "L");
31 alias metre = meter; /// ditto
32 enum kilogram = unit!(double, "M"); /// ditto
33 enum second = unit!(double, "T"); /// ditto
34 enum ampere = unit!(double, "I"); /// ditto
35 enum kelvin = unit!(double, "Θ"); /// ditto
36 enum mole = unit!(double, "N"); /// ditto
37 enum candela = unit!(double, "J"); /// ditto
38 
39 enum radian = meter / meter; // ditto
40 enum steradian = square(meter) / square(meter); /// ditto
41 enum hertz = 1 / second; /// ditto
42 enum newton = kilogram * meter / square(second); /// ditto
43 enum pascal = newton / square(meter); /// ditto
44 enum joule = newton * meter; /// ditto
45 enum watt = joule / second; /// ditto
46 enum coulomb = second * ampere; /// ditto
47 enum volt = watt / ampere; /// ditto
48 enum farad = coulomb / volt; /// ditto
49 enum ohm = volt / ampere; /// ditto
50 enum siemens = ampere / volt; /// ditto
51 enum weber = volt * second; /// ditto
52 enum tesla = weber / square(meter); /// ditto
53 enum henry = weber / ampere; /// ditto
54 enum celsius = kelvin; /// ditto
55 enum lumen = candela / steradian; /// ditto
56 enum lux = lumen / square(meter); /// ditto
57 enum becquerel = 1 / second; /// ditto
58 enum gray = joule / kilogram; /// ditto
59 enum sievert = joule / kilogram; /// ditto
60 enum katal = mole / second; /// ditto
61 
62 enum gram = 1e-3 * kilogram; /// ditto
63 enum minute = 60 * second; /// ditto
64 enum hour = 60 * minute; /// ditto
65 enum day = 24 * hour; /// ditto
66 enum degreeOfAngle = PI / 180 * radian; /// ditto
67 enum minuteOfAngle = degreeOfAngle / 60; /// ditto
68 enum secondOfAngle = minuteOfAngle / 60; /// ditto
69 enum hectare = 1e4 * square(meter); /// ditto
70 enum liter = 1e-3 * cubic(meter); /// ditto
71 alias litre = liter; /// ditto
72 enum ton = 1e3 * kilogram; /// ditto
73 enum electronVolt = 1.60217653e-19 * joule; /// ditto
74 enum dalton = 1.66053886e-27 * kilogram; /// ditto
75 
76 enum one = Quantity!double(1); /// The dimensionless unit 'one'
77 
78 alias Length = typeof(meter); /// Predefined quantity type templates for SI quantities
79 alias Mass = typeof(kilogram); /// ditto
80 alias Time = typeof(second); /// ditto
81 alias ElectricCurrent = typeof(ampere); /// ditto
82 alias Temperature = typeof(kelvin); /// ditto
83 alias AmountOfSubstance = typeof(mole); /// ditto
84 alias LuminousIntensity = typeof(candela); /// ditto
85 
86 alias Area = typeof(square(meter)); /// ditto
87 alias Surface = Area;
88 alias Volume = typeof(cubic(meter)); /// ditto
89 alias Speed = typeof(meter/second); /// ditto
90 alias Acceleration = typeof(meter/square(second)); /// ditto
91 alias MassDensity = typeof(kilogram/cubic(meter)); /// ditto
92 alias CurrentDensity = typeof(ampere/square(meter)); /// ditto
93 alias MagneticFieldStrength = typeof(ampere/meter); /// ditto
94 alias Concentration = typeof(mole/cubic(meter)); /// ditto
95 alias MolarConcentration = Concentration; /// ditto
96 alias MassicConcentration = typeof(kilogram/cubic(meter)); /// ditto
97 alias Luminance = typeof(candela/square(meter)); /// ditto
98 alias RefractiveIndex = typeof(kilogram); /// ditto
99 
100 alias Angle = typeof(radian); /// ditto
101 alias SolidAngle = typeof(steradian); /// ditto
102 alias Frequency = typeof(hertz); /// ditto
103 alias Force = typeof(newton); /// ditto
104 alias Pressure = typeof(pascal); /// ditto
105 alias Energy = typeof(joule); /// ditto
106 alias Work = Energy; /// ditto
107 alias Heat = Energy; /// ditto
108 alias Power = typeof(watt); /// ditto
109 alias ElectricCharge = typeof(coulomb); /// ditto
110 alias ElectricPotential = typeof(volt); /// ditto
111 alias Capacitance = typeof(farad); /// ditto
112 alias ElectricResistance = typeof(ohm); /// ditto
113 alias ElectricConductance = typeof(siemens); /// ditto
114 alias MagneticFlux = typeof(weber); /// ditto
115 alias MagneticFluxDensity = typeof(tesla); /// ditto
116 alias Inductance = typeof(henry); /// ditto
117 alias LuminousFlux = typeof(lumen); /// ditto
118 alias Illuminance = typeof(lux); /// ditto
119 alias CelsiusTemperature = typeof(celsius); /// ditto
120 alias Radioactivity = typeof(becquerel); /// ditto
121 alias AbsorbedDose = typeof(gray); /// ditto
122 alias DoseEquivalent = typeof(sievert); /// ditto
123 alias CatalyticActivity = typeof(katal); /// ditto
124 
125 alias Dimensionless = typeof(meter/meter); /// The type of dimensionless quantities
126 
127 /// SI prefixes.
128 alias yotta = prefix!1e24;
129 alias zetta = prefix!1e21; /// ditto
130 alias exa = prefix!1e18; /// ditto
131 alias peta = prefix!1e15; /// ditto
132 alias tera = prefix!1e12; /// ditto
133 alias giga = prefix!1e9; /// ditto
134 alias mega = prefix!1e6; /// ditto
135 alias kilo = prefix!1e3; /// ditto
136 alias hecto = prefix!1e2; /// ditto
137 alias deca = prefix!1e1; /// ditto
138 alias deci = prefix!1e-1; /// ditto
139 alias centi = prefix!1e-2; /// ditto
140 alias milli = prefix!1e-3; /// ditto
141 alias micro = prefix!1e-6; /// ditto
142 alias nano = prefix!1e-9; /// ditto
143 alias pico = prefix!1e-12; /// ditto
144 alias femto = prefix!1e-15; /// ditto
145 alias atto = prefix!1e-18; /// ditto
146 alias zepto = prefix!1e-21; /// ditto
147 alias yocto = prefix!1e-24; /// ditto
148 
149 private alias siSymbolTuple = TypeTuple!(
150     withUnit("m", meter),
151     withUnit("kg", kilogram),
152     withUnit("s", second),
153     withUnit("A", ampere),
154     withUnit("K", kelvin),
155     withUnit("mol", mole),
156     withUnit("cd", candela),
157     withUnit("rad", radian),
158     withUnit("sr", steradian),
159     withUnit("Hz", hertz),
160     withUnit("N", newton),
161     withUnit("Pa", pascal),
162     withUnit("J", joule),
163     withUnit("W", watt),
164     withUnit("C", coulomb),
165     withUnit("V", volt),
166     withUnit("F", farad),
167     withUnit("Ω", ohm),
168     withUnit("S", siemens),
169     withUnit("Wb", weber),
170     withUnit("T", tesla),
171     withUnit("H", henry),
172     withUnit("lm", lumen),
173     withUnit("lx", lux),
174     withUnit("Bq", becquerel),
175     withUnit("Gy", gray),
176     withUnit("Sv", sievert),
177     withUnit("kat", katal),
178     withUnit("g", gram),
179     withUnit("min", minute),
180     withUnit("h", hour),
181     withUnit("d", day),
182     withUnit("l", liter),
183     withUnit("L", liter),
184     withUnit("t", ton),
185     withUnit("eV", electronVolt),
186     withUnit("Da", dalton),
187     withPrefix("Y", 1e24),
188     withPrefix("Z", 1e21),
189     withPrefix("E", 1e18),
190     withPrefix("P", 1e15),
191     withPrefix("T", 1e12),
192     withPrefix("G", 1e9),
193     withPrefix("M", 1e6),
194     withPrefix("k", 1e3),
195     withPrefix("h", 1e2),
196     withPrefix("da", 1e1),
197     withPrefix("d", 1e-1),
198     withPrefix("c", 1e-2),
199     withPrefix("m", 1e-3),
200     withPrefix("µ", 1e-6),
201     withPrefix("n", 1e-9),
202     withPrefix("p", 1e-12),
203     withPrefix("f", 1e-15),
204     withPrefix("a", 1e-18),
205     withPrefix("z", 1e-21),
206     withPrefix("y", 1e-24),
207     withPrefix("Yi", 1024.0^^8),
208     withPrefix("Zi", 1024.0^^7),
209     withPrefix("Ei", 1024.0^^6),
210     withPrefix("Pi", 1024.0^^5),
211     withPrefix("Ti", 1024.0^^4),
212     withPrefix("Gi", 1024.0^^3),
213     withPrefix("Mi", 1024.0^^2),
214     withPrefix("Ki", 1024.0)
215 );
216 
217 enum _siSymbolList = makeSymbolList!double(siSymbolTuple);
218 static __gshared SymbolList!double siSymbolList;
219 shared static this()
220 {
221     siSymbolList = _siSymbolList;
222 }
223 
224 /// Creates a function that parses a string for a SI unit or quantity at runtime.
225 alias parseSI = rtQuantityParser!(double, siSymbolList);
226 ///
227 unittest
228 {
229     auto t = parseSI!Time("90 min");
230     assert(t == 90 * minute);
231     t = parseSI!Time("h");
232     assert(t == 1 * hour);
233 }
234 
235 unittest
236 {
237     auto v = parseSI!Dimensionless("2");
238     assert(v == (2 * meter) / meter);
239 }
240 
241 /// Creates a function that parses a string for a SI unit or quantity at compile-time.
242 alias si = ctQuantityParser!(double, _siSymbolList, std.conv.parse!(double, string));
243 ///
244 unittest
245 {
246     enum min = si!"min";
247     enum inch = si!"2.54 cm";
248     
249     auto conc = si!"1 µmol/L";
250     auto speed = si!"m s^-1";
251     auto value = si!"0.5";
252     
253     static assert(is(typeof(conc) == Concentration));
254     static assert(is(typeof(speed) == Speed));
255     static assert(is(typeof(value) == Dimensionless));
256 }
257 
258 /++
259 Helper template that can be used to add all SI units and prefixes when
260 building a symbol list with makeSymbolList.
261 +/
262 alias withAllSI = siSymbolTuple;
263 
264 /// Converts a quantity of time to or from a core.time.Duration
265 Time fromDuration(Duration d)
266 {
267     return d.total!"hnsecs" * hecto(nano(second));
268 }
269 
270 /// ditto
271 Duration toDuration(Q)(Q quantity)
272     if (isQuantity!Q)
273 {
274     import std.conv;
275     auto hns = quantity.value(hecto(nano(second)));
276     return dur!"hnsecs"(roundTo!long(hns));
277 }
278 
279 static if (__VERSION__ <= 2065) // Deprecation of Duration.get
280 {
281     unittest // Durations
282     {
283         auto d = 4.dur!"msecs";
284         auto t = fromDuration(d);
285         assert(t.value(milli(second)).approxEqual(4));
286 
287         auto t2 = 3.5 * minute;
288         auto d2 = t2.toDuration;
289         assert(d2.get!"minutes" == 3);
290         assert(d2.get!"seconds" == 30);
291     }
292 }
293 else
294 {
295     unittest // Durations
296     {
297         auto d = 4.dur!"msecs";
298         auto t = fromDuration(d);
299         assert(t.value(milli(second)).approxEqual(4));
300         
301         auto t2 = 3.5 * minute;
302         auto d2 = t2.toDuration;
303         auto s = d2.split!("minutes", "seconds")();
304         assert(s.minutes == 3 && s.seconds == 30);
305     }
306 }