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