ArtemisThermalBase 0.5.0
Source

06 · Python API

Python API

Running simulations and reading results from your own scripts.

Everything the command line and the app do is available from Python. The examples on this page are executed by the test suite, so they work as written.

Run a simulation

from dataclasses import replace
from datetime import UTC, datetime

from artemis_thermal.core_engine.constants import load_config
from artemis_thermal.simulation.runner import SimulationRunner

config = load_config()                       # or load_config("shackleton_preview")
config = replace(config, synthetic_dem=replace(config.synthetic_dem,
                 radius_m=2000.0, depth_m=500.0, grid_resolution_m=200.0))

results = SimulationRunner(config).run(
    start_time=datetime(2025, 1, 1, tzinfo=UTC),
    duration_hours=6.0,
    dt_s=900.0,
    output_dir="output/api_example",
)
print(results.metadata["num_faces"], float(results.max_surface_T.max()))

load_config takes a path or the name of a bundled configuration and returns a frozen dataclass tree; change it with dataclasses.replace. SimulationRunner.run accepts:

argument default meaning
start_time required UTC start of the recorded period
duration_hours 24.0 length of the recorded period
dt_s config time step
output_interval_s config spacing of stored maps
point_source_mode config True for one ray per facet
ephemeris_mode config "synthetic", "skyfield" or "spice"
latitude_deg, longitude_deg config site, for synthetic terrain
spinup_cycles config lunar days before start_time
save_data True write the run folder
output_dir config where
external_dem None a DEMData, e.g. from LOLALoader().load_dem(path)
progress None called with a RunProgress after every step
should_stop None polled every step; return True to cancel

It returns SimulationResults: per-facet arrays final_surface_T, min_surface_T, max_surface_T, mean_surface_T, final_illumination, mean_illumination, sunlit_fraction; geometry face_centroids, face_normals, face_areas, dem_elevation, dem_x, dem_y; the series times, sun_elevations, sun_azimuths, probe_temps; and metadata.

Check the cost first

from artemis_thermal.core_engine.constants import load_config
from artemis_thermal.data_ingestion.synthetic_dem import synthetic_grid_shape
from artemis_thermal.simulation.runner import estimate_run_memory_mb, faces_for_dem_shape

config = load_config()
faces = faces_for_dem_shape(*synthetic_grid_shape(config.synthetic_dem))
estimate = estimate_run_memory_mb(faces, config)
print(faces, round(estimate["total"]), "MB")

Read a run folder

from artemis_thermal.simulation.io_manager import ResultsBundle

run = ResultsBundle.open("output/api_example")
grid = run.raster("surface_T_max")           # north-up (rows, cols) float32
print(run.grid_shape, run.extent, run.snapshot_count)

cell = run.face_at(0.0, 0.0)                  # facet index under a point [m]
first_frame = run.raster("snapshots_surface_T", frame=0)
hours = run.timeline["hours"]

ResultsBundle.open raises ResultsError with a readable message if the folder is missing, incomplete or from an older version. Arrays are opened memory-mapped and only read when used.

Figures

from artemis_thermal.simulation.io_manager import ResultsBundle
from artemis_thermal.visualization.figures import render_run_figures

paths = render_run_figures(ResultsBundle.open("output/api_example"), "output/api_example",
                           dpi=120, hero_dpi=120)
print([p.name for p in paths])

The colour maps and palette live in artemis_thermal.style; artemis_thermal.visualization.figures.colormap("temperature") returns the Matplotlib map used everywhere.

Lower-level pieces

module what you get
artemis_thermal.core_engine.frames body_to_enu, projected_frame, frame_for_dem, curvature_drop
artemis_thermal.core_engine.mesh dem_to_mesh(dem, curvature_radius_m) → TriangleMesh
artemis_thermal.core_engine.illumination IlluminationEngine(mesh, ...).compute(sun_dir) → visible fraction and projected factor
artemis_thermal.core_engine.raytracer build_bvh, the shadow kernels, moller_trumbore
artemis_thermal.data_ingestion.ephemeris SyntheticSun, SolarEphemeris (Skyfield), iau_moon_rotation
artemis_thermal.data_ingestion.spice_ephemeris SpiceSolarEphemeris
artemis_thermal.data_ingestion.lola_loader LOLALoader().load_dem(path, max_size=...)
artemis_thermal.thermal_solver.crank_nicolson CrankNicolsonSolver.step_batch on a dense (faces, nodes) array
artemis_thermal.thermal_solver.volatiles sublimation_rate, ice_retention_timescale, compute_cold_trap_map

A Sun source is anything with sun_state(utc_time, solar_constant) -> SunState, where SunState carries a body-frame unit vector, the distance and the irradiance; pass your own to experiment with other ephemerides.