The thermal model requires boundary conditions at the surface (top) and at depth (bottom). These follow Hayne et al. (2017), Appendix A1.1 and A2.1 (Eqs. A7--A12, A21--A30).
The surface energy balance determines the surface temperature
where:
-
$\varepsilon$ is the infrared emissivity (0.95 for the Moon) -
$\sigma$ is the Stefan-Boltzmann constant -
$Q_s$ is the absorbed solar flux - The last term is the conductive heat flux from below
The absorbed solar flux depends on the solar incidence angle
where
The albedo model follows Keihm (1984) and Vasavada et al. (2012), as used in Hayne et al. (2017), Eq. A8:
where
The surface energy balance is a nonlinear equation in
converges when |ΔT| < ε (default: 0.1 K). A maximum iteration count (default: 100) prevents infinite loops.
The surface temperature gradient is approximated using a second-order forward difference:
The surface boundary condition uses operator splitting: the nonlinear
This contrasts with linearization approaches (e.g., Schorghofer &
Khatiwala 2024, PSJ 5:120) that approximate
and fold the linearized radiation term into the tridiagonal matrix. Williams
& Curry (1977, IJNME 11:1605) showed that this can produce spurious surface
temperature oscillations when
The operator-splitting approach avoids this conditional instability entirely, at the cost of first-order accuracy in the boundary-condition coupling (vs. second-order for a fully coupled Crank-Nicolson scheme). In practice, the accuracy trade-off is small because the Newton iteration converges the surface temperature to within the DTSURF tolerance (0.1 K) at every time step.
When use_volterra_predictor=True, the Newton iteration is initialized with
a second-order accurate prediction of the surface temperature from the
Volterra integral equation (Schorghofer & Khatiwala 2024, PSJ 5:120, Eq. 62):
where
The predictor provides a better initial guess for the Newton iteration, particularly at sunrise where the surface temperature changes rapidly and the previous time step's temperature is a poor starting point. It does not change the final converged result — only the number of Newton iterations needed.
To enable:
config = Configurator(use_volterra_predictor=True)Or via YAML:
volterra_predictor: trueThe bottom boundary is set by the interior heat flux
For the Moon,
Permanently shadowed regions (PSRs) on the floors of polar craters receive no
direct sunlight, but they are heated by sunlight scattered off the illuminated
crater walls and by thermal infrared radiation re-emitted by those walls. heat1d
models this using the analytical framework of
Ingersoll & Svitek (1992)
for spherical-cap (bowl-shaped) craters.
A bowl-shaped crater is parameterized by its depth-to-diameter ratio
The corresponding crater half-angle
Typical simple lunar craters have d/D ≈ 0.1–0.2, giving f ≈ 0.04–0.14 and β ≈ 23°–44°.
A permanently shadowed region can exist only when the Sun never rises above the
crater rim as seen from the floor. The maximum solar elevation angle at
latitude
A PSR exists when:
This sets a minimum depth-to-diameter ratio for PSR formation at a given latitude. Inverting the geometry relations gives:
For the Moon (ε ≈ 1.54°):
| Latitude | Min |
Min |
|
|---|---|---|---|
| 90° (pole) | 1.5° | 0.007 | 1.5° |
| 85° | 6.5° | 0.029 | 6.5° |
| 80° | 11.5° | 0.051 | 11.5° |
| 70° | 21.5° | 0.097 | 21.5° |
At lower latitudes the required crater depth increases rapidly, and below ~70° latitude even the deepest simple craters (d/D ≈ 0.2) cannot sustain a PSR on the Moon.
If PSR mode is enabled at a latitude where the viability condition is not met, the model issues a warning and runs anyway — the crater floor will receive direct sunlight at high solar elevations, but the cavity scattering model still applies.
The concave crater geometry traps infrared radiation: thermal photons emitted by the floor are partially intercepted and re-absorbed by the walls, and vice versa. This increases the effective emissivity beyond the flat-surface value (Ingersoll & Svitek 1992, Eq. 7):
For
In the surface energy balance,
The absorbed solar flux at the PSR floor comes entirely from scattering and re-emission by the sunlit crater walls (Ingersoll & Svitek 1992, Eq. 8):
where
The three factors have clear physical meanings:
-
$S_0 \sin e_0 / r^2$ : solar flux intercepted by the crater opening (projected area) -
$f (1 - A) / (1 - Af)$ : fraction absorbed by the cavity after multiple wall reflections -
$\varepsilon + A(1 - f)$ : partition between thermal re-emission ($\varepsilon$ ) reaching the floor and reflected sunlight escaping through the opening ($A(1-f)$)
At night (
The PSR flux model requires the solar elevation angle
This uses the same orbital mechanics as the flat-surface model (see Theory). The PSR model clamps sin e₀ ≥ 0 so that negative values (Sun below the horizon) produce zero flux rather than unphysical negative flux.
When an external flux time series is provided (e.g., from JPL Horizons/SPICE
via --use-spice), the precomputed direct flux replaces the analytical
illumination model during the output phase. In the current implementation, PSR
mode and external flux series are mutually exclusive: the PSR cavity
scattering formula requires the solar elevation angle, which is computed from
the analytical orbit model rather than extracted from a precomputed flux value.
For SPICE-driven PSR simulations, the recommended workflow is:
- Use the analytical orbit model with PSR mode for equilibration and output
- Verify that the analytical ephemeris adequately reproduces the SPICE geometry at the latitude of interest
Future versions may support extracting solar elevation from SPICE geometry kernels directly.
PSR mode is compatible with all three time-stepping solvers (explicit, implicit, Crank-Nicolson) and with adaptive timestepping. The Fourier-matrix solver is not supported for PSR because it requires the surface flux to be expressible as a function of the surface temperature alone, whereas the PSR flux depends nonlinearly on the solar elevation angle through the cavity scattering formula. When the Fourier-matrix solver is selected with PSR mode enabled, the model automatically falls back to the implicit solver.
Python API:
from heat1d import Model
from heat1d import planets
import numpy as np
model = Model(planet=planets.Moon, lat=np.deg2rad(85), ndays=1, psr_d_D=0.2)
model.run()
print(f"PSR floor Tmax: {model.T[:, 0].max():.1f} K")CLI:
heat1d --lat 85 --psr-d-D 0.2 --ndays 1Ingersoll, A. P., Svitek, T., & Murray, B. C. (1992). Stability of polar frosts in spherical bowl-shaped craters on the Moon, Mercury, and Mars. Icarus, 100, 40--47. doi:10.1016/0019-1035(92)90017-2