1 from __future__ import division
2 """
3 Units class hierarchy: is used to manage both wavelength and flux
4 unit conversions
5
6 Warning: vegamag unit conversions require spectrum and locations => circular
7 imports.
8 """
9
10 import math
11 import numpy as N
12 import locations, spectrum
13
14 import observationmode
15
16
17
18
19 C = 2.99792458e18
20 H = 6.62620E-27
21 HC = H * C
22 ABZERO = -48.60
23 STZERO = -21.10
24
25
26
27
29 """This needs to be a factory function in order to return an object
30 of the correct subclass."""
31 if isinstance(uname,BaseUnit):
32 return uname
33 else:
34 try:
35 if issubclass(uname,BaseUnit):
36 return uname()
37 except TypeError:
38
39 try:
40 return factory(uname)
41 except KeyError:
42 if uname == str(None):
43 return None
44 else:
45 raise ValueError("Unknown units %s"%uname)
46
47
49 """Method to allow smart comparisons between classes, instances,
50 and string representations of units and give the right answer."""
51 match=False
52
53 if a == b:
54 return True
55 else:
56
57 try:
58 if isinstance(a,b):
59 return True
60 except TypeError:
61 try:
62 if isinstance(b,a):
63 return True
64 except TypeError:
65
66 try:
67 if isinstance(a,type(b)):
68 return True
69 except TypeError:
70 try:
71 if isinstance(b,type(a)):
72 return True
73 except TypeError:
74
75 if str(a).lower() == str(b).lower():
76 return True
77 else:
78 return False
79
80
81
82
84 """ Base class for all units; defines UI"""
86 self.Dispatch=None
87 self.name=uname
88
91
92 - def Convert(self,wave,flux,target_units):
93
94 try:
95 return self.Dispatch[target_units.lower()](wave,flux)
96 except KeyError:
97 raise TypeError("%s cannot be converted to %s"%(self.name,
98 target_units))
99
101 """All WaveUnits know how to convert themselves to Angstroms"""
103 self.name=None
104 self.isFlux = False
105 self.Dispatch = {'angstrom' : self.ToAngstrom}
106
107 - def Convert(self,wave,target_units):
108 """WaveUnits only need a wavelength table to do a conversion."""
109 try:
110 return self.Dispatch[target_units.lower()](wave)
111 except KeyError:
112 raise TypeError("%s cannot be converted to %s"%(self.name,
113 target_units))
114
116 raise NotImplementedError("Required method ToAngstrom not yet implemented")
117
119 """All FluxUnits know how to convert themselves to Photlam"""
121 self.isFlux = True
122 self.isMag = False
123 self.isDensity = True
124 self.name=None
125 self.Dispatch = {'photlam':self.ToPhotlam}
126 self.nativewave = Angstrom
127
128
129
130
131
132
133 - def Convert(self,wave,flux,target_units):
134 """FluxUnits need both wavelength and flux tables to do a unit conversion."""
135 try:
136 return self.Dispatch[target_units](wave,flux)
137 except KeyError:
138 raise TypeError("%s is not a valid flux unit"%(target_units))
139
141 raise NotImplementedError("Required method ToPhotlam not yet implemented")
142
143
145 """Base class for magnitudes, which often require special handling"""
147 FluxUnits.__init__(self)
148 self.isMag=True
149 self.linunit=None
150 self.zeropoint=None
151
152
153
156 WaveUnits.__init__(self)
157 self.name = 'angstrom'
158 self.Dispatch = {'angstrom' : self.ToAngstrom,
159 'angstroms' : self.ToAngstrom,
160 'nm': self.ToNm,
161 'micron': self.ToMicron,
162 'microns': self.ToMicron,
163 '1/um': self.ToInverseMicron,
164 'inversemicron': self.ToInverseMicron,
165 'inversemicrons': self.ToInverseMicron,
166 'mm': self.ToMm,
167 'cm': self.ToCm,
168 'm': self.ToMeter,
169 'hz': self.ToHz}
170
171
174
175 - def ToNm(self, wave):
177
180
183
184 - def ToMm(self, wave):
186
187 - def ToCm(self, wave):
189
191 return wave * 1.0e-10
192
193 - def ToHz(self, wave):
195
196
197
198
200 ''' photlam = photons cm^-2 s^-1 Ang^-1)'''
202 FluxUnits.__init__(self)
203 self.name = 'photlam'
204 self.Dispatch = {'flam': self.ToFlam,
205 'fnu': self.ToFnu,
206 'photlam': self.ToPhotlam,
207 'photnu': self.ToPhotnu,
208 'jy': self.ToJy,
209 'mjy': self.TomJy,
210 'mujy': self.TomuJy,
211 'microjy': self.TomuJy,
212 'ujy': self.TomuJy,
213 'njy': self.TonJy,
214 'nanojy': self.TonJy,
215 'abmag': self.ToABMag,
216 'stmag': self.ToSTMag,
217 'obmag': self.ToOBMag,
218 'vegamag': self.ToVegaMag,
219 'counts': self.ToCounts,
220 'counts': self.ToCounts}
221
222 self.nativewave = Angstrom
223
224
231
232
233 - def ToFlam(self, wave, flux):
235
236 - def ToFnu(self, wave, flux):
238
241
244
245 - def ToJy(self, wave, flux):
247
248 - def TomJy(self, wave, flux):
250
251 - def TomuJy(self, wave, flux):
253
254 - def TonJy(self, wave, flux):
256
260
264
269
275
278
279
280
281
282
283 -class Hz(WaveUnits):
290
298
300 """ Encapsulates some easy unit-conversion machinery. Angstrom
301 is not subclassed from here because it needs to be especially smart in
302 other ways. (Although multiple inheritence might be possible.)"""
304 return wave*self.factor
305
306 -class Nm(_MetricWavelength):
311
312 -class Micron(_MetricWavelength):
317
318 -class Mm(_MetricWavelength):
323
324 -class Cm(_MetricWavelength):
329
330 -class Meter(_MetricWavelength):
335
336
337
338
339
340 -class Flam(FluxUnits):
341 ''' flam = erg cm^-2 s^-1 Ang^-1'''
342
348
351
352
360
361
363 ''' photnu = photon cm^-2 s^-1 Hz^-1'''
368
371
372
380
381
382 -class Fnu(FluxUnits):
383 ''' fnu = erg cm^-2 s^-1 Hz^-1'''
388
391
392
400
401 -class Jy(FluxUnits):
402 ''' jy = 10^-23 erg cm^-2 s^-1 Hz^-1'''
407
410
411
419
420 -class mJy(FluxUnits):
421 ''' mjy = 10^-26 erg cm^-2 s^-1 Hz^-1'''
426
429
430
438
439 -class muJy(FluxUnits):
440 ''' mujy = 10^-29 erg cm^-2 s^-1 Hz^-1'''
445
448
454
455 -class nJy(FluxUnits):
456 ''' njy = 10^-32 erg cm^-2 s^-1 Hz^-1'''
461
464
470
471 -class ABMag(LogFluxUnits):
489
490 -class STMag(LogFluxUnits):
508
509 -class OBMag(LogFluxUnits):
516
520
522
523 total = band.throughput.sum()
524 return 2.5*math.log10(total)
525
531
535
537 sp=band*self.vegaspec
538 total=sp.integrate()
539 return 2.5*math.log10(total)
540
546
549
550
552
553 total = band.throughput.sum()
554 return 1.0/total
555
556
557
558
559
560 -def factory(uname, *args, **kwargs):
561 unitsClasses = {'flam' : Flam,
562 'fnu' : Fnu,
563 'photlam' : Photlam,
564 'photnu' : Photnu,
565 'jy' : Jy,
566 'mjy' : mJy,
567 'mujy' : muJy,
568 'microjy' : muJy,
569 'ujy' : muJy,
570 'njy' : nJy,
571 'nanojy' : nJy,
572 'abmag' : ABMag,
573 'stmag' : STMag,
574 'obmag' : OBMag,
575 'vegamag' : VegaMag,
576 'counts' : Counts,
577 'count' : Counts,
578 'angstrom' : Angstrom,
579 'angstroms' : Angstrom,
580 'nm' : Nm,
581 'micron' : Micron,
582 'microns' : Micron,
583 'um' : Micron,
584 'inversemicron': InverseMicron,
585 'inversemicrons': InverseMicron,
586 '1/um' : InverseMicron,
587 'mm' : Mm,
588 'cm' : Cm,
589 'm' : Meter,
590 'meter' : Meter,
591 'hz' : Hz}
592
593 key=uname.lower()
594 ans= unitsClasses[key]()
595 return ans
596
598 """ Compute delta wavelngth for an array of wavelengths.
599 If we had a WaveTable class, this function would be a method
600 on that class: possible refactoring candidate. """
601
602 last = wave.shape[0]-1
603
604 hold1 = N.empty(shape=wave.shape, dtype=N.float64)
605 hold2 = N.empty(shape=wave.shape, dtype=N.float64)
606
607 hold1[1::] = wave[0:last]
608 hold2[0:last] = wave[1::]
609
610 delta = (hold2 - hold1) / 2.0
611
612 delta[0] = wave[1] - wave[0]
613 delta[last] = wave[last] - wave[last-1]
614
615 return delta
616