Skip to content
LIBRARY
PlanarMechanics.CutTorqueTest.md

PlanarMechanics.CutTorqueTest

Test for CutTorque sensor: a damped pendulum with a CutTorque sensor, a WorldForce applying sinusoidal forces and torque to the body.

Topology: Fixed → CutTorque → FixedTranslation1(r=[0.5,0]) → Revolute → FixedTranslation(r=[1,0]) → Body ← WorldForce A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutTorques.

Usage

MultibodyComponents.PlanarMechanics.CutTorqueTest()

Behavior

julia
using MultibodyComponents #hide
using ModelingToolkit #hide
@named sys = MultibodyComponents.PlanarMechanics.CutTorqueTest() #hide
full_equations(sys) #hide
<< @example-block not executed in draft mode >>

Source

dyad
"""
Test for CutTorque sensor: a damped pendulum with a CutTorque sensor, a WorldForce
applying sinusoidal forces and torque to the body.

Topology: Fixed → CutTorque → FixedTranslation1(r=[0.5,0]) → Revolute → FixedTranslation(r=[1,0]) → Body ← WorldForce
A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutTorques.
"""
test component CutTorqueTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 20, "x2": 120, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  fixed_frame = MultibodyComponents.PlanarMechanics.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 40, "y1": 380, "x2": 140, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  cut_torque = CutTorque(scale = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 155, "y1": 380, "x2": 255, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  rod1 = FixedTranslation(r = [0.5, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 380, "x2": 380, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  revolute = Revolute() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 380, "x2": 510, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  rod = FixedTranslation(r = [1, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 542.5,
          "y1": 380,
          "x2": 642.5,
          "y2": 480,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  body = Body(m = 1, I = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 670, "y1": 140, "x2": 770, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  damper = RotationalComponents.Components.Damper(d = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 560, "x2": 510, "y2": 660, "rot": 270}
      },
      "tags": []
    }
  }
  rot_fixed = RotationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 720, "x2": 510, "y2": 820, "rot": 0}
      },
      "tags": []
    }
  }
  "WorldForce applying sinusoidal force and torque to body"
  world_force = WorldForceTorque(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameB()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 540, "y1": 140, "x2": 640, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine signals: force in y direction and torque"
  sine_fx = BlockComponents.Sources.Sine(amplitude = 0, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 20, "x2": 505, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  sine_fy = BlockComponents.Sources.Sine(amplitude = -5, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 140, "x2": 505, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  sine_tau = BlockComponents.Sources.Sine(amplitude = 1, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 260, "x2": 505, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
relations
  initial revolute.phi = 0
  initial revolute.w = 0
  # Main chain: Fixed → CutTorque → Rod1 → Revolute → Rod → Body
  connect(fixed_frame.frame_b, cut_torque.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_torque.frame_b, rod1.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod1.frame_b, revolute.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(revolute.frame_b, rod.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod.frame_b, body.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 660, "y": 430}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 660, "y": 190}],
      "renderStyle": "standard"
    }
  }
  # WorldForce on body
  connect(world_force.frame_b, body.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(sine_fx.y, world_force.force_x) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 525, "y": 70}, {"x": 525, "y": 155}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(sine_fy.y, world_force.force_y) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(sine_tau.y, world_force.torque) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 525, "y": 310}, {"x": 525, "y": 223}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # Rotational damper on revolute flange
  connect(damper.spline_b, revolute.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rot_fixed.spline, damper.spline_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 3}}}}
end
Flattened Source
dyad
"""
Test for CutTorque sensor: a damped pendulum with a CutTorque sensor, a WorldForce
applying sinusoidal forces and torque to the body.

Topology: Fixed → CutTorque → FixedTranslation1(r=[0.5,0]) → Revolute → FixedTranslation(r=[1,0]) → Body ← WorldForce
A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutTorques.
"""
test component CutTorqueTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 20, "x2": 120, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  fixed_frame = MultibodyComponents.PlanarMechanics.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 40, "y1": 380, "x2": 140, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  cut_torque = CutTorque(scale = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 155, "y1": 380, "x2": 255, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  rod1 = FixedTranslation(r = [0.5, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 380, "x2": 380, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  revolute = Revolute() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 380, "x2": 510, "y2": 480, "rot": 0}
      },
      "tags": []
    }
  }
  rod = FixedTranslation(r = [1, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {
          "iconName": "default",
          "x1": 542.5,
          "y1": 380,
          "x2": 642.5,
          "y2": 480,
          "rot": 0
        }
      },
      "tags": []
    }
  }
  body = Body(m = 1, I = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 670, "y1": 140, "x2": 770, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  damper = RotationalComponents.Components.Damper(d = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 560, "x2": 510, "y2": 660, "rot": 270}
      },
      "tags": []
    }
  }
  rot_fixed = RotationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 410, "y1": 720, "x2": 510, "y2": 820, "rot": 0}
      },
      "tags": []
    }
  }
  "WorldForce applying sinusoidal force and torque to body"
  world_force = WorldForceTorque(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameB()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 540, "y1": 140, "x2": 640, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine signals: force in y direction and torque"
  sine_fx = BlockComponents.Sources.Sine(amplitude = 0, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 20, "x2": 505, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  sine_fy = BlockComponents.Sources.Sine(amplitude = -5, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 140, "x2": 505, "y2": 240, "rot": 0}
      },
      "tags": []
    }
  }
  sine_tau = BlockComponents.Sources.Sine(amplitude = 1, frequency = 1, start_time = 1.8) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 405, "y1": 260, "x2": 505, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
relations
  initial revolute.phi = 0
  initial revolute.w = 0
  # Main chain: Fixed → CutTorque → Rod1 → Revolute → Rod → Body
  connect(fixed_frame.frame_b, cut_torque.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_torque.frame_b, rod1.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod1.frame_b, revolute.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(revolute.frame_b, rod.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod.frame_b, body.frame_a) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 660, "y": 430}], "E": -1}, {"S": -1, "M": [], "E": 2}],
      "junctions": [{"x": 660, "y": 190}],
      "renderStyle": "standard"
    }
  }
  # WorldForce on body
  connect(world_force.frame_b, body.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(sine_fx.y, world_force.force_x) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 525, "y": 70}, {"x": 525, "y": 155}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(sine_fy.y, world_force.force_y) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(sine_tau.y, world_force.torque) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 525, "y": 310}, {"x": 525, "y": 223}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # Rotational damper on revolute flange
  connect(damper.spline_b, revolute.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rot_fixed.spline, damper.spline_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
metadata {"Dyad": {"tests": {"case1": {"stop": 3}}}}
end


Test Cases

julia
using MultibodyComponents
using DyadInterface: TransientAnalysis, rebuild_sol, ODEAlg
using ModelingToolkit: toggle_namespacing, get_initial_conditions, @named
using CSV, DataFrames, Plots

snapshotsdir = joinpath(dirname(dirname(pathof(MultibodyComponents))), "test", "snapshots")
<< @setup-block not executed in draft mode >>

Test Case case1

julia
@named model_case1 = MultibodyComponents.PlanarMechanics.CutTorqueTest()
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 = 3e+0, abstol=1e-6, reltol=1e-6)
sol_case1 = rebuild_sol(result_case1)
<< @setup-block not executed in draft mode >>