Skip to content
LIBRARY
Logical.Tests.Logical.md

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

julia
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

dyad
"""
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"
          ]
        }
      }
    }
  }
}
end
Flattened Source
dyad
"""
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"
          ]
        }
      }
    }
  }
}
end


Test Cases

julia
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

julia
@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 >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>
julia
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 >>
julia
plt
<< @example-block not executed in draft mode >>