forked from JuliaLang/julia
/
units.jl
367 lines (340 loc) · 12.4 KB
/
units.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# Support for physical units: I/O and conversion
# Timothy E. Holy, 2012
abstract SIPrefix
type Yocto <: SIPrefix end
type Zepto <: SIPrefix end
type Atto <: SIPrefix end
type Femto <: SIPrefix end
type Pico <: SIPrefix end
type Nano <: SIPrefix end
type Micro <: SIPrefix end
type Milli <: SIPrefix end
type Centi <: SIPrefix end
type Deci <: SIPrefix end
type SINone <: SIPrefix end
type Deca <: SIPrefix end
type Hecto <: SIPrefix end
type Kilo <: SIPrefix end
type Mega <: SIPrefix end
type Giga <: SIPrefix end
type Tera <: SIPrefix end
type Peta <: SIPrefix end
type Exa <: SIPrefix end
type Zetta <: SIPrefix end
type Yotta <: SIPrefix end
# PrettyShow and PrettyString
pshow(x) = pshow(OUTPUT_STREAM::IOStream, x)
function pstring(x)
s = memio(0, false)
pshow(s, x)
takebuf_string(s)
end
# LatexShow and LatexString
lshow(x) = lshow(OUTPUT_STREAM::IOStream, x)
function lstring(x)
s = memio(0, false)
lshow(s, x)
takebuf_string(s)
end
# FullShow and FullString
fshow(x) = fshow(OUTPUT_STREAM::IOStream, x)
function fstring(x)
s = memio(0, false)
fshow(s, x)
takebuf_string(s)
end
let
# Prefix ToSINone Show PrettyShow LatexShow Full
const prefix_table = {
(Yocto, 1e-24, "y", "y", "y", "yocto")
(Zepto, 1e-21, "z", "z", "z", "zepto")
(Atto, 1e-18, "a", "a", "a", "atto")
(Femto, 1e-15, "f", "f", "f", "femto")
(Pico, 1e-12, "p", "p", "p", "pico")
(Nano, 1e-9, "n", "n", "n", "nano")
(Micro, 1e-6, "u", "\u03bc", L"$\mu$", "micro")
(Milli, 1e-3, "m", "m", "m", "milli")
(Centi, 1e-2, "c", "c", "c", "centi")
(Deci, 1e-1, "d", "d", "d", "deci")
(Deca, 1e1, "da", "da", "da", "deca")
(Hecto, 1e2, "h", "h", "h", "hecto")
(Kilo, 1e3, "k", "k", "k", "kilo")
(Mega, 1e6, "M", "M", "M", "mega")
(Giga, 1e9, "G", "G", "G", "giga")
(Tera, 1e12, "T", "T", "T", "tera")
(Peta, 1e15, "P", "P", "P", "peta")
(Exa, 1e18, "E", "E", "E", "exa")
(Zetta, 1e21, "Z", "Z", "Z", "zetta")
(Yotta, 1e24, "Y", "Y", "Y", "yotta")
}
global to_reference
global show
global pshow
global lshow
global _unit_si_prefixes
for (t, f, s, ps, ls, fs) in prefix_table
@eval to_reference(::Type{$t}) = $f
@eval show(io, ::Type{$t}) = print(io, $s)
@eval pshow(io, ::Type{$t}) = print(io, $ps)
@eval lshow(io, ::Type{$t}) = print(io, $ls)
@eval fshow(io, ::Type{$t}) = print(io, $fs)
end
function return_prefix(i::Int)
if i <= length(prefix_table)
return prefix_table[i][1]
elseif i == length(prefix_table)+1
return SINone
else
error("Something is wrong")
end
end
_unit_si_prefixes = ntuple(length(prefix_table)+1, i->return_prefix(i))
end # let
to_reference(::Type{SINone}) = 1
show(io, ::Type{SINone}) = nothing
pshow(io, ::Type{SINone}) = nothing
lshow(io, ::Type{SINone}) = nothing
fshow(io, ::Type{SINone}) = nothing
# Units
abstract UnitBase
type Unit{TP<:SIPrefix, TU<:UnitBase} end
Unit{TP<:SIPrefix, TU<:UnitBase}(tp::Type{TP}, tu::Type{TU}) = Unit{tp, tu}
Unit{TU<:UnitBase}(tu::Type{TU}) = Unit{SINone, tu}
function show{TP<:SIPrefix, TU<:UnitBase}(io, tu::Unit{TP, TU})
print(io, TP)
print(io, TU)
end
# Values with units
type Quantity{TP<:SIPrefix, TU<:UnitBase, Tdata}
value::Tdata
end
Quantity{TP<:SIPrefix, TU<:UnitBase, Tdata}(tp::Type{TP}, tu::Type{TU}, val::Tdata) = Quantity{tp, tu, Tdata}(val)
Quantity{TU<:UnitBase, Tdata}(tu::Type{TU}, val::Tdata) = Quantity{SINone, tu, Tdata}(val)
prefix{TP<:SIPrefix, TU<:UnitBase, Tdata}(q::Quantity{TP, TU, Tdata}) = TP
base{TP<:SIPrefix, TU<:UnitBase, Tdata}(q::Quantity{TP, TU, Tdata}) = TU
function show{TP<:SIPrefix, TU<:UnitBase}(io, q::Quantity{TP, TU})
print(io, q.value, " ")
show(io, TP)
show(io, TU)
end
function pshow{TP<:SIPrefix, TU<:UnitBase}(io, q::Quantity{TP, TU})
print(io, q.value, " ")
pshow(io, TP)
pshow(io, TU)
end
function lshow{TP<:SIPrefix, TU<:UnitBase}(io, q::Quantity{TP, TU})
print(io, q.value, " ")
lshow(io, TP)
lshow(io, TU)
end
function fshow{TP<:SIPrefix, TU<:UnitBase}(io, q::Quantity{TP, TU})
print(io, q.value, " ")
fshow(io, TP)
fshow(io, TU)
end
# Functions and dictionaries for parsing
_unit_string_dict = (String=>Tuple)[]
function _unit_gen_func_multiplicative(table)
for (t, r, to_r, s, ps, ls, fs) in table
@eval reference(::Type{$t}) = $r
@eval to_reference(::Type{$t}) = x->x*$to_r
@eval from_reference(::Type{$t}) = x->x/$to_r
@eval show(io, ::Type{$t}) = print(io, $s)
@eval pshow(io, ::Type{$t}) = print(io, $ps)
@eval lshow(io, ::Type{$t}) = print(io, $ls)
@eval fshow(io, ::Type{$t}) = print(io, $fs)
end
end
function _unit_gen_func(table)
for (t, r, to_func, from_func, s, ps, ls, fs) in table
@eval reference(::Type{$t}) = $r
@eval to_reference(::Type{$t}) = $to_func
@eval from_reference(::Type{$t}) = $from_func
@eval show(io, ::Type{$t}) = print(io, $s)
@eval pshow(io, ::Type{$t}) = print(io, $ps)
@eval lshow(io, ::Type{$t}) = print(io, $ls)
@eval fshow(io, ::Type{$t}) = print(io, $fs)
end
end
function _unit_gen_dict_with_prefix(table)
for (u, rest) in table
ustr = string(u)
upstr = pstring(u)
for p in _unit_si_prefixes
pstr = string(p)
key = pstr*ustr
# println(key, ": ", p, ", ", u)
_unit_string_dict[key] = (p, u)
# Add "pretty" variants, too
ppstr = pstring(p)
_unit_string_dict[pstr*upstr] = (p, u)
_unit_string_dict[ppstr*ustr] = (p, u)
_unit_string_dict[ppstr*upstr] = (p, u)
end
end
end
function _unit_gen_dict(table)
for (u, rest) in table
key = string(u)
# println(key, ": ", u)
_unit_string_dict[key] = (SINone, u)
upstr = pstring(u)
if key != upstr
_unit_string_dict[upstr] = (SINone, u)
end
end
end
# Unknown unit
type Unknown <: UnitBase end
# Length units
type Meter <: UnitBase end
type Angstrom <: UnitBase end
type Inch <: UnitBase end
type Foot <: UnitBase end
type Yard <: UnitBase end
type Mile <: UnitBase end
type LightYear <: UnitBase end
type Parsec <: UnitBase end
type AstronomicalUnit <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Meter, Meter, 1, "m", "m", "m", "meter")
(Angstrom, Meter, 1e-10, "A", "\u212b",L"$\AA$","angstrom")
(Inch, Meter, 25.4/1000, "in", "in", "in", "inch")
(Foot, Meter, 12*25.4/1000, "ft", "ft", "ft", "foot")
(Yard, Meter, 36*25.4/1000, "yd", "yd", "yd", "yard")
(Mile, Meter, 5280*12*25.4/1000,"mi", "mi", "mi", "mile")
(LightYear,Meter, 9.4605284e15, "ly", "ly", "ly", "lightyear")
(Parsec, Meter, 3.08568025e16, "pc", "pc", "pc", "parsec")
(AstronomicalUnit, Meter, 149_597_870_700, "AU", "AU", "AU", "astronomical unit")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix({utable[1]}) # parse prefixes only for Meter
_unit_gen_dict(utable[2:end])
end
# Time units
type Second <: UnitBase end
type Minute <: UnitBase end
type Hour <: UnitBase end
type Day <: UnitBase end
type Week <: UnitBase end
type YearJulian <: UnitBase end
type PlanckTime <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Second, Second, 1, "s", "s", "s", "second")
(Minute, Second, 60, "min", "min", "min", "minute")
(Hour, Second, 3600, "hr", "hr", "hr", "hour")
(Day, Second, 86400, "d", "d", "d", "day")
(YearJulian, Second, 365.25*86400, "yr", "yr", "yr", "year")
(PlanckTime, Second, 5.3910632e-44, "tP", "tP", L"$t_P$","Planck time")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix({utable[1]}) # prefixes are only common for seconds
_unit_gen_dict(utable[2:end])
end
# Rate units
type Herz <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Herz, Herz, 1, "Hz", "Hz", "Hz", "Herz")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix(utable)
end
# Mass units
# (note English units like pounds are technically weight, not mass)
type Gram <: UnitBase end
type AtomicMassUnit <: UnitBase end
type Dalton <: UnitBase end
type PlanckMass <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Gram, Gram, 1, "g", "g", "g", "gram")
(AtomicMassUnit, Gram, 1.66053892173e-24, "amu","amu", "amu", "atomic mass unit")
(Dalton, Gram, 1.66053892173e-24, "Da", "Da", "Da", "Dalton")
(PlanckMass, Gram, 2.1765113e-5, "mP", "mP", L"$m_P$","Planck mass")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix({utable[1]})
_unit_gen_dict(utable[2:end])
end
# Electric current units
type Ampere <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Ampere, Ampere, 1, "A", "A", "A", "ampere")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix(utable)
end
# Temperature units
# The conversions for these are not just a product
type Kelvin <: UnitBase end
type Celsius <: UnitBase end
type Fahrenheit <: UnitBase end
let
# Unit RefUnit ToRef FromRef show pshow lshow fshow
const utable = {
(Kelvin, Kelvin, x->x, x->x, "K", "K", "K", "Kelvin")
(Celsius, Kelvin, x->x+273.15, x->x-273.15,"C", "C", "C", "Celsius")
(Fahrenheit, Kelvin, x->(x+459.67)*5/9, x->x*9/5-459.67,"F","F", "F", "Fahrenheit")
}
_unit_gen_func(utable)
_unit_gen_dict_with_prefix({utable[1]}) # prefixes are only common for Kelvin
_unit_gen_dict(utable[2:end])
end
# Luminosity units
type Candela <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Candela, Candela, 1, "cd", "cd", "cd", "candela")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix(utable)
end
# Amount units
type Mole <: UnitBase end
type Entities <: UnitBase end
let
# Unit RefUnit ToRef show pshow lshow fshow
const utable = {
(Mole, Mole, 1, "mol", "mol", "mol", "mole")
(Entities, Mole, 1/6.0221417930e23, "", "", "", "entities")
}
_unit_gen_func_multiplicative(utable)
_unit_gen_dict_with_prefix({utable[1]})
end
# Parsing string to extract Quantity
function parse_quantity(s::String, strict::Bool)
# Find the last character of the numeric component
m = match(r"[0-9\.\+-](?![0-9\.\+-])", s)
if m == nothing
error("String does not have a 'value unit' structure")
end
val = float64(s[1:m.offset])
ustr = strip(s[m.offset+1:end])
if isempty(ustr)
if strict
error("String does not have a 'value unit' structure")
else
return Quantity(SINone, Unknown, val)
end
end
(prefix, unit) = _unit_string_dict[ustr]
return Quantity(prefix, unit, val)
end
parse_quantity(s::String) = parse_quantity(s, true)
# Conversion between units
function convert{Uin<:UnitBase, Uout<:UnitBase, Pin<:SIPrefix, Pout<:SIPrefix, T}(::Type{Unit{Pout, Uout}}, q::Quantity{Pin, Uin, T})
if reference(Uin) != reference(Uout)
error("Not convertable")
end
return Quantity(Pout, Uout, from_reference(Uout)(to_reference(Uin)(q.value*to_reference(Pin))) / to_reference(Pout))
end