Advanced Runtime Integration
Variable time-step behavior and integration constraints.
Audience: Integration engineers embedding TDSE into simulators with adaptive or multi-rate time-stepping (SPICE-family, PSCAD/EMTP, Keysight ADS, etc.).
Use this chapter when your simulator does not step at exactly the same interval
used to build the pack. It explains how runtime dt relates to build-time
model_dt, what accuracy changes when they differ, and which integration
patterns are usually safe.
This chapter starts with variable time-step integration, then continues into runtime concurrency, scaling, and multi-model deployment patterns in the sections that follow.
Use it only after the fixed-step step loop is already understood. For a first
minimal integration, keep dt_runtime = model_dt and return here only when the
host truly needs adaptive or multi-rate stepping.
The three most common host cases are:
| Host case | Recommended first policy |
|---|---|
| fixed-step simulator | keep dt_runtime = model_dt |
| adaptive simulator | start with clamped adaptive dt, then measure interpolation error |
| multi-rate orchestration | use multiple model handles, one time base per handle |
Core Semantics
The impulse-response tensor h[k] (shape [nh, nq, np]) is sampled at
build time with a fixed step size model_dt:
tau_k = k * model_dt, k = 0, 1, ..., nh - 1
model_dt is an intrinsic model property, queryable via tdse_model_info_t.dt.
The dt parameter passed to tdse_step_begin(model, t, dt) is the
simulator's current physical time span for this step. It does not
need to equal model_dt. The runtime convolution engine bridges the two
automatically.
Two Internal Paths
The runtime selects one of two paths based on the time-line history.
Uniform Fast Path
Triggered when all of these conditions are satisfied simultaneously:
- The committed history timeline is monotonically increasing and uniform,
with step size equal to
model_dt(relative tolerance 1e-9, absolute 1e-12). - The current step time
tminus the latest committed timet_newestequalsmodel_dt(same tolerance).
Under this path:
- History terms are read directly from the ring buffer by tap index, with no interpolation.
- This is the most accurate and fastest path, equivalent to fixed-step convolution.
Time-Driven Interpolation Fallback
Entered whenever any trigger condition fails (including but not limited to:
dt != model_dt, dt varying between steps, a large time-step jump, or
initial conditions where the accepted queue has not yet been uniformized).
For each delayed tap k >= 1:
sample_t = t - tau_k = t - k * model_dt
The runtime performs linear interpolation on the committed primary input
timeline to obtain v(sample_t), then weights and accumulates with h[k].
- Boundary semantics: when
sample_tprecedes the earliest retained committed time, that tap's contribution is treated as zero. This is zero extrapolation at the retained-history boundary, not evidence that the physical system was at rest before time zero. - Interpolation order: first-order (linear). There is no zero-order hold or higher-order interpolation path.
Accuracy Impact
For a sufficiently smooth accepted primary signal, linear interpolation has a second-order local approximation error. The resulting history error also depends on the tap values, retained history, and host coupling; it is not a universal bound:
err(hr) = O(dt_runtime^2 * sup_t |d^2 v / dt^2|)
where v is the committed primary input sequence. Practical guidance:
| Scenario | Starting sweep for dt_runtime / model_dt | Notes |
|---|---|---|
| Smooth input (AC steady-state, low-freq transient) | 0.5 -- 10 | Large steps only lose linear interpolation accuracy in history |
| General transient (step, pulse) | 0.5 -- 2 | Excessive ratio causes visible "smearing" near step edges |
| Steep transient (switching, surge, impulse) | 0.5 -- 1 | Recommend dt_runtime ~ model_dt |
Key numerical conclusions:
- The instantaneous path (
tdse_step_op, direct response intdse_step_commit) is driven byh[0]and does not interpolate across the runtime time grid. - The IR term (
tdse_step_ir) queries the pack's internal sampling table by absolute timet;dt_runtimeaffects only the time quantization. - Only the history term
hraccumulates linear interpolation error asdt_runtime / model_dtdeviates from 1.
Stability
Variable time-stepping does not rewrite the pack coefficients, but it changes the history approximation presented to the host solver. TDSE therefore makes no general stability or energy-conservation guarantee for an arbitrary variable-step host coupling. Extreme dt jumps can amplify interpolation error; validate the coupled simulation on representative transients and use the runtime metrics below as diagnostics rather than as a proof of stability.
Monitoring
When exploring aggressive dt strategies, poll runtime guard metrics via
tdse_ext_context_get_runtime_guard_metrics() on the model's extension context:
| Metric | Meaning | Watch For |
|---|---|---|
max_abs_g0 | max amplitude of h[0] entries this step | model-dependent; use as trend |
pivot_min | minimum pivot | significant drop means op near-singular |
pivot_ratio | current pivot_min vs baseline | sustained < 0.1 warrants investigation |
growth_factor | inter-step ` | hr |
Multi-Rate Models
TDSE multi-rate at the runtime level is achieved by running multiple model handles concurrently, not through internal multi-clock support:
tdse_model_t* fast_model; /* model_dt = 1 ns */
tdse_model_t* slow_model; /* model_dt = 10 ns */
tdse_model_create(fast_pack, fast_len, NULL, &diag, &fast_model);
tdse_model_create(slow_pack, slow_len, NULL, &diag, &slow_model);
for (step = 0; step < N; ++step) {
/* fast model: step every 1 ns */
tdse_step_begin(fast_model, t_fast, 1e-9);
/* ... */ tdse_step_commit(fast_model, primary_fast, fast_np);
/* slow model: step every 10 ns */
if (step % 10 == 0) {
tdse_step_begin(slow_model, t_slow, 10e-9);
/* ... */ tdse_step_commit(slow_model, primary_slow, slow_np);
}
}
Each handle maintains its own history ring buffer independently. Concurrency rules still apply: no concurrent API calls on the same handle; multiple independent handles may run in parallel on different threads.
Recommended Integration Patterns
Use these in order. Do not jump to adaptive or multi-rate operation before the fixed-step path is already validated.
Fixed dt (simple SPICE integration)
Always use dt_runtime = model_dt in the simulator main loop:
const double model_dt = info.dt;
for (...) {
tdse_step_begin(model, t, model_dt);
/* op / hr / ir */
tdse_step_commit(model, primary, np);
t += model_dt;
}
Once the committed timeline is aligned and uniform, this uses the uniform fast path. The host must still provide coherent time coordinates and establish the required committed history.
Adaptive dt (LTE-controlled SPICE)
Let dt_runtime follow the solver's local truncation error control:
const double model_dt = info.dt;
for (...) {
double dt = solver_proposed_dt();
/* Clamp to [0.1 * model_dt, 10 * model_dt] for typical EDA scenarios */
if (dt < 0.1 * model_dt) dt = 0.1 * model_dt;
if (dt > 10.0 * model_dt) dt = 10.0 * model_dt;
tdse_step_begin(model, t, dt);
/* ... */
tdse_step_commit(model, primary, np);
t += dt;
}
Host/TDSE split here:
- the host proposes and clamps
dt - TDSE interprets the accepted
(t, dt)againstmodel_dt - interpolation error belongs to the integration policy, not to Runtime
Trace Replay
When primary comes from an external trace file with trace_dt != model_dt:
- Option A: drive the runtime with
trace_dtdirectly (time-driven interpolation). - Option B: resample the trace to
model_dtexternally, then use the fast path.
They are not bitwise or numerically identical in general: Option A uses Runtime's interpolation at fixed model delays, while Option B adopts the external resampler's interpolation/filtering policy. Option B can enable the uniform fast path when the host also supplies an aligned, uniform committed timeline. TDSE privately selects the implementation for the model's configured device; applications do not select an internal backend for this path.
Decision Table
| If your main goal is... | Start with... | Escalate to... |
|---|---|---|
| fastest bring-up | fixed dt_runtime = model_dt | nothing until basic loop is stable |
| adaptive solver fidelity | clamped adaptive dt | error measurement and tuning |
| multi-rate orchestration | one handle per rate | explicit scheduling and cross-model review |
Related API
tdse_step_begin(model, t, dt)intdse/tdse.h: core entry point discussed heretdse_model_info_t.dtintdse/tdse.h: querymodel_dttdse_ext_context_get_runtime_guard_metrics()intdse_ext.h: runtime stability monitoringtdse_builder_compute_consistent_grid()intdse_builder.h: derive consistentmodel_dt,nh,nfft, anddwat build time
