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.internal.dimensions; 21 import quantities.base; 22 import quantities.math; 23 import quantities.parsing; 24 import quantities.qvariant; 25 26 import std.array : appender; 27 import std.conv; 28 import std.math : PI; 29 import std.format; 30 import std.string; 31 import std.typetuple; 32 import core.time : Duration, dur; 33 34 version (unittest) import std.math : approxEqual; 35 36 version (SIReal) 37 alias Numeric = real; 38 else version (SIDouble) 39 alias Numeric = double; 40 else version (SIFloat) 41 alias Numeric = float; 42 else 43 alias Numeric = double; 44 45 /++ 46 Predefined SI units. 47 +/ 48 enum meter = unit!(Numeric, "L"); 49 alias metre = meter; /// ditto 50 enum kilogram = unit!(Numeric, "M"); /// ditto 51 enum second = unit!(Numeric, "T"); /// ditto 52 enum ampere = unit!(Numeric, "I"); /// ditto 53 enum kelvin = unit!(Numeric, "Θ"); /// ditto 54 enum mole = unit!(Numeric, "N"); /// ditto 55 enum candela = unit!(Numeric, "J"); /// ditto 56 57 enum radian = meter / meter; // ditto 58 enum steradian = square(meter) / square(meter); /// ditto 59 enum hertz = 1 / second; /// ditto 60 enum newton = kilogram * meter / square(second); /// ditto 61 enum pascal = newton / square(meter); /// ditto 62 enum joule = newton * meter; /// ditto 63 enum watt = joule / second; /// ditto 64 enum coulomb = second * ampere; /// ditto 65 enum volt = watt / ampere; /// ditto 66 enum farad = coulomb / volt; /// ditto 67 enum ohm = volt / ampere; /// ditto 68 enum siemens = ampere / volt; /// ditto 69 enum weber = volt * second; /// ditto 70 enum tesla = weber / square(meter); /// ditto 71 enum henry = weber / ampere; /// ditto 72 enum celsius = kelvin; /// ditto 73 enum lumen = candela / steradian; /// ditto 74 enum lux = lumen / square(meter); /// ditto 75 enum becquerel = 1 / second; /// ditto 76 enum gray = joule / kilogram; /// ditto 77 enum sievert = joule / kilogram; /// ditto 78 enum katal = mole / second; /// ditto 79 80 enum gram = 1e-3 * kilogram; /// ditto 81 enum minute = 60 * second; /// ditto 82 enum hour = 60 * minute; /// ditto 83 enum day = 24 * hour; /// ditto 84 enum degreeOfAngle = PI / 180 * radian; /// ditto 85 enum minuteOfAngle = degreeOfAngle / 60; /// ditto 86 enum secondOfAngle = minuteOfAngle / 60; /// ditto 87 enum hectare = 1e4 * square(meter); /// ditto 88 enum liter = 1e-3 * cubic(meter); /// ditto 89 alias litre = liter; /// ditto 90 enum ton = 1e3 * kilogram; /// ditto 91 enum electronVolt = 1.60217653e-19 * joule; /// ditto 92 enum dalton = 1.66053886e-27 * kilogram; /// ditto 93 94 //enum one = Quantity!(Numeric, Dimensions.init)(1); /// The dimensionless unit 'one' 95 96 alias Length = typeof(meter); /// Predefined quantity type templates for SI quantities 97 alias Mass = typeof(kilogram); /// ditto 98 alias Time = typeof(second); /// ditto 99 alias ElectricCurrent = typeof(ampere); /// ditto 100 alias Temperature = typeof(kelvin); /// ditto 101 alias AmountOfSubstance = typeof(mole); /// ditto 102 alias LuminousIntensity = typeof(candela); /// ditto 103 104 alias Area = typeof(square(meter)); /// ditto 105 alias Surface = Area; 106 alias Volume = typeof(cubic(meter)); /// ditto 107 alias Speed = typeof(meter/second); /// ditto 108 alias Acceleration = typeof(meter/square(second)); /// ditto 109 alias MassDensity = typeof(kilogram/cubic(meter)); /// ditto 110 alias CurrentDensity = typeof(ampere/square(meter)); /// ditto 111 alias MagneticFieldStrength = typeof(ampere/meter); /// ditto 112 alias Concentration = typeof(mole/cubic(meter)); /// ditto 113 alias MolarConcentration = Concentration; /// ditto 114 alias MassicConcentration = typeof(kilogram/cubic(meter)); /// ditto 115 alias Luminance = typeof(candela/square(meter)); /// ditto 116 alias RefractiveIndex = typeof(kilogram); /// ditto 117 118 alias Angle = typeof(radian); /// ditto 119 alias SolidAngle = typeof(steradian); /// ditto 120 alias Frequency = typeof(hertz); /// ditto 121 alias Force = typeof(newton); /// ditto 122 alias Pressure = typeof(pascal); /// ditto 123 alias Energy = typeof(joule); /// ditto 124 alias Work = Energy; /// ditto 125 alias Heat = Energy; /// ditto 126 alias Power = typeof(watt); /// ditto 127 alias ElectricCharge = typeof(coulomb); /// ditto 128 alias ElectricPotential = typeof(volt); /// ditto 129 alias Capacitance = typeof(farad); /// ditto 130 alias ElectricResistance = typeof(ohm); /// ditto 131 alias ElectricConductance = typeof(siemens); /// ditto 132 alias MagneticFlux = typeof(weber); /// ditto 133 alias MagneticFluxDensity = typeof(tesla); /// ditto 134 alias Inductance = typeof(henry); /// ditto 135 alias LuminousFlux = typeof(lumen); /// ditto 136 alias Illuminance = typeof(lux); /// ditto 137 alias CelsiusTemperature = typeof(celsius); /// ditto 138 alias Radioactivity = typeof(becquerel); /// ditto 139 alias AbsorbedDose = typeof(gray); /// ditto 140 alias DoseEquivalent = typeof(sievert); /// ditto 141 alias CatalyticActivity = typeof(katal); /// ditto 142 143 alias Dimensionless = typeof(meter/meter); /// The type of dimensionless quantities 144 145 /// SI prefixes. 146 alias yotta = prefix!1e24; 147 alias zetta = prefix!1e21; /// ditto 148 alias exa = prefix!1e18; /// ditto 149 alias peta = prefix!1e15; /// ditto 150 alias tera = prefix!1e12; /// ditto 151 alias giga = prefix!1e9; /// ditto 152 alias mega = prefix!1e6; /// ditto 153 alias kilo = prefix!1e3; /// ditto 154 alias hecto = prefix!1e2; /// ditto 155 alias deca = prefix!1e1; /// ditto 156 alias deci = prefix!1e-1; /// ditto 157 alias centi = prefix!1e-2; /// ditto 158 alias milli = prefix!1e-3; /// ditto 159 alias micro = prefix!1e-6; /// ditto 160 alias nano = prefix!1e-9; /// ditto 161 alias pico = prefix!1e-12; /// ditto 162 alias femto = prefix!1e-15; /// ditto 163 alias atto = prefix!1e-18; /// ditto 164 alias zepto = prefix!1e-21; /// ditto 165 alias yocto = prefix!1e-24; /// ditto 166 167 /// A list of common SI symbols and prefixes 168 enum siSymbols = SymbolList!Numeric() 169 .addUnit("m", meter) 170 .addUnit("kg", kilogram) 171 .addUnit("s", second) 172 .addUnit("A", ampere) 173 .addUnit("K", kelvin) 174 .addUnit("mol", mole) 175 .addUnit("cd", candela) 176 .addUnit("rad", radian) 177 .addUnit("sr", steradian) 178 .addUnit("Hz", hertz) 179 .addUnit("N", newton) 180 .addUnit("Pa", pascal) 181 .addUnit("J", joule) 182 .addUnit("W", watt) 183 .addUnit("C", coulomb) 184 .addUnit("V", volt) 185 .addUnit("F", farad) 186 .addUnit("Ω", ohm) 187 .addUnit("S", siemens) 188 .addUnit("Wb", weber) 189 .addUnit("T", tesla) 190 .addUnit("H", henry) 191 .addUnit("lm", lumen) 192 .addUnit("lx", lux) 193 .addUnit("Bq", becquerel) 194 .addUnit("Gy", gray) 195 .addUnit("Sv", sievert) 196 .addUnit("kat", katal) 197 .addUnit("g", gram) 198 .addUnit("min", minute) 199 .addUnit("h", hour) 200 .addUnit("d", day) 201 .addUnit("l", liter) 202 .addUnit("L", liter) 203 .addUnit("t", ton) 204 .addUnit("eV", electronVolt) 205 .addUnit("Da", dalton) 206 .addPrefix("Y", 1e24) 207 .addPrefix("Z", 1e21) 208 .addPrefix("E", 1e18) 209 .addPrefix("P", 1e15) 210 .addPrefix("T", 1e12) 211 .addPrefix("G", 1e9) 212 .addPrefix("M", 1e6) 213 .addPrefix("k", 1e3) 214 .addPrefix("h", 1e2) 215 .addPrefix("da", 1e1) 216 .addPrefix("d", 1e-1) 217 .addPrefix("c", 1e-2) 218 .addPrefix("m", 1e-3) 219 .addPrefix("µ", 1e-6) 220 .addPrefix("n", 1e-9) 221 .addPrefix("p", 1e-12) 222 .addPrefix("f", 1e-15) 223 .addPrefix("a", 1e-18) 224 .addPrefix("z", 1e-21) 225 .addPrefix("y", 1e-24) 226 .addPrefix("Yi", 1024.0^^8) 227 .addPrefix("Zi", 1024.0^^7) 228 .addPrefix("Ei", 1024.0^^6) 229 .addPrefix("Pi", 1024.0^^5) 230 .addPrefix("Ti", 1024.0^^4) 231 .addPrefix("Gi", 1024.0^^3) 232 .addPrefix("Mi", 1024.0^^2) 233 .addPrefix("Ki", 1024.0); 234 235 private static SymbolList!Numeric _siSymbols; 236 private static Parser!Numeric _siParser; 237 static this() 238 { 239 _siSymbols = siSymbols; 240 _siParser = Parser!Numeric(_siSymbols, &std.conv.parse!(Numeric, string)); 241 } 242 243 /// Parses a string for a quantity of type Q at runtime 244 Q parseSI(Q)(string str) 245 if (isQuantity!Q) 246 { 247 return _siParser.parse!Q(str); 248 } 249 /// 250 @safe unittest 251 { 252 auto t = parseSI!Time("90 min"); 253 assert(t == 90 * minute); 254 t = parseSI!Time("h"); 255 assert(t == 1 * hour); 256 257 auto v = parseSI!Dimensionless("2"); 258 assert(v == (2 * meter) / meter); 259 } 260 261 /// A compile-time parser with automatic type deduction for SI quantities. 262 alias si = compileTimeParser!(Numeric, siSymbols, std.conv.parse!(Numeric, string)); 263 /// 264 pure @safe unittest 265 { 266 enum min = si!"min"; 267 enum inch = si!"2.54 cm"; 268 auto conc = si!"1 µmol/L"; 269 auto speed = si!"m s^-1"; 270 auto value = si!"0.5"; 271 272 static assert(is(typeof(inch) == Length)); 273 static assert(is(typeof(conc) == Concentration)); 274 static assert(is(typeof(speed) == Speed)); 275 static assert(is(typeof(value) == Dimensionless)); 276 } 277 278 /++ 279 Helper struct that formats a SI quantity. 280 +/ 281 struct SIFormatWrapper(Q) 282 if (isQuantity!Q || isQVariant!Q) 283 { 284 private string fmt; 285 286 /++ 287 Creates a new formatter from a format string. 288 +/ 289 this(string fmt) 290 { 291 this.fmt = fmt; 292 } 293 294 /++ 295 Returns a wrapper struct that can be formatted by `std.string.format` or 296 `std.format` functions. 297 +/ 298 auto opCall(Q quantity) const 299 { 300 auto _fmt = fmt; 301 302 /// 303 struct Wrapper 304 { 305 private Q quantity; 306 307 void toString(scope void delegate(const(char)[]) sink) const 308 { 309 auto spec = FormatSpec!char(_fmt); 310 spec.writeUpToNextSpec(sink); 311 auto target = spec.trailing.idup; 312 auto unit = parseSI!Q(target); 313 sink.formatValue(quantity.value(unit), spec); 314 sink(target); 315 } 316 317 string toString() const 318 { 319 return format("%s", this); 320 } 321 } 322 323 return Wrapper(quantity); 324 } 325 } 326 /// 327 unittest 328 { 329 import std.string; 330 331 auto sf = SIFormatWrapper!Speed("%.1f km/h"); 332 auto speed = 343.4 * meter/second; 333 assert("Speed: %s".format(sf(speed)) == "Speed: 1236.2 km/h"); 334 } 335 336 unittest 337 { 338 auto sf = SIFormatWrapper!Speed("%.1f km/h"); 339 assert(text(sf(si!"343.4 m/s")) == "1236.2 km/h"); 340 assert(text(sf(parseSI!Speed("343.4 m/s"))) == "1236.2 km/h"); 341 } 342 343 /++ 344 Convenience function that returns a SIFormatter when the format string is 345 known at compile-time. 346 +/ 347 auto siFormatWrapper(string fmt)() 348 { 349 enum unit = si!({ 350 auto spec = FormatSpec!char(fmt); 351 auto dummy = appender!string; 352 spec.writeUpToNextSpec(dummy); 353 return spec.trailing.idup; 354 }()); 355 356 return SIFormatWrapper!(typeof(unit))(fmt); 357 } 358 /// 359 unittest 360 { 361 import std.string; 362 363 auto sf = siFormatWrapper!"%.1f km/h"; 364 auto speed = 343.4 * meter/second; 365 assert("Speed: %s".format(sf(speed)) == "Speed: 1236.2 km/h"); 366 } 367 368 /// Converts a quantity of time to or from a core.time.Duration 369 Time fromDuration(Duration d) pure @safe 370 { 371 return d.total!"hnsecs" * hecto(nano(second)); 372 } 373 374 /// ditto 375 Duration toDuration(Q)(Q quantity) 376 if (isQuantity!Q) 377 { 378 import std.conv; 379 auto hns = quantity.value(hecto(nano(second))); 380 return dur!"hnsecs"(hns.roundTo!long); 381 } 382 383 /// 384 @safe unittest // Durations 385 { 386 auto d = 4.dur!"msecs"; 387 auto t = fromDuration(d); 388 assert(t.value(milli(second)).approxEqual(4)); 389 390 auto t2 = 3.5 * minute; 391 auto d2 = t2.toDuration; 392 auto s = d2.split!("minutes", "seconds")(); 393 assert(s.minutes == 3 && s.seconds == 30); 394 }