Time-Domain System Equivalent logoTime-Domain System EquivalentLinear dynamics, solved faster.Discuss Integration

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 caseRecommended first policy
fixed-step simulatorkeep dt_runtime = model_dt
adaptive simulatorstart with clamped adaptive dt, then measure interpolation error
multi-rate orchestrationuse 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:

  1. The committed history timeline is monotonically increasing and uniform, with step size equal to model_dt (relative tolerance 1e-9, absolute 1e-12).
  2. The current step time t minus the latest committed time t_newest equals model_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_t precedes the earliest committed time, that tap's contribution is treated as zero (equivalent to assuming the model was at rest before time zero).
  • Interpolation order: first-order (linear). There is no zero-order hold or higher-order interpolation path.

Accuracy Impact

The dominant error in the history term hr[n] under the time-driven path is governed by linear interpolation:

err(hr) = O(dt_runtime^2 * sup_t |d^2 v / dt^2|)

where v is the committed primary input sequence. Practical guidance:

ScenarioRecommended dt_runtime / model_dtNotes
Smooth input (AC steady-state, low-freq transient)0.5 -- 10Large steps only lose linear interpolation accuracy in history
General transient (step, pulse)0.5 -- 2Excessive ratio causes visible "smearing" near step edges
Steep transient (switching, surge, impulse)0.5 -- 1Recommend dt_runtime ~ model_dt

Key numerical conclusions:

  • The instantaneous path (tdse_step_op, direct response in tdse_step_commit) is driven by h[0] and is exact for any runtime step size.
  • The IR term (tdse_step_ir) queries the pack's internal sampling table by absolute time t; dt_runtime affects only the time quantization.
  • Only the history term hr accumulates linear interpolation error as dt_runtime / model_dt deviates from 1.

Stability

Variable time-stepping does not change the model's stability properties:

  • Model stability is determined by the spectral radius of h[k] in the pack, which is fixed at build time.
  • Variable stepping only affects the computational approximation of the history term; it cannot make a stable model unstable.
  • Extreme dt jumps (e.g., from model_dt to 100 * model_dt and back) may amplify interpolation error in transients but will not inject spurious energy.

Monitoring

When exploring aggressive dt strategies, poll runtime guard metrics via tdse_ext_get_runtime_guard_metrics():

MetricMeaningWatch For
max_abs_g0max amplitude of h[0] entries this stepmodel-dependent; use as trend
pivot_minminimum pivotsignificant drop means op near-singular
pivot_ratiocurrent pivot_min vs baselinesustained < 0.1 warrants investigation
growth_factorinter-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, &diag, &fast_model);
tdse_model_create(slow_pack, slow_len, &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);

    /* 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);
    }
}

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.

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);
    t += model_dt;
}

This always hits the uniform fast path, maximizing accuracy and performance.

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);
    t += dt;
}

Host/TDSE split here:

  • the host proposes and clamps dt
  • TDSE interprets the accepted (t, dt) against model_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_dt directly (time-driven interpolation).
  • Option B: resample the trace to model_dt externally, then use the fast path.

Both are mathematically equivalent. Option B enables more optimized convolution backends (CPU_BLAS / GPU_PACK_BLAS paths perform best on the uniform fast path).

Decision Table

If your main goal is...Start with...Escalate to...
fastest bring-upfixed dt_runtime = model_dtnothing until basic loop is stable
adaptive solver fidelityclamped adaptive dterror measurement and tuning
multi-rate orchestrationone handle per rateexplicit scheduling and cross-model review

Related API

  • tdse_step_begin(model, t, dt) in tdse/tdse.h: core entry point discussed here
  • tdse_model_info_t.dt in tdse/tdse.h: query model_dt
  • tdse_ext_get_runtime_guard_metrics() in tdse_ext.h: runtime stability monitoring
  • tdse_builder_compute_consistent_grid() in tdse_builder.h: derive consistent model_dt, nh, nfft, and dw at build time