Python
The quickest way to get a model into a notebook, a test rig or a data pipeline.
<name>_deployableCore.py<name>_testbench.py
execute_blocks()
python3 with numpy
float / numpy
307 of 308
Module-level state and a module-level execute_blocks(): import it, set
the inputs, call it once per sample. It needs numpy, which is what makes the matrix
signals behave the way they do in the simulator.
Python is one of the two targets that can carry a Python block — your own source, running in the loop — which is why it supports 307 of the 308 library blocks.
It is also the fastest target to sanity-check by hand: the testbench prints the same samples the app plots, so a disagreement is visible in a diff rather than in a debugger.
What the export looks like
Every target page shows the same model, so the ten are directly comparable:
an input, an error junction, a gain of 1.8, a discrete plant
0.4z⁻¹ / (1 − 0.6z⁻¹), an output — and the plant's
output fed back into the junction. Five blocks, in a diagram the
export-verification suite calls DPT_Feedback_Discrete — which is where the names
in the file come from. Below is what Deploy writes for this target, with only the
file's header banner removed.
DPT_Feedback_Discrete_deployableCore.py
# ========================================================= # USER IMPORTS # ========================================================= import numpy as np from typing import List # --- Module external input interface ----------------------------- # Top-level input gates read their values from here. A consumer (e.g. the # testbench) sets inputs each step via set_inputs(); empty -> zeros. _INPUTS = {} def set_inputs(values): global _INPUTS _INPUTS = values def get_input(name, m, p): if not _INPUTS: return np.zeros((m, p)) arr = np.zeros((m, p)) for r in range(m): for c in range(p): key = name if (m == 1 and p == 1) else f"{name}[{r},{c}]" arr[r][c] = _INPUTS.get(key, 0.0) return arr # ========================================================= # GENERATED SIGNAL STORAGE # ========================================================= class Signals: def __init__(self): # sig0: ICore Blocks/Home/DPT_Feedback_Discrete/In1/ICoreDouble-Out-0 self.sig0 = np.zeros((1, 1)) # sig1: ICore Blocks/Home/DPT_Feedback_Discrete/Error/ICoreDouble-Out-0 self.sig1 = np.zeros((1, 1)) # sig2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain/ICoreDouble-Out-0 self.sig2 = np.zeros((1, 1)) # sig3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant/ICoreDouble-Out-0 self.sig3 = np.zeros((1, 1)) # sig4 (boundary output): ICore Blocks/Home/DPT_Feedback_Discrete/ICoreDouble-Out-0 self.sig4 = np.zeros((1, 1)) signals = Signals() # ========================================================= # BLOCK CONFIG PARAMETERS # ========================================================= class BlockParams: def __init__(self): # blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain self.blk2_gain = np.array([[1.8]], dtype=float) params = BlockParams() # ========================================================= # GENERATED EXECUTION GRAPH # ========================================================= # ... ICoreIIREmulator — the shared IIR helper emitted with every Python # ... export — elided here; this model's plant uses the inlined form in Blk3 ... class Blk0: def solve(self): signals.sig0 = get_input("sig0", 1, 1) blk0 = Blk0() class Blk1: def solve(self): output = np.zeros((1, 1)) output = output + signals.sig0 output = output - signals.sig3 signals.sig1 = output blk1 = Blk1() class Blk2: def solve(self): output = signals.sig1 * params.blk2_gain[0][0] signals.sig2 = output blk2 = Blk2() class Blk3: def __init__(self): # Discrete transfer-function IIR coefficients (normalized, a0 dropped) self._num = [0, 0.40000000000000002] self._den = [-0.59999999999999998] # Per-entry IIR history: the SISO transfer function is applied to each [p,m] entry. self._u_hist = np.zeros((1, 1, 2)) self._y_hist = np.zeros((1, 1, 1)) def solve(self): u = np.asarray(signals.sig2, dtype=float).reshape(1, 1) out = np.zeros((1, 1)) for r in range(1): for c in range(1): uk = float(u[r, c]) for k in range(1, 0, -1): self._u_hist[r, c, k] = self._u_hist[r, c, k - 1] self._u_hist[r, c, 0] = uk yk = 0.0 for i in range(2): yk += self._num[i] * self._u_hist[r, c, i] for i in range(1): yk -= self._den[i] * self._y_hist[r, c, i] self._y_hist[r, c, 0] = yk out[r, c] = yk signals.sig3 = out blk3 = Blk3() class Blk4: def solve(self): signals.sig4 = signals.sig3 blk4 = Blk4() def execute_blocks(): # Execution order generated automatically from block diagram # blk0: ICore Blocks/Home/DPT_Feedback_Discrete/In1 blk0.solve() # blk1: ICore Blocks/Home/DPT_Feedback_Discrete/Error blk1.solve() # blk2: ICore Blocks/Home/DPT_Feedback_Discrete/Ctrl_Gain blk2.solve() # blk3: ICore Blocks/Home/DPT_Feedback_Discrete/Plant blk3.solve() # blk4: ICore Blocks/Home/DPT_Feedback_Discrete/Out1 blk4.solve()
Module-level signals and params, one instance per block, and a
module-level execute_blocks(). Because the matrices are numpy arrays, the sum and
the gain read as whole-array expressions rather than loops — the same reason the matrix signals
behave here exactly as they do in the simulator.
How it is checked
Every one of the ten targets is verifiable, and this one is no exception: the export is compiled with the toolchain above, run across the simulation window, and compared against the solver sample by sample. Software targets pass at around 1e-11 % against a 0.1 % tolerance; the HDL targets are bounded by their fixed-point quantum instead. See verification.
See also: Code export · Multi-target, multi-rate deploy
See it run on your own model.
Download the application from the customer portal, or read the documentation first — the manual, every block with its measured response, and the full command reference are public.