Skip to content

Commit d26b432

Browse files
committed
Merge pull request #1560 from pllim/expand-flux-conv
Rebased in eteq/expand-flux-conv to clean up commit log a bit
2 parents 21f694b + 935350e commit d26b432

File tree

8 files changed

+246
-104
lines changed

8 files changed

+246
-104
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ New Features
107107
- Added new spectroscopic equivalencies for velocity conversions
108108
(relativistic, optical, and radio conventions are supported)
109109

110+
- The ``spectral`` equivalency now also handles wave number.
111+
112+
- The ``spectral_density`` equivalency now also accepts a Quantity for the
113+
frequency or wavelength. It also handles additional flux units.
114+
110115
- Added Brightness Temperature (antenna gain) equivalency for conversion
111116
between :math:`T_B` and flux density.
112117

astropy/units/equivalencies.py

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,48 +29,106 @@ def parallax():
2929
def spectral():
3030
"""
3131
Returns a list of equivalence pairs that handle spectral
32-
wavelength, frequency, and energy equivalences.
32+
wavelength, wave number, frequency, and energy equivalences.
3333
34-
Allows conversions between wavelength units, frequency units and
35-
energy units as they relate to light.
36-
"""
34+
Allows conversions between wavelength units, wave number units,
35+
frequency units, and energy units as they relate to light.
3736
37+
"""
38+
hc = _si.h.value * _si.c.value
39+
inv_m = si.m ** -1
3840
return [
3941
(si.m, si.Hz, lambda x: _si.c.value / x),
40-
(si.m, si.J, lambda x: (_si.c.value * _si.h.value) / x),
41-
(si.Hz, si.J, lambda x: _si.h.value * x)
42+
(si.m, si.J, lambda x: hc / x),
43+
(si.m, inv_m, lambda x: 1.0 / x),
44+
(si.Hz, si.J, lambda x: _si.h.value * x, lambda x: x / _si.h.value),
45+
(si.Hz, inv_m, lambda x: x / _si.c.value, lambda x: _si.c.value * x),
46+
(si.J, inv_m, lambda x: x / hc, lambda x: hc * x)
4247
]
4348

4449

45-
def spectral_density(sunit, sfactor):
50+
def spectral_density(wav, factor=None):
4651
"""
4752
Returns a list of equivalence pairs that handle spectral density
4853
with regard to wavelength and frequency.
54+
55+
Parameters
56+
----------
57+
wav : Quantity
58+
Quantity associated with values being converted
59+
(e.g., wavelength or frequency).
60+
61+
Notes
62+
-----
63+
The ``factor`` argument is left for backward-compatibility with the syntax
64+
``spectral_density(unit, factor)`` but users are encouraged to use
65+
``spectral_density(factor * unit)`` instead.
66+
4967
"""
50-
c_Aps = _si.c.value * 10 ** 10
68+
from .core import UnitBase
69+
70+
if isinstance(wav, UnitBase):
71+
if factor is None:
72+
raise ValueError(
73+
'If ``wav`` is specified as a unit, ``factor`` should be set')
74+
wav = factor * wav # Convert to Quantity
75+
76+
c_Aps = _si.c.to(si.AA / si.s).value # Angstrom/s
77+
h_cgs = _si.h.cgs.value # erg * s
78+
hc = c_Aps * h_cgs
5179

5280
fla = cgs.erg / si.angstrom / si.cm ** 2 / si.s
5381
fnu = cgs.erg / si.Hz / si.cm ** 2 / si.s
5482
nufnu = cgs.erg / si.cm ** 2 / si.s
5583
lafla = nufnu
84+
photlam = astrophys.photon / (si.cm ** 2 * si.s * si.AA)
85+
photnu = astrophys.photon / (si.cm ** 2 * si.s * si.Hz)
5686

5787
def converter(x):
58-
return x * (sunit.to(si.AA, sfactor, spectral()) ** 2 / c_Aps)
88+
return x * (wav.to(si.AA, spectral()).value ** 2 / c_Aps)
5989

6090
def iconverter(x):
61-
return x / (sunit.to(si.AA, sfactor, spectral()) ** 2 / c_Aps)
91+
return x / (wav.to(si.AA, spectral()).value ** 2 / c_Aps)
6292

6393
def converter_fnu_nufnu(x):
64-
return x * sunit.to(si.Hz, sfactor, spectral())
94+
return x * wav.to(si.Hz, spectral()).value
6595

6696
def iconverter_fnu_nufnu(x):
67-
return x / sunit.to(si.Hz, sfactor, spectral())
97+
return x / wav.to(si.Hz, spectral()).value
6898

6999
def converter_fla_lafla(x):
70-
return x * sunit.to(si.AA, sfactor, spectral())
100+
return x * wav.to(si.AA, spectral()).value
71101

72102
def iconverter_fla_lafla(x):
73-
return x / sunit.to(si.AA, sfactor, spectral())
103+
return x / wav.to(si.AA, spectral()).value
104+
105+
def converter_photlam_fla(x):
106+
return hc * x / wav.to(si.AA, spectral()).value
107+
108+
def iconverter_photlam_fla(x):
109+
return x * wav.to(si.AA, spectral()).value / hc
110+
111+
def converter_photlam_fnu(x):
112+
return h_cgs * x * wav.to(si.AA, spectral()).value
113+
114+
def iconverter_photlam_fnu(x):
115+
return x / (wav.to(si.AA, spectral()).value * h_cgs)
116+
117+
def converter_photlam_photnu(x):
118+
return x * wav.to(si.AA, spectral()).value ** 2 / c_Aps
119+
120+
def iconverter_photlam_photnu(x):
121+
return c_Aps * x / wav.to(si.AA, spectral()).value ** 2
122+
123+
converter_photnu_fnu = converter_photlam_fla
124+
125+
iconverter_photnu_fnu = iconverter_photlam_fla
126+
127+
def converter_photnu_fla(x):
128+
return x * hc * c_Aps / wav.to(si.AA, spectral()).value ** 3
129+
130+
def iconverter_photnu_fla(x):
131+
return x * wav.to(si.AA, spectral()).value ** 3 / (hc * c_Aps)
74132

75133
return [
76134
(si.AA, fnu, converter, iconverter),
@@ -79,6 +137,11 @@ def iconverter_fla_lafla(x):
79137
(fla, si.Hz, converter, iconverter),
80138
(fnu, nufnu, converter_fnu_nufnu, iconverter_fnu_nufnu),
81139
(fla, lafla, converter_fla_lafla, iconverter_fla_lafla),
140+
(photlam, fla, converter_photlam_fla, iconverter_photlam_fla),
141+
(photlam, fnu, converter_photlam_fnu, iconverter_photlam_fnu),
142+
(photlam, photnu, converter_photlam_photnu, iconverter_photlam_photnu),
143+
(photnu, fnu, converter_photnu_fnu, iconverter_photnu_fnu),
144+
(photnu, fla, converter_photnu_fla, iconverter_photnu_fla)
82145
]
83146

84147

@@ -87,14 +150,14 @@ def doppler_radio(rest):
87150
Return the equivalency pairs for the radio convention for velocity.
88151
89152
The radio convention for the relation between velocity and frequency is:
90-
153+
91154
:math:`V = c \frac{f_0 - f}{f_0} ; f(V) = f_0 ( 1 - V/c )`
92155
93156
Parameters
94157
----------
95158
rest : Quantity
96159
Any quantity supported by the standard spectral equivalencies
97-
(wavelength, energy, frequency)
160+
(wavelength, energy, frequency, wave number).
98161
99162
References
100163
----------
@@ -159,7 +222,7 @@ def doppler_optical(rest):
159222
----------
160223
rest : Quantity
161224
Any quantity supported by the standard spectral equivalencies
162-
(wavelength, energy, frequency)
225+
(wavelength, energy, frequency, wave number).
163226
164227
References
165228
----------
@@ -225,7 +288,7 @@ def doppler_relativistic(rest):
225288
----------
226289
rest : Quantity
227290
Any quantity supported by the standard spectral equivalencies
228-
(wavelength, energy, frequency)
291+
(wavelength, energy, frequency, wave number).
229292
230293
References
231294
----------
@@ -294,10 +357,10 @@ def mass_energy():
294357

295358
return [(si.kg, si.J, lambda x: x * _si.c.value ** 2,
296359
lambda x: x / _si.c.value ** 2),
297-
(si.kg / si.m ** 2, si.J / si.m ** 2 ,
360+
(si.kg / si.m ** 2, si.J / si.m ** 2 ,
298361
lambda x: x * _si.c.value ** 2,
299362
lambda x: x / _si.c.value ** 2),
300-
(si.kg / si.m ** 3, si.J / si.m ** 3 ,
363+
(si.kg / si.m ** 3, si.J / si.m ** 3 ,
301364
lambda x: x * _si.c.value ** 2,
302365
lambda x: x / _si.c.value ** 2),
303366
(si.kg / si.s, si.J / si.s , lambda x: x * _si.c.value ** 2,

astropy/units/physical.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ def get_physical_type(unit):
117117
(si.cd / si.m ** 2, 'luminance'),
118118
(astrophys.Jy, 'spectral flux density'),
119119
(cgs.erg / si.angstrom / si.cm ** 2 / si.s, 'spectral flux density wav'),
120+
(astrophys.photon / si.Hz / si.cm ** 2 / si.s, 'photon flux density'),
121+
(astrophys.photon / si.AA / si.cm ** 2 / si.s, 'photon flux density wav'),
120122
(astrophys.R, 'photon flux'),
121123
(astrophys.bit, 'data quantity'),
122124
(astrophys.bit / si.s, 'bandwidth')

astropy/units/tests/test_equivalencies.py

Lines changed: 102 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -189,57 +189,136 @@ def test_spectral2():
189189
c = u.AA.to(u.J, 1, u.spectral())
190190
assert_allclose(b, c)
191191

192+
c = u.J.to(u.Hz, b, u.spectral())
193+
assert_allclose(a, c)
194+
192195

193196
def test_spectral3():
194197
a = u.nm.to(u.Hz, [1000, 2000], u.spectral())
195198
assert_allclose(a, [2.99792458e+14, 1.49896229e+14])
196199

197200

198-
def test_spectraldensity():
201+
@pytest.mark.parametrize(
202+
('in_val', 'in_unit'),
203+
[([0.1, 5000.0, 10000.0], u.AA),
204+
([2.99792458e+19, 5.99584916e+14, 2.99792458e+14], u.Hz),
205+
([1.98644568e-14, 3.97289137e-19, 1.98644568e-19], u.J)])
206+
def test_spectral4(in_val, in_unit):
207+
"""Wave number conversion w.r.t. wavelength, freq, and energy."""
208+
# Forward
209+
a = in_unit.to(u.micron ** -1, in_val, u.spectral())
210+
assert_allclose(a, [1e+5, 2.0, 1.0])
211+
212+
# Backward
213+
b = (u.micron ** -1).to(in_unit, [1e+5, 2.0, 1.0], u.spectral())
214+
assert_allclose(b, in_val)
199215

216+
217+
def test_spectraldensity():
200218
a = u.AA.to(u.Jy, 1, u.spectral_density(u.eV, 2.2))
201219
assert_allclose(a, 1059416252057.8357, rtol=1e-4)
202220

203221
b = u.Jy.to(u.AA, a, u.spectral_density(u.eV, 2.2))
204222
assert_allclose(b, 1)
205223

224+
c = u.AA.to(u.Jy, 1, u.spectral_density(2.2 * u.eV))
225+
assert_allclose(c, 1059416252057.8357, rtol=1e-4)
226+
227+
d = u.Jy.to(u.AA, c, u.spectral_density(2.2 * u.eV))
228+
assert_allclose(d, 1)
229+
206230

207231
def test_spectraldensity2():
208232
flambda = u.erg / u.angstrom / u.cm ** 2 / u.s
209233
fnu = u.erg / u.Hz / u.cm ** 2 / u.s
210234

211-
a = flambda.to(fnu, 1, u.spectral_density(u.AA, 3500))
235+
a = flambda.to(fnu, 1, u.spectral_density(u.Quantity(3500, u.AA)))
212236
assert_allclose(a, 4.086160166177361e-12)
213237

214238

215239
def test_spectraldensity3():
216-
217240
# Define F_nu in Jy
218241
f_nu = u.Jy
219242

243+
# Define F_lambda in ergs / cm^2 / s / micron
244+
f_lambda = u.erg / u.cm ** 2 / u.s / u.micron
245+
246+
# 1 GHz
247+
one_ghz = u.Quantity(1, u.GHz)
248+
220249
# Convert to ergs / cm^2 / s / Hz
221250
assert_allclose(f_nu.to(u.erg / u.cm ** 2 / u.s / u.Hz, 1.), 1.e-23, 10)
222251

223252
# Convert to ergs / cm^2 / s at 10 Ghz
224253
assert_allclose(f_nu.to(u.erg / u.cm ** 2 / u.s, 1.,
225-
equivalencies=u.spectral_density(u.GHz, 10)), 1.e-13, 10)
254+
equivalencies=u.spectral_density(one_ghz * 10)),
255+
1.e-13, 10)
226256

227-
# Convert to ergs / cm^2 / s / micron at 1 Ghz
228-
assert_allclose(f_nu.to(u.erg / u.cm ** 2 / u.s / u.micron, 1.,
229-
equivalencies=u.spectral_density(u.Hz, 1.e9)),
257+
# Convert to F_lambda at 1 Ghz
258+
assert_allclose(f_nu.to(f_lambda, 1.,
259+
equivalencies=u.spectral_density(one_ghz)),
230260
3.335640951981521e-20, 10)
231261

232-
# Define F_lambda in ergs / cm^2 / s / micron
233-
f_lambda = u.erg / u.cm ** 2 / u.s / u.micron
234-
235262
# Convert to Jy at 1 Ghz
236263
assert_allclose(f_lambda.to(u.Jy, 1.,
237-
equivalencies=u.spectral_density(u.Hz, 1.e9)),
264+
equivalencies=u.spectral_density(one_ghz)),
238265
1. / 3.335640951981521e-20, 10)
239266

240267
# Convert to ergs / cm^2 / s at 10 microns
241268
assert_allclose(f_lambda.to(u.erg / u.cm ** 2 / u.s, 1.,
242-
equivalencies=u.spectral_density(u.micron, 10.)), 10., 10)
269+
equivalencies=u.spectral_density(u.Quantity(10, u.micron))),
270+
10., 10)
271+
272+
273+
def test_spectraldensity3():
274+
"""PHOTLAM and PHOTNU conversions."""
275+
flam = u.erg / (u.cm ** 2 * u.s * u.AA)
276+
fnu = u.erg / (u.cm ** 2 * u.s * u.Hz)
277+
photlam = u.photon / (u.cm ** 2 * u.s * u.AA)
278+
photnu = u.photon / (u.cm ** 2 * u.s * u.Hz)
279+
280+
wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA)
281+
flux_photlam = [9.7654e-3, 1.003896e-2, 9.78473e-3]
282+
flux_photnu = [8.00335589e-14, 8.23668949e-14, 8.03700310e-14]
283+
flux_flam = [3.9135e-14, 4.0209e-14, 3.9169e-14]
284+
flux_fnu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25]
285+
flux_jy = [3.20735792e-2, 3.29903646e-2, 3.21727226e-2]
286+
287+
# PHOTLAM <--> FLAM
288+
assert_allclose(photlam.to(
289+
flam, flux_photlam, u.spectral_density(wave)), flux_flam, rtol=1e-6)
290+
assert_allclose(flam.to(
291+
photlam, flux_flam, u.spectral_density(wave)), flux_photlam, rtol=1e-6)
292+
293+
# PHOTLAM <--> FNU
294+
assert_allclose(photlam.to(
295+
fnu, flux_photlam, u.spectral_density(wave)), flux_fnu, rtol=1e-6)
296+
assert_allclose(fnu.to(
297+
photlam, flux_fnu, u.spectral_density(wave)), flux_photlam, rtol=1e-6)
298+
299+
# PHOTLAM <--> Jy
300+
assert_allclose(photlam.to(
301+
u.Jy, flux_photlam, u.spectral_density(wave)), flux_jy, rtol=1e-6)
302+
assert_allclose(u.Jy.to(
303+
photlam, flux_jy, u.spectral_density(wave)), flux_photlam, rtol=1e-6)
304+
305+
# PHOTLAM <--> PHOTNU
306+
assert_allclose(photlam.to(
307+
photnu, flux_photlam, u.spectral_density(wave)), flux_photnu, rtol=1e-6)
308+
assert_allclose(photnu.to(
309+
photlam, flux_photnu, u.spectral_density(wave)), flux_photlam, rtol=1e-6)
310+
311+
# PHOTNU <--> FNU
312+
assert_allclose(photnu.to(
313+
fnu, flux_photnu, u.spectral_density(wave)), flux_fnu, rtol=1e-6)
314+
assert_allclose(fnu.to(
315+
photnu, flux_fnu, u.spectral_density(wave)), flux_photnu, rtol=1e-6)
316+
317+
# PHOTNU <--> FLAM
318+
assert_allclose(photnu.to(
319+
flam, flux_photnu, u.spectral_density(wave)), flux_flam, rtol=1e-6)
320+
assert_allclose(flam.to(
321+
photnu, flux_flam, u.spectral_density(wave)), flux_photnu, rtol=1e-6)
243322

244323

245324
def test_equivalent_units():
@@ -257,7 +336,7 @@ def test_equivalent_units2():
257336
units = set(u.Hz.find_equivalent_units(u.spectral()))
258337
match = set(
259338
[u.AU, u.Angstrom, u.Hz, u.J, u.Ry, u.cm, u.eV, u.erg, u.lyr,
260-
u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci])
339+
u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci, u.k])
261340
assert units == match
262341

263342
from .. import imperial
@@ -268,13 +347,13 @@ def test_equivalent_units2():
268347
imperial.cal, u.cm, u.eV, u.erg, imperial.ft,
269348
imperial.inch, imperial.kcal, u.lyr, u.m, imperial.mi,
270349
u.micron, u.pc, u.solRad, imperial.yd, u.Bq, u.Ci,
271-
imperial.nmi])
350+
imperial.nmi, u.k])
272351
assert units == match
273352

274353
units = set(u.Hz.find_equivalent_units(u.spectral()))
275354
match = set(
276355
[u.AU, u.Angstrom, u.Hz, u.J, u.Ry, u.cm, u.eV, u.erg, u.lyr,
277-
u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci])
356+
u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci, u.k])
278357
assert units == match
279358

280359

@@ -294,13 +373,14 @@ def test_irrelevant_equivalency():
294373
with pytest.raises(u.UnitsException):
295374
u.m.to(u.kg, equivalencies=[(u.m, u.l)])
296375

376+
297377
def test_brightness_temperature():
298-
omega_B = np.pi*(50*u.arcsec)**2
378+
omega_B = np.pi * (50 * u.arcsec) ** 2
299379
nu = u.GHz * 5
300380
tb = 7.05258885885 * u.K
301-
np.testing.assert_almost_equal(tb.value,
302-
(1*u.Jy).to(u.K,
303-
equivalencies=u.brightness_temperature(omega_B, nu)).value)
304-
np.testing.assert_almost_equal(1.0,
305-
tb.to(u.Jy,
306-
equivalencies=u.brightness_temperature(omega_B, nu)).value)
381+
np.testing.assert_almost_equal(
382+
tb.value, (1 * u.Jy).to(
383+
u.K, equivalencies=u.brightness_temperature(omega_B, nu)).value)
384+
np.testing.assert_almost_equal(
385+
1.0, tb.to(
386+
u.Jy, equivalencies=u.brightness_temperature(omega_B, nu)).value)

0 commit comments

Comments
 (0)