Components and Reuse ​
A component groups parameters, variables, connectors, and equations into a reusable model. A small component might express one constitutive law; a subsystem might contain dozens of connected parts. Both use the same component declaration.
Define a complete behavior ​
This model relaxes toward target with time constant tau:
component Relaxation
parameter tau::Real = 1
parameter target::Real = 0
variable x::Real
relations
initial x = 1
der(x) = (target - x) / tau
endThe parameters are settings for each instance. The variable is an unknown determined by the differential equation and its initial condition. A simulation of this model requires tau > 0 for the intended relaxation behavior.
Compose a subsystem ​
Build a subsystem by declaring component instances. Each instance has its own variables and parameter values:
component TwoRates
parameter time_scale::Real = 1
fast = Relaxation(tau=0.5 * time_scale)
slow = Relaxation(tau=2 * time_scale)
endThe states are fast.x and slow.x. A caller configures time_scale, and TwoRates passes that parameter to both parts: with the default scale, their time constants are 0.5 and 2. Each instance retains the same equations with its own parameter values.
The viewer below contains two copies of TwoRates. The comparison copy doubles both time constants through one parameter. Double-click baseline or comparison to see its fast and slow parts, then use the breadcrumb to return. Each copy has its own states, such as baseline.fast.x and comparison.fast.x.
A physical assembly usually adds a relations block with connect(...) calls between its parts. See Connectors and Networks for a complete electrical example. Keep a subsystem's external interface small: expose the ports and parameters that a caller needs, then connect or pass those settings to the internal parts.
Expose settings intentionally ​
Use a public parameter to give a contained component's setting a name meaningful to callers. Here, response_time configures the internal tau:
component AdjustableRelaxation
parameter response_time::Real = 2
core = Relaxation(tau=response_time)
endUse ordinary parameters for numerical settings and structural parameters for choices that change a component's shape. A final parameter expresses a setting that derived definitions and instances must preserve. See Parameters, Variables, and Equations for these declarations.
Arrays of components ​
A comprehension constructs repeated instances. A structural parameter controls how many instances exist:
component RelaxationBank
structural parameter N::Integer = 3
cells = [Relaxation(tau=i) for i in 1:N]
endcells[1].x names the first state. A relation loop can connect neighboring elements or relate their variables; see Arrays and repeated equations. Choose N during model construction and construct the model again when changing it.
Inheritance ​
Choose composition when the existing component is a part of your model, and inheritance when the new component specializes its definition.
| Modeling choice | Use it when | Resulting name |
|---|---|---|
part = Base() | The base is a contained part of an assembly | part.x |
extends Base | The new definition specializes the base interface and behavior | x |
Inheritance specializes a common definition. extends brings the base component's declarations and relations into the derived component's namespace:
component SlowRelaxation
extends Relaxation(tau=5)
endThe inherited state is directly available as x. The inherited initialization and differential equation also remain active. Extending a complete component works well for a named preset; adding an incompatible equation for an inherited state would overconstrain it.
Partial components ​
A partial component defines declarations and equations shared by a family of components. Derived components supply the remaining behavior.
For an electrical family, the shared definition can contain two ports and a current convention. The example below uses the potential and flow fields introduced in Connectors and Networks. TwoPin defines the interface and current balance; LinearResistor adds Ohm's law.
partial component TwoPin
p = Pin() {^p}
n = Pin() {^n}
variable v::Real
variable i::Real
relations
v = p.v - n.v
i = p.i
p.i + n.i = 0
metadata {
"_links": {
"p": {
"Dyad": {
"placement": {"icon": {"iconName": "pos", "x1": -50, "y1": 450, "x2": 50, "y2": 550}}
}
},
"n": {
"Dyad": {
"placement": {"icon": {"iconName": "neg", "x1": 950, "y1": 450, "x2": 1050, "y2": 550}}
}
}
}
}
end
component LinearResistor
extends TwoPin
parameter R::Real = 1
relations
v = R * i
endThe built-in Pin supplies the pos and neg electrical icons. The port metadata places them on opposite edges using the placement from ElectricalComponents' TwoPin interface. Derived components inherit these placements.
TwoPin deliberately leaves the relation between voltage and current open. A resistor closes it with v = R * i; another derived component can supply a storage law. Instantiate the completed component in a network that supplies its electrical boundary conditions.
This pattern gives library parts consistent port names, sign conventions, and shared definitions. See the RLC Circuit tutorial for an assembled circuit.
Make a component easy to use ​
Before sharing a component, check that its users can:
Configure it through documented public parameters with sensible defaults.
Connect its ports using documented physical meanings and sign conventions.
Understand its initialization requirements.
Run a complete example with the boundary conditions it needs.
For a component library, continue with Julia-Based Component Libraries. Add icons and port placement using Graphical Models and Metadata.