1 /** 2 * International System of Units (SI) units and prefixes for use with 3 * $(D std.units). 4 * 5 * The definitions have been taken from the NIST Special Publication 330, 6 * $(WEB http://physics.nist.gov/Pubs/SP330/sp330.pdf, The International 7 * System of Units), 2008 edition. 8 * 9 * Todo: $(UL 10 * $(LI Do something about the derived unit types being expanded in the 11 * generated documentation.) 12 * ) 13 * 14 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 15 * Authors: $(WEB klickverbot.at, David Nadlinger) 16 */ 17 module experimental.units.si; 18 19 import experimental.units; 20 21 /** 22 * The full $(XREF units, PrefixSystem) of SI prefixes. 23 * 24 * For each prefix, a helper template like $(D kilo!()) for prefixing units 25 * is provided (see $(XREF units, prefixTemplate)). 26 */ 27 alias SiPrefixSystem = PrefixSystem!(10, { 28 return [Prefix(-24, "yocto", "y"), 29 Prefix(-21, "zepto", "z"), 30 Prefix(-18, "atto", "a"), 31 Prefix(-15, "femto", "f"), 32 Prefix(-12, "pico", "p"), 33 Prefix(-9, "nano", "n"), 34 Prefix(-6, "micro", "µ"), 35 Prefix(-3, "milli", "m"), 36 Prefix(-2, "centi", "c"), 37 Prefix(-1, "deci", "d"), 38 Prefix(1, "deka", "da"), 39 Prefix(2, "hecto", "h"), 40 Prefix(3, "kilo", "k"), 41 Prefix(6, "mega", "M"), 42 Prefix(9, "giga", "G"), 43 Prefix(12, "tera", "T"), 44 Prefix(15, "peta", "P"), 45 Prefix(18, "exa", "E"), 46 Prefix(21, "zetta", "Z"), 47 Prefix(24, "yotta", "Y")]; 48 }); 49 50 // TODO Add binary byte units 51 52 mixin DefinePrefixSystem!(SiPrefixSystem); 53 54 /** 55 * SI base units. 56 */ 57 alias Ampere = BaseUnit!("Ampere", "A"); 58 alias Candela = BaseUnit!("candela", "cd"); 59 alias Gram = BaseUnit!("gram", "g"); 60 alias Kelvin = BaseUnit!("Kelvin", "K"); 61 alias Metre = BaseUnit!("metre", "m"); 62 alias Mole = BaseUnit!("mole", "mol"); 63 alias Second = BaseUnit!("second", "s"); 64 alias Radian = BaseUnit!("radian", "rad"); 65 alias Steradian = BaseUnit!("steradian", "sr"); 66 67 enum ampere = Ampere.init; 68 enum candela = Candela.init; /// ditto 69 enum gram = Gram.init; /// ditto 70 enum kilogram = kilo!gram; /// ditto 71 enum kelvin = Kelvin.init; /// ditto 72 enum metre = Metre.init; /// ditto 73 alias meter = metre; /// ditto 74 enum mole = Mole.init; /// ditto 75 enum second = Second.init; /// ditto 76 77 /** 78 * SI supplementary units for angles. 79 */ 80 enum radian = Radian.init; 81 enum steradian = Steradian.init; /// ditto 82 83 import std.math : PI; 84 85 enum PI_OVER_180 = PI/180; 86 enum _180_OVER_PI = 180/PI; 87 88 /** 89 * SI scaled units for angles. 90 */ 91 // enum degree = PI_OVER_180 * radian; // TODO Use own convertible type: 92 enum degree = scale!(radian, PI/180, "degree"); 93 94 // TODO Celsius: Use AffineUnit 95 // TODO Fahrenheit: Add and use LinearUnit 96 97 auto cos(Q)(Q angle) 98 if (Q.init.isConvertibleTo!radian) // TODO Fix and use ConvertibleTo? 99 { 100 import std.math : cos; 101 return cos(angle.convert!radian.toValue); 102 } 103 104 auto sin(Q)(Q angle) 105 if (Q.init.isConvertibleTo!radian) 106 { 107 import std.math : sin; 108 return sin(angle.convert!radian.toValue); 109 } 110 111 auto tan(Q)(Q angle) 112 if (Q.init.isConvertibleTo!radian) 113 { 114 import std.math : tan; 115 return tan(angle.convert!radian.toValue); 116 } 117 118 auto expi(Q)(Q angle) 119 if (Q.init.isConvertibleTo!radian) 120 { 121 import std.math : expi; 122 return expi(angle.convert!radian.toValue); 123 } 124 125 /// 126 @safe pure nothrow @nogc unittest 127 { 128 import std.math : isClose; 129 130 assert(0.0*radian < 1.0*radian); 131 // TODO fix Quantitiy.opCmp to allow: assert(0.0*radian < 1.0*degree); 132 133 assert(isClose(cos(0.0*radian), 1)); 134 assert(isClose(cos(PI*radian), -1)); 135 assert(isClose(cos(2*PI*radian), 1)); 136 137 enum d = (180*degree); 138 // pragma(msg, d.stringof ~ " : " ~ typeof(d).stringof); 139 140 enum r = d.convert!radian; 141 // pragma(msg, r.stringof ~ " : " ~ typeof(r).stringof); 142 143 // TODO enable when cast in ScaledUnit.{to|from}Base have been removed: 144 // TODO assert(isClose(cos(180*degree), -1)); 145 146 assert(isClose(sin(0.0*radian), 0)); 147 assert(isClose(sin(PI*radian), 0)); 148 assert(isClose(sin(2*PI*radian), 0)); 149 // TODO enable when cast in ScaledUnit.{to|from}Base have been removed: 150 // TODO assert(isClose(sin(360*degree), 0)); 151 assert(isClose(sin(PI*radian), 0)); 152 153 // assert(isClose(expi(0.0*radian)!0.toValue, 0)); 154 } 155 156 /** 157 * SI derived units. 158 */ 159 enum hertz = dimensionless / second; 160 enum newton = kilogram * metre / pow!2(second); /// ditto 161 enum pascal = newton / pow!2(metre); /// ditto 162 enum joule = newton * metre; /// ditto 163 enum watt = joule / second; /// ditto 164 enum coulomb = ampere * second; /// ditto 165 enum volt = watt / ampere; /// ditto 166 enum farad = coulomb / volt; /// ditto 167 enum ohm = volt / ampere; /// ditto 168 enum siemens = ampere / volt; /// ditto 169 enum weber = volt * second; /// ditto 170 enum tesla = weber / pow!2(metre); /// ditto 171 enum henry = weber / ampere; /// ditto 172 enum lumen = candela * steradian; /// ditto 173 enum lux = lumen / pow!2(metre); /// ditto 174 enum becquerel = dimensionless / second; /// ditto 175 enum gray = joule / kilogram; /// ditto 176 enum sievert = joule / kilogram; /// ditto 177 enum katal = mole / second; /// ditto 178 179 /// 180 @safe pure nothrow @nogc unittest 181 { 182 auto work(Quantity!newton force, Quantity!metre displacement) 183 { 184 return force * displacement; 185 } 186 187 Quantity!(mole, V) idealGasAmount(V)(Quantity!(pascal, V) pressure, 188 Quantity!(pow!3(meter), V) volume, Quantity!(kelvin, V) temperature) 189 { 190 enum R = 8.314471 * joule / (kelvin * mole); 191 return (pressure * volume) / (temperature * R); 192 } 193 194 enum forcef = 1.0f * newton; 195 enum force = 1.0 * newton; 196 197 // compare quantities with different value types 198 assert(forcef == force); 199 static assert(forcef == force); 200 201 enum displacement = 1.0 * metre; 202 enum Quantity!joule e = work(force, displacement); 203 static assert(e == 1.0 * joule); 204 205 enum T = (273. + 37.) * kelvin; 206 enum p = 1.01325e5 * pascal; 207 enum r = 0.5e-6 * meter; 208 enum V = (4.0 / 3.0) * 3.141592 * r.pow!3; 209 enum n = idealGasAmount!double(p, V, T); // Need to explicitly specify double due to @@BUG5801@@. 210 // TODO is this needed: static assert(n == 0xb.dd95ef4ddcb82f7p-59 * mole); 211 212 static assert((2 * kilogram).convert!gram == 2000 * gram); 213 static assert((2000 * gram).convert!kilogram == 2 * kilogram); 214 static assert((1000 * newton).convert!(milli!newton) == 1000000 * milli!newton); 215 static assert((2000000 * gram * meter / second.pow!2).convert!(kilo!newton) == 2 * kilo!newton); 216 static assert((1234.0 * micro!newton / milli!metre.pow!2).convert!pascal == 1234.0 * pascal); 217 }