Skip to content
Get started

Composition

Language overview

A component is a reusable set of declarations and equations. An instance creates an occurrence with explicit named bindings:

composition.eqi
component Offset(parameter input: 1, parameter offset: 1 = 1) {
variable output: 1;
relation value { output - input - offset = 0; }
}
model TwoOffsets() {
instance first: Offset(input = 2);
instance second: Offset(input = 5, offset = 3);
}

input is required; offset defaults to 1. The first output is 3 and the second is 8. Each instance has its own output unknown and equation. Equal parameter values do not merge instances. Missing required parameters and unknown binding names are errors.

Voltage divider using a bundled package
import Eqiora.Electrical.Basic.basic as electrical;
/// Ideal 12 V divider with an explicit zero-voltage reference.
/// Ohm and Kirchhoff give 4 mA through both resistors and an 8 V midpoint.
model VoltageDivider() {
instance source: electrical.IdealVoltageSource(voltage = 12[V]);
instance upper: electrical.Resistor(resistance = 1[kOhm]);
instance lower: electrical.Resistor(resistance = 2[kOhm]);
instance ground: electrical.Ground();
connect source.positive, upper.positive;
connect upper.negative, lower.positive;
connect lower.negative, source.negative, ground.terminal;
observable current: A = upper.positive.current;
observable midpoint: V = lower.positive.voltage;
observable upper_power: W = (upper.positive.voltage - upper.negative.voltage) * upper.positive.current;
observable lower_power: W = (lower.positive.voltage - lower.negative.voltage) * lower.positive.current;
observable source_power: W = (source.positive.voltage - source.negative.voltage) * source.positive.current;
}

import Eqiora.Electrical.Basic.basic as electrical makes the module’s public declarations available through the local name electrical. The project records the package dependency separately; see adding a package.

connect a, b joins compatible conserving ports. Across values such as voltage agree, and signed through values such as current into each component sum to zero. Thus the divider shares one current around its series loop while the two resistors have different voltage drops.

Read members using the complete path, for example upper.positive.current. Two ports are compatible when their connector declarations match; matching member names or vector sizes are not enough. The electrical walkthrough compiles and runs this complete circuit.

Use connect output -> input for a directed signal connection. For example, the property-composition project sends a sampled temperature to two consumers:

eqi
connect temperature -> local_flux.temperature;
connect temperature -> slab.temperature;

The signal types and clock identities must agree. This wiring carries a value; it does not apply the flow-conservation rule of a physical connector.

Binding spatial fields and complete boundaries

Section titled “Binding spatial fields and complete boundaries”

A component signature can require a support and an existing field:

eqi
component UniformTemperature(
support body: volume(ambient_dimension = 2),
variable temperature: K on body,
parameter value: K
) {
relation uniform on body { temperature = value; }
}
model Square() {
domain body = box(0, 1, 0, 1);
variable temperature: K on body;
instance prescribed: UniformTemperature(
body = body, temperature = temperature, value = 300[K]
);
}

The component constrains the caller’s temperature field; it does not create a second copy. The same technique lets a volume balance and boundary interface share one displacement or velocity field.

For a component requiring complete_exterior(parent = body), bind exterior = boundaries(left, right, bottom, top) using every exterior member. A family member is addressed as surface.mechanical[boundary = left]. See complete solid and fluid models.

A property contract specifies the inputs, output dimension, and derivative requirements of a constitutive value. A property release supplies that value using an analytic expression or a table, together with its validity and attribution.

A temperature-dependent conductivity
/// Callable conductivity with derivatives only inside smooth open table segments.
public property contract Conductivity(input temperature: K): W / (m * K) {
derivatives first_open_intervals;
}
/// Synthetic instructional samples; exact data and attribution travel with the package.
public property release Samples: Conductivity {
table {
data conductivity_samples;
axis temperature: K;
value conductivity: W / (m * K);
interpolation piecewise_affine;
preprocessing identity;
missing reject;
knot_derivative reject;
endpoint_derivative reject;
}
validity temperature in [300[K], 360[K]];
outside reject;
branch single;
citation synthetic_definition;
license repository_license;
}

The property above accepts temperature in kelvin and returns conductivity in W/(m·K). Its table uses piecewise affine interpolation over 300–360 K and rejects values outside that interval. Derivatives are defined inside open segments; knots and endpoints are excluded from derivative evaluation.

A component requests the contract as property conductivity: Conductivity, then calls it with conductivity(temperature = temperature). The caller binds the release with conductivity = Samples:

A component consuming the property
/// Evaluate signed one-dimensional Fourier flux at each supplied sample.
public component FourierFlux(
clock tick: periodic,
property conductivity: Conductivity,
input temperature: K at tick,
parameter gradient: K / m,
output flux: W / m^2 at tick
) {
relation constitutive at tick {
flux = -conductivity(temperature = temperature) * gradient;
}
}

The complete project contains the table data, attribution, package manifest, and two consumers of the same property. Compile the example from the full project, because the property definition needs those files.

For the physical reasoning, read constitutive laws. To publish your own components, follow Create and share a library. For ready-made components, browse the standard library.