test_earthRadiationModel

Unit tests for earthRadiationModel.

Test geometry (all tests unless noted)

Earth at origin (planet-fixed = inertial, J20002Pfix = I) Sun at (+AU, 0, 0) → sunlit side: +x hemisphere Satellite at (0, REQ+600 [km], 0) → visible side: +y hemisphere Overlap (first quadrant lon 0-90°) → albedoFlux > 0, irFlux > 0

Physics expected values (approximate, for sanity-check only)

irFlux ~ OLR/(pi) * solid_angle_of_Earth ~ O(50) W/m^2 albedoFlux > 0 (sunlit + visible patches exist) Both direction vectors are unit vectors when flux > 0

test_earthRadiationModel.test_earthRadiation_albedoAvg_physicsChecks()[source]

albedoFlux and irFlux are positive; direction vectors are unit vectors.

test_earthRadiationModel.test_earthRadiation_albedoData_nonzero()[source]

With CERES albedo data, albedoFlux must remain positive.

test_earthRadiationModel.test_earthRadiation_albedoDir_geometry()[source]

In the standard geometry (Sun +x, satellite +y), albedoDir_N must point roughly toward the satellite (+y component > 0).

Illuminated patches are in the +x hemisphere; visible patches from the +y satellite are near the (lat=0°, lon~65°-90°) band. The flux-weighted direction from these patches to the satellite has a dominant +y component.

test_earthRadiationModel.test_earthRadiation_analytical_ir()[source]

Validate irFlux against the exact Lambertian-sphere closed form.

Derivation (integrating the patch kernel over the visible hemisphere):

F_IR = (OLR/pi) * integral_{visible} cos_sat * dA / d^2
     = 2 * OLR * (1 - sqrt(1 - (R_aut/D)^2))

This result is independent of the albedo model since OLR is spatially uniform in both avg and CERES-data configurations.

Note

The far-field approximation OLR*(R/D)^2 is only valid for D >> R. At 600 km LEO, R/D ~ 0.91 so the far-field error is ~30 %; the exact formula above is used here.

A fine grid (nLat=180, nLon=360, 1-degree resolution) is used so that the midpoint-rule discretisation error is < 0.3 %, well below the assertion tolerance of 1 %.

test_earthRadiationModel.test_earthRadiation_direction_toward_earth()[source]

The net force direction from ERP points roughly from satellite toward Earth (i.e. the radiation comes from Earth). With the satellite on the +y axis, irDir_N should have a dominant negative-y component.

test_earthRadiationModel.test_earthRadiation_eclipseCase_applied()[source]

P2 regression: eclipseCase=True must be applied in EarthRadiationModel.

Before the fix, isPatchEclipsed() in the base class always returned False regardless of the flag, so eclipseCase=True had no effect on ERM output. After the fix, the base-class implementation runs the Knocke penumbra model when the flag is set.

Properties verified:

  1. eclipseCase=True runs without error.

  2. Monotonicity: eclipse can only reduce albedo flux, never increase it — albedo_eclipse <= albedo_no_eclipse.

  3. IR is unaffected: eclipse shadows apply only to the albedo channel;

    the IR channel is independent of solar illumination and must not change.

  4. Small magnitude: for the standard geometry (satellite well outside

    the terminator zone), nearly all visible patches are clearly sunlit so the penumbra correction is negligible (< 1 % relative).

test_earthRadiationModel.test_earthRadiation_invalid_albedo_file(tmp_path)[source]

Providing a nonexistent albedo file must raise an error in Reset().

test_earthRadiationModel.test_earthRadiation_numpy_reference(useAlbedoData)[source]

Cross-check EarthRadiationModel against a pure-NumPy reimplementation of the same Lambertian patch algorithm.

What this test validates (and does not validate)

_python_lambertian mirrors planetRadiationBase.cpp and earthRadiationModel.cpp line-by-line in a different language. Because it replicates the same model, it cannot catch physics errors shared by both implementations. Its value is:

  • Arithmetic correctness — catches mistakes in the C++/Eigen indexing, DCM convention (Eigen::Map column-major transpose of row-major C array, see avsEigenSupport.cpp), normArea formula, or accumulation loop.

  • CERES CSV loading — verifies that the C++ CSV parser produces the same per-patch albedo values as np.loadtxt. The albedo grid is loaded from Basilisk supportData (DataFile.AlbedoData.Earth_ALB_2018_CERES_All_1x1) and fed identically into both sides.

Physics correctness (the model converges to the right integral) is covered by test_earthRadiation_analytical_ir.

Tolerance: 1e-7 relative, accounting for accumulated floating-point differences between Eigen and NumPy across the 180x360 patch loop.

test_earthRadiationModel.test_earthRadiation_solar_flux_distance_scaling()[source]

albedoFlux must scale as (AU / d_sun)^2; irFlux must be independent of d_sun.

Moving the Sun from 1 AU to 2 AU (same direction) halves the solar flux S_sun = S0*(AU/d)^2, so albedoFlux must drop by factor 4. The IR formula uses the constant OLR and does not contain S_sun, so irFlux must be unchanged.

test_earthRadiationModel.test_earthRadiation_unlinked_messages()[source]

Reset() must raise BSK_ERROR (BasiliskError) when messages are unlinked.

test_earthRadiationModel.test_earthRadiation_zero_albedo_direction_is_zero()[source]

When no patches are both sunlit and visible (albedoFlux = 0), albedoDir_N must be [0, 0, 0].

Geometry: Sun on +x, satellite on -x. Sunlit patches (outward normal toward +x) are on the far side from the satellite; visible patches (outward normal toward -x) face away from the Sun. The two sets are disjoint → albedoFlux = 0. IR flux must still be positive (emitted regardless of illumination).