Memory planning#
The mesh is only part of a time-domain simulation’s memory requirement. Analysis setup constructs material operators and estimates a stable time step before allocating the fields. Temporary setup arrays can exceed the memory held during time integration.
AnalysisTD.estimate_memory() and AnalysisScatteringTD.estimate_memory() report
allocation budgets without building material matrices, solving ports,
computing a CFL eigenvalue, attaching monitors or creating project files:
estimate = analysis.estimate_memory(excited=["input"])
print(estimate)
excited= applies to scattering analyses. General TD analyses represent
one run and omit it. The returned MemoryEstimate exposes byte counts
through mesh_bytes, fields_bytes, coefficients_bytes, phases and
monitors. Printed units are binary (GiB = 2³⁰ bytes,
MiB = 2²⁰ bytes, KiB = 2¹⁰ bytes), matching tools such as free -h.
Phase budgets#
Each phase states simultaneous RAM and, for a GPU scenario, VRAM storage. RAM numbers include the mesh already in memory. Do not sum the phase budgets or add the mesh again. Sequential scattering runs reuse solver storage; raw monitor output on disk is multiplied by the number of excited channels. Results retained by multiple in-memory runs can increase RAM and leave the upper budget unresolved.
Field storage follows the staggered Yee shapes and requested precision,
including the MAGNELIO_PRECISION default. CFL and material operator
calculations remain in double precision. The CFL budget includes the
current explicit sparse curl construction, its Python index lists,
conversion temporaries, the absolute-value matrix copy and Lanczos vectors.
A cached CFL eigenvalue removes the corresponding construction budget.
Ranges model allocation paths in the current implementation, not measured RSS or guaranteed process-memory limits. Conservative workspaces cover material/operator construction, field updates and CPML. CAD/native objects, Python metadata, allocator pools and checkpoint/I/O workspaces are excluded. Mesh arrays and views sharing a backing allocation are counted once.
Port/element mode construction, sparse factorisation and convolution
histories, source state, dispersive/SIBC auxiliary states and monitor
types other than rectangular time/frequency field monitors currently
have no allocation model. When present, they are listed explicitly and
the upper budget is None; known phase budgets still identify costly
setup stages. peak_ram_bytes and peak_vram_bytes are consequently
optional; minimum_peak_ram_bytes exposes the largest modelled lower
endpoint. A missing upper endpoint must not be treated as zero.
An unresolved backend="auto" uses a CPU scenario without probing CUDA.
Use estimate(backend="cupy") to budget the GPU scenario independently.
This does not change the backend used by run().
Time monitor recordings#
Every rectangular time monitor reports bytes_per_snapshot for the
requested raw staggered components. A slice, line or plane therefore
uses its own component shapes, including boundary samples.
Schedule |
Recording budget per run |
|---|---|
|
At most the requested number of snapshots |
|
Snapshot count through that bound |
|
At most one snapshot per step |
|
Unknown total, plus bytes per simulated second |
With a known time step the estimator also coalesces explicit times that would be consumed in the same solver step. Early termination can reduce the recording below every stated count. For interval recording the growth rate uses the requested schedule; when the time step is known, it is capped at one snapshot per step.
Supply the same explicit limits intended for the run:
estimate = analysis.estimate_memory(total_time_steps=200_000)
estimate = analysis.estimate_memory(max_time_steps=500_000)
The automatic runtime cap depends on prepared ports and waveforms. The estimator leaves it unresolved instead of performing that setup. A physical duration requires the step to account for rounding and the monitor’s half-step acceptance window:
estimate = analysis.estimate_memory(t_end=20e-9, dt=1e-12)
dt= is an assumed scenario, not a solver override. If omitted, a cached
CFL eigenvalue supplies the normal-accuracy step; otherwise no new
eigenvalue is computed. Without a known step, a duration alone cannot
certify the number of rounded recordings. t_end is a run option on
AnalysisTD; on a scattering estimate it is only a planning scenario.
Without project=, time snapshots accumulate in RAM, including a possible
second copy when frames are stacked. With project=, the raw total
belongs to disk storage; RAM holds the pending batch and its stacking
copy, at most 100 snapshots between the current solver’s flushes.
monitor_disk_bytes is the raw field-monitor payload across all runs,
excluding geometry, mesh files, port signals, checkpoints and HDF5 overhead.
Frequency monitors hold one complex128 array per recorded component and frequency, independently of run length and field precision. They continue to need those accumulators in RAM on a project-backed run.
The returned MemoryEstimate.n_runs counts sequential runs; collections of
actual runs remain runs. The method is estimate_memory() on both
AnalysisTD and AnalysisScatteringTD. Its dt, backend and t_end
assumptions describe an estimate scenario and do not configure a later run.