Skip to content
LIBRARY
PlanarMechanics.CutForceTest.md

PlanarMechanics.CutForceTest

Test for CutForce sensor: a damped pendulum with three CutForce sensors measuring reaction forces at different points in the chain, each resolved in a different frame.

Topology: World → CutForce(frame_a) → Revolute → CutForce(world) → FixedTranslation → CutForce(frame_b) → Body A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutForces (line 633).

Usage

MultibodyComponents.PlanarMechanics.CutForceTest()

Behavior

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

Source

dyad
"""
Test for CutForce sensor: a damped pendulum with three CutForce sensors
measuring reaction forces at different points in the chain, each resolved
in a different frame.

Topology: World → CutForce(frame_a) → Revolute → CutForce(world) → FixedTranslation → CutForce(frame_b) → Body
A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutForces (line 633).
"""
test component CutForceTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 20, "x2": 120, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  revolute = Revolute() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 20, "x2": 380, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  rod = FixedTranslation(r = [1, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 521, "y1": 20, "x2": 621, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  body = Body(m = 1, I = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 767, "y1": 20, "x2": 867, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  damper = RotationalComponents.Components.Damper(d = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 190, "x2": 380, "y2": 290, "rot": 270}
      },
      "tags": []
    }
  }
  fixed = RotationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 340, "x2": 380, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in frame_a (between world and revolute)"
  cut_force_frame_a = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameA()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 150, "y1": 20, "x2": 250, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in world frame (between revolute and rod)"
  cut_force_world = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 395, "y1": 20, "x2": 495, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in frame_b (between rod and body)"
  cut_force_frame_b = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameB()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 641, "y1": 20, "x2": 741, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
relations
  initial revolute.phi = 0
  initial revolute.w = 0
  # Main chain: World → CutForce(A) → Revolute → CutForce(W) → Rod → CutForce(B) → Body
  connect(world.frame_b, cut_force_frame_a.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_frame_a.frame_b, revolute.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(revolute.frame_b, cut_force_world.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_world.frame_b, rod.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod.frame_b, cut_force_frame_b.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_frame_b.frame_b, body.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "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(damper.spline_a, fixed.spline) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
metadata {"Dyad": {"tests": {"case1": {"stop": 3}}}}
end
Flattened Source
dyad
"""
Test for CutForce sensor: a damped pendulum with three CutForce sensors
measuring reaction forces at different points in the chain, each resolved
in a different frame.

Topology: World → CutForce(frame_a) → Revolute → CutForce(world) → FixedTranslation → CutForce(frame_b) → Body
A rotational damper stabilizes the revolute joint via its flange.

Translated from PlanarMechanicsTest.Sensors.CutForces (line 633).
"""
test component CutForceTest
  world = World() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 20, "y1": 20, "x2": 120, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  revolute = Revolute() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 20, "x2": 380, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  rod = FixedTranslation(r = [1, 0]) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 521, "y1": 20, "x2": 621, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  body = Body(m = 1, I = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 767, "y1": 20, "x2": 867, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  damper = RotationalComponents.Components.Damper(d = 0.1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 190, "x2": 380, "y2": 290, "rot": 270}
      },
      "tags": []
    }
  }
  fixed = RotationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 280, "y1": 340, "x2": 380, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in frame_a (between world and revolute)"
  cut_force_frame_a = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameA()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 150, "y1": 20, "x2": 250, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in world frame (between revolute and rod)"
  cut_force_world = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.World()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 395, "y1": 20, "x2": 495, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
  "CutForce resolved in frame_b (between rod and body)"
  cut_force_frame_b = CutForce(resolve_in_frame = MultibodyComponents.ResolveInFrame.FrameB()) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 641, "y1": 20, "x2": 741, "y2": 120, "rot": 0}
      },
      "tags": []
    }
  }
relations
  initial revolute.phi = 0
  initial revolute.w = 0
  # Main chain: World → CutForce(A) → Revolute → CutForce(W) → Rod → CutForce(B) → Body
  connect(world.frame_b, cut_force_frame_a.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_frame_a.frame_b, revolute.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(revolute.frame_b, cut_force_world.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_world.frame_b, rod.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(rod.frame_b, cut_force_frame_b.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(cut_force_frame_b.frame_b, body.frame_a) {"Dyad": {"edges": [{"S": 1, "M": [], "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(damper.spline_a, fixed.spline) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
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.CutForceTest()
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 >>