Skip to content

Commit 060ae5b

Browse files
committed
Merge pull request #4393 from embray/analytic_functions/issue-4171
Workaround to bug in #4171
1 parent 6fa0ff5 commit 060ae5b

3 files changed

Lines changed: 31 additions & 6 deletions

File tree

‎CHANGES.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ API Changes
8888
Bug Fixes
8989
^^^^^^^^^
9090

91+
- ``astropy.analytic_functions``
92+
93+
- Fixed the blackbody functions' handling of overflows on some platforms
94+
(Windows with MSVC, older Linux versions) with a buggy ``expm1`` function.
95+
[#4393]
96+
9197
- ``astropy.config``
9298

9399
- ``astropy.constants``

‎astropy/analytic_functions/blackbody.py‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
FLAM = u.erg / (u.cm**2 * u.s * u.AA)
2323

2424

25+
# Some platform implementations of expm1() are buggy and Numpy uses
26+
# them anyways--the bug is that on certain large inputs it returns
27+
# NaN instead of INF like it should (it should only return NaN on a
28+
# NaN input
29+
# See https://github.com/astropy/astropy/issues/4171
30+
_has_buggy_expm1 = np.isnan(np.expm1(1000))
31+
32+
2533
def blackbody_nu(in_x, temperature):
2634
"""Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`.
2735
@@ -70,9 +78,24 @@ def blackbody_nu(in_x, temperature):
7078
warnings.warn('Input contains invalid wavelength/frequency value(s)',
7179
AstropyUserWarning)
7280

81+
log_boltz = const.h * freq / (const.k_B * temp)
82+
boltzm1 = np.expm1(log_boltz)
83+
84+
if _has_buggy_expm1:
85+
# Replace incorrect nan results with infs--any result of 'nan' is
86+
# incorrect unless the input (in log_boltz) happened to be nan to begin
87+
# with. (As noted in #4393 ideally this would be replaced by a version
88+
# of expm1 that doesn't have this bug, rather than fixing incorrect
89+
# results after the fact...)
90+
boltzm1_nans = np.isnan(boltzm1)
91+
if np.any(boltzm1_nans):
92+
if boltzm1.isscalar and not np.isnan(log_boltz):
93+
boltzm1 = np.inf
94+
else:
95+
boltzm1[np.where(~np.isnan(log_boltz) & boltzm1_nans)] = np.inf
96+
7397
# Calculate blackbody flux
74-
bb_nu = (2.0 * const.h * freq ** 3 /
75-
(const.c ** 2 * np.expm1(const.h * freq / (const.k_B * temp))))
98+
bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * boltzm1))
7699
flux = bb_nu.to(FNU, u.spectral_density(freq))
77100

78101
return flux / u.sr # Add per steradian to output flux unit

‎astropy/analytic_functions/tests/test_blackbody.py‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import (absolute_import, division, print_function,
44
unicode_literals)
55

6-
import os
76
import warnings
87
# THIRD-PARTY
98
import numpy as np
@@ -26,7 +25,6 @@
2625
__doctest_skip__ = ['*']
2726

2827

29-
@pytest.mark.skipif("os.environ.get('APPVEYOR')", reason="fails on AppVeyor")
3028
@pytest.mark.skipif('not HAS_SCIPY')
3129
def test_blackbody_scipy():
3230
"""Test Planck function.
@@ -47,7 +45,6 @@ def test_blackbody_scipy():
4745
np.testing.assert_allclose(intflux, ans.value, rtol=0.01) # 1% accuracy
4846

4947

50-
@pytest.mark.skipif("os.environ.get('APPVEYOR')", reason="fails on AppVeyor")
5148
def test_blackbody_overflow():
5249
"""Test Planck function with overflow."""
5350
photlam = u.photon / (u.cm**2 * u.s * u.AA)
@@ -69,7 +66,6 @@ def test_blackbody_overflow():
6966
assert flux.value == 0
7067

7168

72-
@pytest.mark.skipif("os.environ.get('APPVEYOR')", reason="fails on AppVeyor")
7369
def test_blackbody_synphot():
7470
"""Test that it is consistent with IRAF SYNPHOT BBFUNC."""
7571
# Solid angle of solar radius at 1 kpc

0 commit comments

Comments
 (0)