|
22 | 22 | FLAM = u.erg / (u.cm**2 * u.s * u.AA) |
23 | 23 |
|
24 | 24 |
|
| 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 | + |
25 | 33 | def blackbody_nu(in_x, temperature): |
26 | 34 | """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. |
27 | 35 |
|
@@ -70,9 +78,24 @@ def blackbody_nu(in_x, temperature): |
70 | 78 | warnings.warn('Input contains invalid wavelength/frequency value(s)', |
71 | 79 | AstropyUserWarning) |
72 | 80 |
|
| 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 | + |
73 | 97 | # 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)) |
76 | 99 | flux = bb_nu.to(FNU, u.spectral_density(freq)) |
77 | 100 |
|
78 | 101 | return flux / u.sr # Add per steradian to output flux unit |
|
0 commit comments