ICore Blocks / Targets / Software

Python

The quickest way to get a model into a notebook, a test rig or a data pipeline.

Deploy writes
  • <name>_deployableCore.py
  • <name>_testbench.py
One call, one sample

execute_blocks()

Verified with

python3 with numpy

Numbers

float / numpy

Library blocks

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

Get started

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.