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