Generated reference › Python Code — Control Systems/User Defined
kind: generated#block#control-systems-user-defined

Python Code — Control Systems/User Defined

.py

Control_Systems/User_Defined/Python_Code · 1 input / 1 output port(s) at insert · exports to Python

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Python Code

Control Systems / User Defined

Runs Python you write, once per simulation step, in the embedded interpreter. Use it for anything the library has no block for – a nonlinearity, a lookup, a hand-written controller.

The contract

Your code defines one function:

def compute(t, u, state):

  • t – the current simulation time, in seconds.
  • u – a list of numpy matrices, one per input port.
  • state – a dict that persists between steps; use it for anything the block must remember.
  • returns – a list of numpy matrices, one per output port.

Ports

  • Inputs and Outputs – one of each by default, and both counts are user-editable. The list u and the returned list follow the port order on the canvas.

Parameters

  • Python Code – edited in the code editor, opened from the Edit Code button on the config dialog's toolbar rather than in the parameter pane.
  • Sampling Time (s) – how often compute is called. Zero or less inherits the solver's rate.

Code export

Python only – your code is embedded, indented, into the generated module. The other nine targets (MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text) are not supported: none can carry a Python interpreter, so an export to any of them fails loudly for this block rather than emitting something that does not run. Replace the block with library blocks, or with a state space, when the model has to reach those targets.

Simulink bridge

Both directions, to simulink/User-Defined Functions/Python Code. The ports cross as SymbolSpec symbols (u1…, y1…, in port order) so the wiring survives, and the code crosses into the block's Output Code as text – Python CodeOutputCode. The two contracts differ: ICore calls your compute once per step, while the Python Code block runs its output code directly over the symbols, so the code needs adapting after crossing before the model runs on the other side. Sampling Time (s) → SampleTime, as on every block.

Notes

  • Discrete by nature: the block is stepped, never integrated, since Python state updates are per-sample.
  • A runtime error in your code is reported once, with the block's path, and stops the run.

Code facts#

FactValue
registered typeControl_Systems/User_Defined/Python_Code
familyControl_Systems/User_Defined
solver environment classICoreBlock_0_Control_Systems_1_User_Defined_2_Python_Code
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/User_Defined/Python_Code/ICoreBlock_0_Control_Systems_1_User_Defined_2_Python_Code.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/User_Defined/Python_Code/ICoreBlock_0_Control_Systems_1_User_Defined_2_Python_Code.h
default size on canvas100 × 80 px
ports at insert1 in, 1 out
code generators implementedPython

Ports#

#DirectionSignal typeDescription label
1inICoreDouble
2outICoreDouble

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
CONFIG_PYTHON_CODE (unresolved)DEFAULT_CODE_TEMPLATE

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::Both
Simulink pathsimulink/User-Defined Functions/Python Code
port-count rulePortsParam::
SampleTime parameteryes
portsderived from a SymbolSpec (addSymbol), not a port-count parameter
ICore configSimulink parameterValue translation
Python CodeOutputCodepasses through

Caveat (shown to the user): the code crosses into the Python Code block's OutputCode as text, but the contracts differ: ICore calls compute(t, u, state) while Simulink runs the output code directly over its SymbolSpec symbols (u1.., y1..), so the code needs adapting after crossing

Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

⚠ Mismatch. Fixed at the source — the description or the code, whichever is wrong (R-D9) — never explained away on a docs page:

  • B7 no ICoreSimulinkBlockCatalog::registerEntry — the bridge treats an unregistered type as unsupported, so the block owes an entry

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Python Code block — user-editable compute(t, u, state) over numpy matrices The user's source lives in a private config variable (edited via the config dialog's "Edit Code" toolbar button, persisted base64 by the recipe serializer). Contract:

def compute(t, u, state): # t: time [s], u: list of numpy matrices (input ... # ports, in order), state: dict persisting between return [y0, ...] # steps -> one numpy matrix per output port

Simulation: discrete-only block; compute_h_discrete runs the code in the embedded interpreter (ICorePythonRuntime). Output port sizes are inferred at build time by a throw-away trial call on zero inputs. Code export: Python only — every other language keeps the base class's empty default, which the exporters report as unsupported.

Sample results#

Python Code — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per samplePython Code — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-202-2-10123inputoutput
tin ICoreDouble-Out-0out ICoreDouble-Out-0
0-2-2
0.40.50.5
0.8-2-2
1.20.50.5
1.6-2-2
20.50.5
2.4-2-2
2.80.50.5
3.2-2-2
3.60.50.5
4-2-2
4.40.50.5
4.8-2-2
5.20.50.5

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 1
rampRamp: slope 1 from t = 00 … 5.8
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-1 … 0.9996
stepStep: 0 -> 1 at t = 1 s0 … 1

Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample

Category static · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Control_Systems__User_Defined__Python_Code.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).