Logical.Tests.Logical
Interconnected test of Logical blocks.
Based on the structure of ModelicaTest.Blocks.Logical from the Modelica Standard Library.
Tests: And, Or, Xor, Nand, Nor, Not, GreaterThreshold, GreaterEqualThreshold, LessThreshold, LessEqualThreshold, Greater, GreaterEqual, Less, LessEqual, LogicalSwitch, Switch.
The stop time is 9.5 s so that every output is evaluated at a deterministic operating point. At t=10 both sines sit exactly on a zero crossing (2*sin(10π)=0, sin(5π)=0), which leaves the threshold/gate outputs decided by the sign of a ~1e-15 residual. At t=9.5 the drives are sine=-2, sine2≈0.707, ramp=5 (ramp completed at t=8), so all 18 block outputs are unambiguous. Outputs are pure algebraic functions of time, so signals snapshots are reproducible.
Usage
BlockComponents.Logical.Tests.Logical()
Behavior
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = BlockComponents.Logical.Tests.Logical() #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hide<< @example-block not executed in draft mode >>Source
"""
Interconnected test of Logical blocks.
Based on the structure of `ModelicaTest.Blocks.Logical` from the Modelica Standard Library.
Tests: And, Or, Xor, Nand, Nor, Not, GreaterThreshold, GreaterEqualThreshold,
LessThreshold, LessEqualThreshold, Greater, GreaterEqual, Less, LessEqual,
LogicalSwitch, Switch.
The stop time is 9.5 s so that every output is evaluated at a deterministic
operating point. At t=10 both sines sit exactly on a zero crossing
(2*sin(10π)=0, sin(5π)=0), which leaves the threshold/gate outputs decided by
the sign of a ~1e-15 residual. At t=9.5 the drives are sine=-2, sine2≈0.707,
ramp=5 (ramp completed at t=8), so all 18 block outputs are unambiguous.
Outputs are pure algebraic functions of time, so `signals` snapshots are
reproducible.
"""
test component Logical
"Sine source for threshold comparisons and boolean signal generation"
sine = BlockComponents.Sources.Sine(amplitude = 2, frequency = 0.5) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 967, "x2": 120, "y2": 1067, "rot": 0}
},
"tags": []
}
}
"Second sine with different frequency for boolean generation"
sine2 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 0.25) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 526, "x2": 120, "y2": 626, "rot": 0}
},
"tags": []
}
}
"Ramp source for two-input comparisons"
ramp = BlockComponents.Sources.Ramp(height = 5, duration = 8) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 1472, "x2": 120, "y2": 1572, "rot": 0}
},
"tags": []
}
}
"Boolean signal generation via thresholds on sine sources"
boolGen1 = BlockComponents.Logical.GreaterThreshold(threshold = 0.0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 350, "x2": 271, "y2": 450, "rot": 0}
},
"tags": []
}
}
boolGen2 = BlockComponents.Logical.GreaterThreshold(threshold = 0.0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 526, "x2": 271, "y2": 626, "rot": 0}
},
"tags": []
}
}
"Logic: Not"
not1 = BlockComponents.Logical.Not() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 340, "y1": 20, "x2": 440, "y2": 120, "rot": 0}
},
"tags": []
}
}
"Logic gates"
and1 = BlockComponents.Logical.And() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 620, "x2": 432, "y2": 720, "rot": 0}
},
"tags": []
}
}
or1 = BlockComponents.Logical.Or() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 500, "x2": 432, "y2": 600, "rot": 0}
},
"tags": []
}
}
xor1 = BlockComponents.Logical.Xor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 260, "x2": 432, "y2": 360, "rot": 0}
},
"tags": []
}
}
nand1 = BlockComponents.Logical.Nand() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 380, "x2": 432, "y2": 480, "rot": 0}
},
"tags": []
}
}
nor1 = BlockComponents.Logical.Nor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 140, "x2": 432, "y2": 240, "rot": 0}
},
"tags": []
}
}
"Threshold comparisons"
greaterThreshold = BlockComponents.Logical.GreaterThreshold(threshold = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 766, "x2": 271, "y2": 866, "rot": 0}
},
"tags": []
}
}
greaterEqualThreshold = BlockComponents.Logical.GreaterEqualThreshold(threshold = -0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1087, "x2": 271, "y2": 1187, "rot": 0}
},
"tags": []
}
}
lessThreshold = BlockComponents.Logical.LessThreshold(threshold = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 646, "x2": 271, "y2": 746, "rot": 0}
},
"tags": []
}
}
lessEqualThreshold = BlockComponents.Logical.LessEqualThreshold(threshold = -0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 967, "x2": 271, "y2": 1067, "rot": 0}
},
"tags": []
}
}
"Two-input comparisons"
greater1 = BlockComponents.Logical.Greater() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1567, "x2": 271, "y2": 1667, "rot": 0}
},
"tags": []
}
}
greaterEqual1 = BlockComponents.Logical.GreaterEqual() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1207, "x2": 271, "y2": 1307, "rot": 0}
},
"tags": []
}
}
less1 = BlockComponents.Logical.Less() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1327, "x2": 271, "y2": 1427, "rot": 0}
},
"tags": []
}
}
lessEqual1 = BlockComponents.Logical.LessEqual() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1447, "x2": 271, "y2": 1547, "rot": 0}
},
"tags": []
}
}
"Switch blocks"
logicalSwitch1 = BlockComponents.Logical.LogicalSwitch() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 766, "x2": 432, "y2": 866, "rot": 0}
},
"tags": []
}
}
switch1 = BlockComponents.Logical.Switch() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 898, "x2": 432, "y2": 998, "rot": 0}
},
"tags": []
}
}
relations
# Boolean signal generation: sine/sine2 → threshold → boolean
connect(sine.y, boolGen1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 150, "y": 400}], "E": 2}],
"junctions": [{"x": 150, "y": 1017}],
"renderStyle": "standard"
}
}
connect(sine2.y, boolGen2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# Not: boolGen1 → not1
connect(boolGen1.y, not1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 311, "y": 70}], "E": 2}],
"junctions": [{"x": 311, "y": 400}],
"renderStyle": "standard"
}
}
# Logic gates: boolGen1 → u1, boolGen2 → u2
connect(boolGen1.y, and1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 640}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, and1.u2) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [], "E": -2},
{"S": -2, "M": [], "E": 2}
],
"junctions": [{"x": 301, "y": 576}, {"x": 301, "y": 696}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, or1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 520}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, or1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen1.y, xor1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 280}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, xor1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 301, "y": 336}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, nand1.u1) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen2.y, nand1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 301, "y": 456}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, nor1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 160}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, nor1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}, {"x": 301, "y": 216}], "E": 2}],
"renderStyle": "standard"
}
}
# Threshold comparisons: all driven by sine
connect(sine.y, greaterThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 816}],
"renderStyle": "standard"
}
}
connect(sine.y, greaterEqualThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1137}],
"renderStyle": "standard"
}
}
connect(sine.y, lessThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 696}],
"renderStyle": "standard"
}
}
connect(sine.y, lessEqualThreshold.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# Two-input comparisons: sine (u1) vs ramp (u2)
connect(sine.y, greater1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}, {"x": 150, "y": 1586}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ramp.y, greater1.u2) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [], "E": -2},
{"S": -2, "M": [], "E": 2}
],
"junctions": [{"x": 140, "y": 1522}, {"x": 140, "y": 1642}],
"renderStyle": "standard"
}
}
connect(sine.y, greaterEqual1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1226}],
"renderStyle": "standard"
}
}
connect(ramp.y, greaterEqual1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 140, "y": 1522}, {"x": 140, "y": 1282}], "E": 2}],
"renderStyle": "standard"
}
}
connect(sine.y, less1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1346}],
"renderStyle": "standard"
}
}
connect(ramp.y, less1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 140, "y": 1522}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 140, "y": 1402}],
"renderStyle": "standard"
}
}
connect(sine.y, lessEqual1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1466}],
"renderStyle": "standard"
}
}
connect(ramp.y, lessEqual1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# LogicalSwitch: select between boolGen1 (u1) and boolGen2 (u3) based on greaterThreshold (u2)
connect(boolGen1.y, logicalSwitch1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}, {"x": 311, "y": 779}], "E": 2}],
"renderStyle": "standard"
}
}
connect(greaterThreshold.y, logicalSwitch1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen2.y, logicalSwitch1.u3) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}, {"x": 301, "y": 853}], "E": 2}],
"renderStyle": "standard"
}
}
# Switch: select between sine (u1) and ramp (u3) based on greaterThreshold (u2)
connect(sine.y, switch1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 916}],
"renderStyle": "standard"
}
}
connect(greaterThreshold.y, switch1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 291, "y": 948}], "E": 2}],
"junctions": [{"x": 291, "y": 816}],
"renderStyle": "standard"
}
}
connect(ramp.y, switch1.u3) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 140, "y": 1522},
{"x": 140, "y": 1717},
{"x": 291, "y": 1717},
{"x": 291, "y": 980}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
metadata {
"Dyad": {
"icons": {"default": "dyad://BlockComponents/Example.svg"},
"tests": {
"case1": {
"stop": 9.5,
"atol": {"sine.y": 0.001, "ramp.y": 0.001, "switch1.y": 0.001},
"expect": {
"signals": [
"sine.y",
"sine2.y",
"ramp.y",
"boolGen1.y",
"boolGen2.y",
"not1.y",
"and1.y",
"or1.y",
"xor1.y",
"nand1.y",
"nor1.y",
"greaterThreshold.y",
"greaterEqualThreshold.y",
"lessThreshold.y",
"lessEqualThreshold.y",
"greater1.y",
"greaterEqual1.y",
"less1.y",
"lessEqual1.y",
"logicalSwitch1.y",
"switch1.y"
]
}
}
}
}
}
endFlattened Source
"""
Interconnected test of Logical blocks.
Based on the structure of `ModelicaTest.Blocks.Logical` from the Modelica Standard Library.
Tests: And, Or, Xor, Nand, Nor, Not, GreaterThreshold, GreaterEqualThreshold,
LessThreshold, LessEqualThreshold, Greater, GreaterEqual, Less, LessEqual,
LogicalSwitch, Switch.
The stop time is 9.5 s so that every output is evaluated at a deterministic
operating point. At t=10 both sines sit exactly on a zero crossing
(2*sin(10π)=0, sin(5π)=0), which leaves the threshold/gate outputs decided by
the sign of a ~1e-15 residual. At t=9.5 the drives are sine=-2, sine2≈0.707,
ramp=5 (ramp completed at t=8), so all 18 block outputs are unambiguous.
Outputs are pure algebraic functions of time, so `signals` snapshots are
reproducible.
"""
test component Logical
"Sine source for threshold comparisons and boolean signal generation"
sine = BlockComponents.Sources.Sine(amplitude = 2, frequency = 0.5) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 967, "x2": 120, "y2": 1067, "rot": 0}
},
"tags": []
}
}
"Second sine with different frequency for boolean generation"
sine2 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 0.25) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 526, "x2": 120, "y2": 626, "rot": 0}
},
"tags": []
}
}
"Ramp source for two-input comparisons"
ramp = BlockComponents.Sources.Ramp(height = 5, duration = 8) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 20, "y1": 1472, "x2": 120, "y2": 1572, "rot": 0}
},
"tags": []
}
}
"Boolean signal generation via thresholds on sine sources"
boolGen1 = BlockComponents.Logical.GreaterThreshold(threshold = 0.0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 350, "x2": 271, "y2": 450, "rot": 0}
},
"tags": []
}
}
boolGen2 = BlockComponents.Logical.GreaterThreshold(threshold = 0.0) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 526, "x2": 271, "y2": 626, "rot": 0}
},
"tags": []
}
}
"Logic: Not"
not1 = BlockComponents.Logical.Not() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 340, "y1": 20, "x2": 440, "y2": 120, "rot": 0}
},
"tags": []
}
}
"Logic gates"
and1 = BlockComponents.Logical.And() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 620, "x2": 432, "y2": 720, "rot": 0}
},
"tags": []
}
}
or1 = BlockComponents.Logical.Or() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 500, "x2": 432, "y2": 600, "rot": 0}
},
"tags": []
}
}
xor1 = BlockComponents.Logical.Xor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 260, "x2": 432, "y2": 360, "rot": 0}
},
"tags": []
}
}
nand1 = BlockComponents.Logical.Nand() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 380, "x2": 432, "y2": 480, "rot": 0}
},
"tags": []
}
}
nor1 = BlockComponents.Logical.Nor() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 140, "x2": 432, "y2": 240, "rot": 0}
},
"tags": []
}
}
"Threshold comparisons"
greaterThreshold = BlockComponents.Logical.GreaterThreshold(threshold = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 766, "x2": 271, "y2": 866, "rot": 0}
},
"tags": []
}
}
greaterEqualThreshold = BlockComponents.Logical.GreaterEqualThreshold(threshold = -0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1087, "x2": 271, "y2": 1187, "rot": 0}
},
"tags": []
}
}
lessThreshold = BlockComponents.Logical.LessThreshold(threshold = 0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 646, "x2": 271, "y2": 746, "rot": 0}
},
"tags": []
}
}
lessEqualThreshold = BlockComponents.Logical.LessEqualThreshold(threshold = -0.1) {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 967, "x2": 271, "y2": 1067, "rot": 0}
},
"tags": []
}
}
"Two-input comparisons"
greater1 = BlockComponents.Logical.Greater() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1567, "x2": 271, "y2": 1667, "rot": 0}
},
"tags": []
}
}
greaterEqual1 = BlockComponents.Logical.GreaterEqual() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1207, "x2": 271, "y2": 1307, "rot": 0}
},
"tags": []
}
}
less1 = BlockComponents.Logical.Less() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1327, "x2": 271, "y2": 1427, "rot": 0}
},
"tags": []
}
}
lessEqual1 = BlockComponents.Logical.LessEqual() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 171, "y1": 1447, "x2": 271, "y2": 1547, "rot": 0}
},
"tags": []
}
}
"Switch blocks"
logicalSwitch1 = BlockComponents.Logical.LogicalSwitch() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 766, "x2": 432, "y2": 866, "rot": 0}
},
"tags": []
}
}
switch1 = BlockComponents.Logical.Switch() {
"Dyad": {
"placement": {
"diagram": {"iconName": "default", "x1": 332, "y1": 898, "x2": 432, "y2": 998, "rot": 0}
},
"tags": []
}
}
relations
# Boolean signal generation: sine/sine2 → threshold → boolean
connect(sine.y, boolGen1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 150, "y": 400}], "E": 2}],
"junctions": [{"x": 150, "y": 1017}],
"renderStyle": "standard"
}
}
connect(sine2.y, boolGen2.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# Not: boolGen1 → not1
connect(boolGen1.y, not1.u) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 311, "y": 70}], "E": 2}],
"junctions": [{"x": 311, "y": 400}],
"renderStyle": "standard"
}
}
# Logic gates: boolGen1 → u1, boolGen2 → u2
connect(boolGen1.y, and1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 640}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, and1.u2) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [], "E": -2},
{"S": -2, "M": [], "E": 2}
],
"junctions": [{"x": 301, "y": 576}, {"x": 301, "y": 696}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, or1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 520}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, or1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen1.y, xor1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 280}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, xor1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 301, "y": 336}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, nand1.u1) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen2.y, nand1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 301, "y": 456}],
"renderStyle": "standard"
}
}
connect(boolGen1.y, nor1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 311, "y": 160}],
"renderStyle": "standard"
}
}
connect(boolGen2.y, nor1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}, {"x": 301, "y": 216}], "E": 2}],
"renderStyle": "standard"
}
}
# Threshold comparisons: all driven by sine
connect(sine.y, greaterThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 816}],
"renderStyle": "standard"
}
}
connect(sine.y, greaterEqualThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1137}],
"renderStyle": "standard"
}
}
connect(sine.y, lessThreshold.u) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 696}],
"renderStyle": "standard"
}
}
connect(sine.y, lessEqualThreshold.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# Two-input comparisons: sine (u1) vs ramp (u2)
connect(sine.y, greater1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}, {"x": 150, "y": 1586}], "E": 2}],
"renderStyle": "standard"
}
}
connect(ramp.y, greater1.u2) {
"Dyad": {
"edges": [
{"S": 1, "M": [], "E": -1},
{"S": -1, "M": [], "E": -2},
{"S": -2, "M": [], "E": 2}
],
"junctions": [{"x": 140, "y": 1522}, {"x": 140, "y": 1642}],
"renderStyle": "standard"
}
}
connect(sine.y, greaterEqual1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1226}],
"renderStyle": "standard"
}
}
connect(ramp.y, greaterEqual1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 140, "y": 1522}, {"x": 140, "y": 1282}], "E": 2}],
"renderStyle": "standard"
}
}
connect(sine.y, less1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1346}],
"renderStyle": "standard"
}
}
connect(ramp.y, less1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 140, "y": 1522}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 140, "y": 1402}],
"renderStyle": "standard"
}
}
connect(sine.y, lessEqual1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 1466}],
"renderStyle": "standard"
}
}
connect(ramp.y, lessEqual1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
# LogicalSwitch: select between boolGen1 (u1) and boolGen2 (u3) based on greaterThreshold (u2)
connect(boolGen1.y, logicalSwitch1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 311, "y": 400}, {"x": 311, "y": 779}], "E": 2}],
"renderStyle": "standard"
}
}
connect(greaterThreshold.y, logicalSwitch1.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
connect(boolGen2.y, logicalSwitch1.u3) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 301, "y": 576}, {"x": 301, "y": 853}], "E": 2}],
"renderStyle": "standard"
}
}
# Switch: select between sine (u1) and ramp (u3) based on greaterThreshold (u2)
connect(sine.y, switch1.u1) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 150, "y": 1017}], "E": -1}, {"S": -1, "M": [], "E": 2}],
"junctions": [{"x": 150, "y": 916}],
"renderStyle": "standard"
}
}
connect(greaterThreshold.y, switch1.u2) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 291, "y": 948}], "E": 2}],
"junctions": [{"x": 291, "y": 816}],
"renderStyle": "standard"
}
}
connect(ramp.y, switch1.u3) {
"Dyad": {
"edges": [
{
"S": 1,
"M": [
{"x": 140, "y": 1522},
{"x": 140, "y": 1717},
{"x": 291, "y": 1717},
{"x": 291, "y": 980}
],
"E": 2
}
],
"renderStyle": "standard"
}
}
metadata {
"Dyad": {
"icons": {"default": "dyad://BlockComponents/Example.svg"},
"tests": {
"case1": {
"stop": 9.5,
"atol": {"sine.y": 0.001, "ramp.y": 0.001, "switch1.y": 0.001},
"expect": {
"signals": [
"sine.y",
"sine2.y",
"ramp.y",
"boolGen1.y",
"boolGen2.y",
"not1.y",
"and1.y",
"or1.y",
"xor1.y",
"nand1.y",
"nor1.y",
"greaterThreshold.y",
"greaterEqualThreshold.y",
"lessThreshold.y",
"lessEqualThreshold.y",
"greater1.y",
"greaterEqual1.y",
"less1.y",
"lessEqual1.y",
"logicalSwitch1.y",
"switch1.y"
]
}
}
}
}
}
endTest Cases
using BlockComponents
using DyadInterface: TransientAnalysis, rebuild_sol, ODEAlg
using ModelingToolkit: toggle_namespacing, get_initial_conditions, @named
using CSV, DataFrames, Plots
snapshotsdir = joinpath(dirname(dirname(pathof(BlockComponents))), "test", "snapshots")<< @setup-block not executed in draft mode >>Test Case case1
@named model_case1 = BlockComponents.Logical.Tests.Logical()
model_case1 = toggle_namespacing(model_case1, false)
model_case1 = toggle_namespacing(model_case1, true)
result_case1 = TransientAnalysis(; model = model_case1, alg = ODEAlg.Auto(), start = 0e+0, stop = 9.5e+0, abstol=1e-6, reltol=1e-6)
sol_case1 = rebuild_sol(result_case1)<< @setup-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.sine.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.sine.y], width=2, label="Actual value of sine.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of sine.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.sine2.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.sine2.y], width=2, label="Actual value of sine2.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of sine2.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.ramp.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.ramp.y], width=2, label="Actual value of ramp.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of ramp.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.boolGen1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.boolGen1.y], width=2, label="Actual value of boolGen1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of boolGen1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.boolGen2.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig4.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.boolGen2.y], width=2, label="Actual value of boolGen2.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of boolGen2.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.not1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig5.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.not1.y], width=2, label="Actual value of not1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of not1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.and1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig6.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.and1.y], width=2, label="Actual value of and1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of and1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.or1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig7.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.or1.y], width=2, label="Actual value of or1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of or1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.xor1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig8.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.xor1.y], width=2, label="Actual value of xor1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of xor1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.nand1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig9.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.nand1.y], width=2, label="Actual value of nand1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of nand1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.nor1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig10.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.nor1.y], width=2, label="Actual value of nor1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of nor1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.greaterThreshold.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig11.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.greaterThreshold.y], width=2, label="Actual value of greaterThreshold.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of greaterThreshold.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.greaterEqualThreshold.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig12.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.greaterEqualThreshold.y], width=2, label="Actual value of greaterEqualThreshold.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of greaterEqualThreshold.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.lessThreshold.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig13.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.lessThreshold.y], width=2, label="Actual value of lessThreshold.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of lessThreshold.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.lessEqualThreshold.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig14.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.lessEqualThreshold.y], width=2, label="Actual value of lessEqualThreshold.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of lessEqualThreshold.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.greater1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig15.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.greater1.y], width=2, label="Actual value of greater1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of greater1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.greaterEqual1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig16.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.greaterEqual1.y], width=2, label="Actual value of greaterEqual1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of greaterEqual1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.less1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig17.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.less1.y], width=2, label="Actual value of less1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of less1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.lessEqual1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig18.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.lessEqual1.y], width=2, label="Actual value of lessEqual1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of lessEqual1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.logicalSwitch1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig19.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.logicalSwitch1.y], width=2, label="Actual value of logicalSwitch1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of logicalSwitch1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.switch1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.Logical_case1_sig20.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.switch1.y], width=2, label="Actual value of switch1.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of switch1.y")
end<< @setup-block not executed in draft mode >>plt<< @example-block not executed in draft mode >>