Skip to content
LIBRARY
Logical.Tests.LogicalValidation.md

Logical.Tests.LogicalValidation

Validation test for all Logical blocks with deterministic expected outputs.

Uses a Ramp with height=4, duration=4: ramp(t) = offset + t. At t=1 (the common stop time), ramp = offset + 1. A Constant at 1.0 serves as the second input for two-input comparisons. All threshold blocks use threshold = 1.0.

Three test cases via params overriding ramp.offset:

  • below (offset=-2): ramp(1)=-1.0 < threshold

  • equal (offset=0): ramp(1)=1.0 = threshold

  • above (offset=1.5): ramp(1)=2.5 > threshold

Usage

BlockComponents.Logical.Tests.LogicalValidation()

Behavior

julia
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = BlockComponents.Logical.Tests.LogicalValidation() #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hide
<< @example-block not executed in draft mode >>

Source

dyad
"""
Validation test for all Logical blocks with deterministic expected outputs.

Uses a Ramp with height=4, duration=4: ramp(t) = offset + t.
At t=1 (the common stop time), ramp = offset + 1.
A Constant at 1.0 serves as the second input for two-input comparisons.
All threshold blocks use threshold = 1.0.

Three test cases via `params` overriding `ramp.offset`:
- below (offset=-2): ramp(1)=-1.0 < threshold
- equal (offset=0):  ramp(1)=1.0 = threshold
- above (offset=1.5): ramp(1)=2.5 > threshold
"""
test component LogicalValidation
  "Ramp: ramp(t) = offset + t for t in [0, 4]"
  ramp = BlockComponents.Sources.Ramp(height = 4, duration = 4, offset = -2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 1200, "x2": 120, "y2": 1300, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant reference at 1.0"
  const1 = BlockComponents.Sources.Constant(k = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 405, "x2": 120, "y2": 505, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant reference at 10.0 for Switch test"
  const10 = BlockComponents.Sources.Constant(k = 10.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 1301, "x2": 271, "y2": 1401, "rot": 0}
      },
      "tags": []
    }
  }
  "Always-true boolean source (threshold well below ramp range)"
  boolTrue = BlockComponents.Logical.GreaterThreshold(threshold = -100.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 620, "x2": 271, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Always-false boolean source (threshold well above ramp range)"
  boolFalse = BlockComponents.Logical.GreaterThreshold(threshold = 100.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 740, "x2": 271, "y2": 840, "rot": 0}
      },
      "tags": []
    }
  }
  "Threshold comparisons (threshold = 1.0)"
  gt_thresh = BlockComponents.Logical.GreaterThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 980, "x2": 271, "y2": 1080, "rot": 0}
      },
      "tags": []
    }
  }
  ge_thresh = BlockComponents.Logical.GreaterEqualThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 1100, "x2": 271, "y2": 1200, "rot": 0}
      },
      "tags": []
    }
  }
  lt_thresh = BlockComponents.Logical.LessThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 860, "x2": 271, "y2": 960, "rot": 0}
      },
      "tags": []
    }
  }
  le_thresh = BlockComponents.Logical.LessEqualThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 500, "x2": 271, "y2": 600, "rot": 0}
      },
      "tags": []
    }
  }
  "Two-input comparisons: ramp (u1) vs const1 (u2)"
  gt = BlockComponents.Logical.Greater() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 140, "x2": 271, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  ge = BlockComponents.Logical.GreaterEqual() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 380, "x2": 271, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  lt = BlockComponents.Logical.Less() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 260, "x2": 271, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
  le = BlockComponents.Logical.LessEqual() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 180, "y1": 20, "x2": 280, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "Not: inverts GreaterThreshold output"
  not1 = BlockComponents.Logical.Not() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 980, "x2": 412, "y2": 1080, "rot": 0}
      },
      "tags": []
    }
  }
  "Switch: selects ramp (u1) when gt_thresh true, else const10 (u3)"
  sw = BlockComponents.Logical.Switch() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 1232, "x2": 412, "y2": 1332, "rot": 0}
      },
      "tags": []
    }
  }
  "LogicalSwitch: selects boolTrue (u1) when gt_thresh true, else boolFalse (u3)"
  lsw = BlockComponents.Logical.LogicalSwitch() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 703, "x2": 412, "y2": 803, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # Threshold comparisons: all driven by ramp
  connect(ramp.y, gt_thresh.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 140, "y": 1250}, {"x": 140, "y": 1030}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, ge_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 1150}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, lt_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 910}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, le_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 550}],
      "renderStyle": "standard"
    }
  }
  # Two-input comparisons: ramp (u1) vs const1 (u2)
  connect(ramp.y, gt.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 159}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, gt.u2) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 150, "y": 455}, {"x": 150, "y": 215}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, ge.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 399}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, ge.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(ramp.y, lt.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 279}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, lt.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 150, "y": 455}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 150, "y": 335}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, le.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}, {"x": 140, "y": 39}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, le.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 150, "y": 455}, {"x": 150, "y": 95}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # Not: inverts GreaterThreshold
  connect(gt_thresh.y, not1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  # Boolean sources for LogicalSwitch
  connect(ramp.y, boolTrue.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, boolFalse.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 790}],
      "renderStyle": "standard"
    }
  }
  # Switch: u2=gt_thresh controls between ramp (u1) and const10 (u3)
  connect(ramp.y, sw.u1) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(gt_thresh.y, sw.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 291, "y": 1282}], "E": 2}],
      "junctions": [{"x": 291, "y": 1030}],
      "renderStyle": "standard"
    }
  }
  connect(const10.y, sw.u3) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 1351}, {"x": 291, "y": 1314}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # LogicalSwitch: u2=gt_thresh controls between boolTrue (u1) and boolFalse (u3)
  connect(boolTrue.y, lsw.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 670}, {"x": 291, "y": 716}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(gt_thresh.y, lsw.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 1030}, {"x": 291, "y": 753}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(boolFalse.y, lsw.u3) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {
  "Dyad": {
    "icons": {"default": "dyad://BlockComponents/Example.svg"},
    "tests": {
      "below": {
        "stop": 1,
        "params": {"ramp.offset": -2},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.y"
          ]
        }
      },
      "equal": {
        "stop": 1,
        "params": {"ramp.offset": 0},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.y"
          ]
        }
      },
      "above": {
        "stop": 1,
        "params": {"ramp.offset": 1.5},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.y"
          ]
        }
      }
    }
  }
}
end
Flattened Source
dyad
"""
Validation test for all Logical blocks with deterministic expected outputs.

Uses a Ramp with height=4, duration=4: ramp(t) = offset + t.
At t=1 (the common stop time), ramp = offset + 1.
A Constant at 1.0 serves as the second input for two-input comparisons.
All threshold blocks use threshold = 1.0.

Three test cases via `params` overriding `ramp.offset`:
- below (offset=-2): ramp(1)=-1.0 < threshold
- equal (offset=0):  ramp(1)=1.0 = threshold
- above (offset=1.5): ramp(1)=2.5 > threshold
"""
test component LogicalValidation
  "Ramp: ramp(t) = offset + t for t in [0, 4]"
  ramp = BlockComponents.Sources.Ramp(height = 4, duration = 4, offset = -2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 1200, "x2": 120, "y2": 1300, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant reference at 1.0"
  const1 = BlockComponents.Sources.Constant(k = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 405, "x2": 120, "y2": 505, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant reference at 10.0 for Switch test"
  const10 = BlockComponents.Sources.Constant(k = 10.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 1301, "x2": 271, "y2": 1401, "rot": 0}
      },
      "tags": []
    }
  }
  "Always-true boolean source (threshold well below ramp range)"
  boolTrue = BlockComponents.Logical.GreaterThreshold(threshold = -100.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 620, "x2": 271, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Always-false boolean source (threshold well above ramp range)"
  boolFalse = BlockComponents.Logical.GreaterThreshold(threshold = 100.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 740, "x2": 271, "y2": 840, "rot": 0}
      },
      "tags": []
    }
  }
  "Threshold comparisons (threshold = 1.0)"
  gt_thresh = BlockComponents.Logical.GreaterThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 980, "x2": 271, "y2": 1080, "rot": 0}
      },
      "tags": []
    }
  }
  ge_thresh = BlockComponents.Logical.GreaterEqualThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 1100, "x2": 271, "y2": 1200, "rot": 0}
      },
      "tags": []
    }
  }
  lt_thresh = BlockComponents.Logical.LessThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 860, "x2": 271, "y2": 960, "rot": 0}
      },
      "tags": []
    }
  }
  le_thresh = BlockComponents.Logical.LessEqualThreshold(threshold = 1.0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 500, "x2": 271, "y2": 600, "rot": 0}
      },
      "tags": []
    }
  }
  "Two-input comparisons: ramp (u1) vs const1 (u2)"
  gt = BlockComponents.Logical.Greater() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 140, "x2": 271, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  ge = BlockComponents.Logical.GreaterEqual() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 380, "x2": 271, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  lt = BlockComponents.Logical.Less() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 171, "y1": 260, "x2": 271, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
  le = BlockComponents.Logical.LessEqual() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 180, "y1": 20, "x2": 280, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "Not: inverts GreaterThreshold output"
  not1 = BlockComponents.Logical.Not() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 980, "x2": 412, "y2": 1080, "rot": 0}
      },
      "tags": []
    }
  }
  "Switch: selects ramp (u1) when gt_thresh true, else const10 (u3)"
  sw = BlockComponents.Logical.Switch() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 1232, "x2": 412, "y2": 1332, "rot": 0}
      },
      "tags": []
    }
  }
  "LogicalSwitch: selects boolTrue (u1) when gt_thresh true, else boolFalse (u3)"
  lsw = BlockComponents.Logical.LogicalSwitch() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 312, "y1": 703, "x2": 412, "y2": 803, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # Threshold comparisons: all driven by ramp
  connect(ramp.y, gt_thresh.u) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 140, "y": 1250}, {"x": 140, "y": 1030}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, ge_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 1150}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, lt_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 910}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, le_thresh.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 550}],
      "renderStyle": "standard"
    }
  }
  # Two-input comparisons: ramp (u1) vs const1 (u2)
  connect(ramp.y, gt.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 159}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, gt.u2) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [], "E": -2},
        {"S": -2, "M": [], "E": 2}
      ],
      "junctions": [{"x": 150, "y": 455}, {"x": 150, "y": 215}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, ge.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 399}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, ge.u2) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(ramp.y, lt.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 279}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, lt.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 150, "y": 455}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 150, "y": 335}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, le.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}, {"x": 140, "y": 39}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(const1.y, le.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 150, "y": 455}, {"x": 150, "y": 95}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # Not: inverts GreaterThreshold
  connect(gt_thresh.y, not1.u) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  # Boolean sources for LogicalSwitch
  connect(ramp.y, boolTrue.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, boolFalse.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 140, "y": 1250}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 140, "y": 790}],
      "renderStyle": "standard"
    }
  }
  # Switch: u2=gt_thresh controls between ramp (u1) and const10 (u3)
  connect(ramp.y, sw.u1) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(gt_thresh.y, sw.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [], "E": -1}, {"S": -1, "M": [{"x": 291, "y": 1282}], "E": 2}],
      "junctions": [{"x": 291, "y": 1030}],
      "renderStyle": "standard"
    }
  }
  connect(const10.y, sw.u3) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 1351}, {"x": 291, "y": 1314}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # LogicalSwitch: u2=gt_thresh controls between boolTrue (u1) and boolFalse (u3)
  connect(boolTrue.y, lsw.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 670}, {"x": 291, "y": 716}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(gt_thresh.y, lsw.u2) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 291, "y": 1030}, {"x": 291, "y": 753}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(boolFalse.y, lsw.u3) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {
  "Dyad": {
    "icons": {"default": "dyad://BlockComponents/Example.svg"},
    "tests": {
      "below": {
        "stop": 1,
        "params": {"ramp.offset": -2},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.y"
          ]
        }
      },
      "equal": {
        "stop": 1,
        "params": {"ramp.offset": 0},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.y"
          ]
        }
      },
      "above": {
        "stop": 1,
        "params": {"ramp.offset": 1.5},
        "expect": {
          "signals": [
            "ramp.y",
            "gt_thresh.y",
            "ge_thresh.y",
            "lt_thresh.y",
            "le_thresh.y",
            "gt.y",
            "ge.y",
            "lt.y",
            "le.y",
            "not1.y",
            "sw.y",
            "lsw.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 below

julia
@named model_below = BlockComponents.Logical.Tests.LogicalValidation(ramp.offset=-2)
model_below = toggle_namespacing(model_below, false)

model_below = toggle_namespacing(model_below, true)
result_below = TransientAnalysis(; model = model_below, alg = ODEAlg.Auto(), start = 0e+0, stop = 1e+0, abstol=1e-6, reltol=1e-6)
sol_below = rebuild_sol(result_below)
<< @setup-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.ramp.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.ramp.y], width=2, label="Actual value of ramp.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.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_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.gt_thresh.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.gt_thresh.y], width=2, label="Actual value of gt_thresh.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of gt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.ge_thresh.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.ge_thresh.y], width=2, label="Actual value of ge_thresh.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of ge_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.lt_thresh.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.lt_thresh.y], width=2, label="Actual value of lt_thresh.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of lt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.le_thresh.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig4.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.le_thresh.y], width=2, label="Actual value of le_thresh.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of le_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.gt.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig5.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.gt.y], width=2, label="Actual value of gt.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of gt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.ge.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig6.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.ge.y], width=2, label="Actual value of ge.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of ge.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.lt.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig7.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.lt.y], width=2, label="Actual value of lt.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of lt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.le.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig8.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.le.y], width=2, label="Actual value of le.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of le.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.not1.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig9.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.not1.y], width=2, label="Actual value of not1.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.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_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.sw.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig10.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.sw.y], width=2, label="Actual value of sw.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of sw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_below = DataFrame(:t => sol_below[:t], :actual => sol_below[model_below.lsw.y])
dfr_below = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_below_sig11.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_below, idxs=[model_below.lsw.y], width=2, label="Actual value of lsw.y")
if !isnothing(dfr_below)
  scatter!(plt, dfr_below.t, dfr_below.expected, mc=:red, ms=3, label="Expected value of lsw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>

Test Case equal

julia
@named model_equal = BlockComponents.Logical.Tests.LogicalValidation(ramp.offset=0)
model_equal = toggle_namespacing(model_equal, false)

model_equal = toggle_namespacing(model_equal, true)
result_equal = TransientAnalysis(; model = model_equal, alg = ODEAlg.Auto(), start = 0e+0, stop = 1e+0, abstol=1e-6, reltol=1e-6)
sol_equal = rebuild_sol(result_equal)
<< @setup-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.ramp.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.ramp.y], width=2, label="Actual value of ramp.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.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_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.gt_thresh.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.gt_thresh.y], width=2, label="Actual value of gt_thresh.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of gt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.ge_thresh.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.ge_thresh.y], width=2, label="Actual value of ge_thresh.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of ge_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.lt_thresh.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.lt_thresh.y], width=2, label="Actual value of lt_thresh.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of lt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.le_thresh.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig4.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.le_thresh.y], width=2, label="Actual value of le_thresh.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of le_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.gt.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig5.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.gt.y], width=2, label="Actual value of gt.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of gt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.ge.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig6.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.ge.y], width=2, label="Actual value of ge.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of ge.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.lt.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig7.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.lt.y], width=2, label="Actual value of lt.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of lt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.le.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig8.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.le.y], width=2, label="Actual value of le.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of le.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.not1.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig9.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.not1.y], width=2, label="Actual value of not1.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.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_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.sw.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig10.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.sw.y], width=2, label="Actual value of sw.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of sw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_equal = DataFrame(:t => sol_equal[:t], :actual => sol_equal[model_equal.lsw.y])
dfr_equal = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_equal_sig11.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_equal, idxs=[model_equal.lsw.y], width=2, label="Actual value of lsw.y")
if !isnothing(dfr_equal)
  scatter!(plt, dfr_equal.t, dfr_equal.expected, mc=:red, ms=3, label="Expected value of lsw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>

Test Case above

julia
@named model_above = BlockComponents.Logical.Tests.LogicalValidation(ramp.offset=1.5)
model_above = toggle_namespacing(model_above, false)

model_above = toggle_namespacing(model_above, true)
result_above = TransientAnalysis(; model = model_above, alg = ODEAlg.Auto(), start = 0e+0, stop = 1e+0, abstol=1e-6, reltol=1e-6)
sol_above = rebuild_sol(result_above)
<< @setup-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.ramp.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.ramp.y], width=2, label="Actual value of ramp.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.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_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.gt_thresh.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.gt_thresh.y], width=2, label="Actual value of gt_thresh.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of gt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.ge_thresh.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.ge_thresh.y], width=2, label="Actual value of ge_thresh.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of ge_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.lt_thresh.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.lt_thresh.y], width=2, label="Actual value of lt_thresh.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of lt_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.le_thresh.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig4.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.le_thresh.y], width=2, label="Actual value of le_thresh.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of le_thresh.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.gt.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig5.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.gt.y], width=2, label="Actual value of gt.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of gt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.ge.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig6.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.ge.y], width=2, label="Actual value of ge.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of ge.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.lt.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig7.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.lt.y], width=2, label="Actual value of lt.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of lt.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.le.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig8.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.le.y], width=2, label="Actual value of le.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of le.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.not1.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig9.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.not1.y], width=2, label="Actual value of not1.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.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_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.sw.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig10.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.sw.y], width=2, label="Actual value of sw.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of sw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_above = DataFrame(:t => sol_above[:t], :actual => sol_above[model_above.lsw.y])
dfr_above = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Logical.Tests.LogicalValidation_above_sig11.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_above, idxs=[model_above.lsw.y], width=2, label="Actual value of lsw.y")
if !isnothing(dfr_above)
  scatter!(plt, dfr_above.t, dfr_above.expected, mc=:red, ms=3, label="Expected value of lsw.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>