Skip to content
LIBRARY
Examples.Oscillator.md

Examples.Oscillator

Oscillator demonstrates the use of initial conditions.

Replicates the structure of Modelica.Mechanics.Translational.Examples.Oscillator.

A spring–mass system is a mechanical oscillator. The resonant frequency is ω_res = sqrt(c / m). For c=10000 N/m and m=1 kg, f_res = ω_res/(2π) ≈ 15.9155 Hz.

System 1 (top, undamped): A sinusoidal force at the resonant frequency drives mass1 through a spring to a wall. Without damping, the amplitude grows without bound (resonance).

System 2 (bottom, damped): Same as system 1 but with a damper in parallel with the spring. Damping bounds the amplitude.

Note: MSL uses a one-flange Force source. Here we use the two-flange Force with flange_b connected to a Fixed ground, which is equivalent to a grounded one-flange Force.

Usage

TranslationalComponents.Examples.Oscillator()

Behavior

julia
using TranslationalComponents #hide
using ModelingToolkit #hide
@named sys = TranslationalComponents.Examples.Oscillator() #hide
full_equations(sys) #hide
<< @example-block not executed in draft mode >>

Source

dyad
"""
Oscillator demonstrates the use of initial conditions.

Replicates the structure of Modelica.Mechanics.Translational.Examples.Oscillator.

A spring–mass system is a mechanical oscillator. The resonant frequency is
ω_res = sqrt(c / m). For c=10000 N/m and m=1 kg, f_res = ω_res/(2π) ≈ 15.9155 Hz.

**System 1 (top, undamped):** A sinusoidal force at the resonant frequency
drives mass1 through a spring to a wall. Without damping, the amplitude grows
without bound (resonance).

**System 2 (bottom, damped):** Same as system 1 but with a damper in parallel
with the spring. Damping bounds the amplitude.

Note: MSL uses a one-flange Force source. Here we use the two-flange Force
with flange_b connected to a Fixed ground, which is equivalent to a grounded
one-flange Force.
"""
example component Oscillator
  "Sinusoidal force signal at resonance frequency"
  sine1 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 15.9155) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 30, "x2": 180, "y2": 130, "rot": 0}
      },
      "tags": []
    }
  }
  "Force source for system 1"
  force1 = TranslationalComponents.Sources.Force() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 210, "y1": 210, "x2": 310, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Ground for force source 1"
  force1_ground = TranslationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 90, "y1": 440, "x2": 190, "y2": 340, "rot": 180}
      },
      "tags": []
    }
  }
  "Mass for system 1 (undamped)"
  mass1 = TranslationalComponents.Components.Mass(L = 1, m = 1, s = initial -0.5, v = initial 0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 400, "y1": 210, "x2": 500, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Spring for system 1"
  spring1 = TranslationalComponents.Components.Spring(s_rel0 = 1, c = 10000) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 640, "y1": 210, "x2": 740, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Wall for system 1"
  fixed1 = TranslationalComponents.Components.Fixed(s0 = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 830, "y1": 340, "x2": 930, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "Sinusoidal force signal at resonance frequency"
  sine2 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 15.9155) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 500, "x2": 180, "y2": 600, "rot": 0}
      },
      "tags": []
    }
  }
  "Force source for system 2"
  force2 = TranslationalComponents.Sources.Force() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 220, "y1": 620, "x2": 320, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Ground for force source 2"
  force2_ground = TranslationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 70, "y1": 770, "x2": 170, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
  "Mass for system 2 (damped)"
  mass2 = TranslationalComponents.Components.Mass(L = 1, m = 1, s = initial -0.5, v = initial 0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 400, "y1": 620, "x2": 500, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Spring for system 2"
  spring2 = TranslationalComponents.Components.Spring(s_rel0 = 1, c = 10000) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 620, "y1": 770, "x2": 720, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
  "Damper for system 2"
  damper1 = TranslationalComponents.Components.Damper(d = 10) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 610, "y1": 620, "x2": 710, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Wall for system 2"
  fixed2 = TranslationalComponents.Components.Fixed(s0 = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 830, "y1": 770, "x2": 930, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # System 1: force → mass → spring → fixed
  connect(sine1.y, force1.f) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 260, "y": 80}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(force1.flange_b, mass1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(force1.flange_a, force1_ground.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 122.5, "y": 260}, {"x": 122.5, "y": 340}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(mass1.flange_b, spring1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(spring1.flange_b, fixed1.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 880, "y": 260}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # System 2: force → mass → [spring ‖ damper] → fixed
  connect(sine2.y, force2.f) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 270, "y": 550}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(force2.flange_b, mass2.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(force2.flange_a, force2_ground.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 120, "y": 670}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(mass2.flange_b, spring2.flange_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 560, "y": 670}, {"x": 560, "y": 820}], "E": 2}
      ],
      "junctions": [{"x": 610, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(spring2.flange_b, fixed2.flange) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 750, "y": 820}], "E": -1},
        {"S": -1, "M": [{"x": 880, "y": 670}], "E": 2}
      ],
      "junctions": [{"x": 750, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(mass2.flange_b, damper1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(damper1.flange_b, fixed2.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 880, "y": 670}], "E": 2}],
      "renderStyle": "standard"
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://TranslationalComponents/Example.svg"},
    "tests": {
      "case1": {
        "stop": 1,
        "atol": {
          "damper1.s_rel": 0.001,
          "damper1.v_rel": 0.001,
          "mass1.s": 0.001,
          "mass1.v": 0.01
        },
        "expect": {
          "final": {
            "damper1.s_rel": 1.00086,
            "damper1.v_rel": 0.0502,
            "mass1.s": -0.50433,
            "mass1.v": -0.25276
          },
          "signals": ["damper1.s_rel", "damper1.v_rel", "mass1.s", "mass1.v"]
        }
      }
    }
  }
}
end
Flattened Source
dyad
"""
Oscillator demonstrates the use of initial conditions.

Replicates the structure of Modelica.Mechanics.Translational.Examples.Oscillator.

A spring–mass system is a mechanical oscillator. The resonant frequency is
ω_res = sqrt(c / m). For c=10000 N/m and m=1 kg, f_res = ω_res/(2π) ≈ 15.9155 Hz.

**System 1 (top, undamped):** A sinusoidal force at the resonant frequency
drives mass1 through a spring to a wall. Without damping, the amplitude grows
without bound (resonance).

**System 2 (bottom, damped):** Same as system 1 but with a damper in parallel
with the spring. Damping bounds the amplitude.

Note: MSL uses a one-flange Force source. Here we use the two-flange Force
with flange_b connected to a Fixed ground, which is equivalent to a grounded
one-flange Force.
"""
example component Oscillator
  "Sinusoidal force signal at resonance frequency"
  sine1 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 15.9155) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 30, "x2": 180, "y2": 130, "rot": 0}
      },
      "tags": []
    }
  }
  "Force source for system 1"
  force1 = TranslationalComponents.Sources.Force() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 210, "y1": 210, "x2": 310, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Ground for force source 1"
  force1_ground = TranslationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 90, "y1": 440, "x2": 190, "y2": 340, "rot": 180}
      },
      "tags": []
    }
  }
  "Mass for system 1 (undamped)"
  mass1 = TranslationalComponents.Components.Mass(L = 1, m = 1, s = initial -0.5, v = initial 0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 400, "y1": 210, "x2": 500, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Spring for system 1"
  spring1 = TranslationalComponents.Components.Spring(s_rel0 = 1, c = 10000) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 640, "y1": 210, "x2": 740, "y2": 310, "rot": 0}
      },
      "tags": []
    }
  }
  "Wall for system 1"
  fixed1 = TranslationalComponents.Components.Fixed(s0 = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 830, "y1": 340, "x2": 930, "y2": 440, "rot": 0}
      },
      "tags": []
    }
  }
  "Sinusoidal force signal at resonance frequency"
  sine2 = BlockComponents.Sources.Sine(amplitude = 1, frequency = 15.9155) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 500, "x2": 180, "y2": 600, "rot": 0}
      },
      "tags": []
    }
  }
  "Force source for system 2"
  force2 = TranslationalComponents.Sources.Force() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 220, "y1": 620, "x2": 320, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Ground for force source 2"
  force2_ground = TranslationalComponents.Components.Fixed() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 70, "y1": 770, "x2": 170, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
  "Mass for system 2 (damped)"
  mass2 = TranslationalComponents.Components.Mass(L = 1, m = 1, s = initial -0.5, v = initial 0) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 400, "y1": 620, "x2": 500, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Spring for system 2"
  spring2 = TranslationalComponents.Components.Spring(s_rel0 = 1, c = 10000) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 620, "y1": 770, "x2": 720, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
  "Damper for system 2"
  damper1 = TranslationalComponents.Components.Damper(d = 10) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 610, "y1": 620, "x2": 710, "y2": 720, "rot": 0}
      },
      "tags": []
    }
  }
  "Wall for system 2"
  fixed2 = TranslationalComponents.Components.Fixed(s0 = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 830, "y1": 770, "x2": 930, "y2": 870, "rot": 0}
      },
      "tags": []
    }
  }
relations
  # System 1: force → mass → spring → fixed
  connect(sine1.y, force1.f) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 260, "y": 80}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(force1.flange_b, mass1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(force1.flange_a, force1_ground.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 122.5, "y": 260}, {"x": 122.5, "y": 340}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(mass1.flange_b, spring1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(spring1.flange_b, fixed1.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 880, "y": 260}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  # System 2: force → mass → [spring ‖ damper] → fixed
  connect(sine2.y, force2.f) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 270, "y": 550}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(force2.flange_b, mass2.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(force2.flange_a, force2_ground.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 120, "y": 670}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(mass2.flange_b, spring2.flange_a) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [], "E": -1},
        {"S": -1, "M": [{"x": 560, "y": 670}, {"x": 560, "y": 820}], "E": 2}
      ],
      "junctions": [{"x": 610, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(spring2.flange_b, fixed2.flange) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 750, "y": 820}], "E": -1},
        {"S": -1, "M": [{"x": 880, "y": 670}], "E": 2}
      ],
      "junctions": [{"x": 750, "y": 670}],
      "renderStyle": "standard"
    }
  }
  connect(mass2.flange_b, damper1.flange_a) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}], "renderStyle": "standard"}}
  connect(damper1.flange_b, fixed2.flange) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 880, "y": 670}], "E": 2}],
      "renderStyle": "standard"
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://TranslationalComponents/Example.svg"},
    "tests": {
      "case1": {
        "stop": 1,
        "atol": {
          "damper1.s_rel": 0.001,
          "damper1.v_rel": 0.001,
          "mass1.s": 0.001,
          "mass1.v": 0.01
        },
        "expect": {
          "final": {
            "damper1.s_rel": 1.00086,
            "damper1.v_rel": 0.0502,
            "mass1.s": -0.50433,
            "mass1.v": -0.25276
          },
          "signals": ["damper1.s_rel", "damper1.v_rel", "mass1.s", "mass1.v"]
        }
      }
    }
  }
}
end


Test Cases

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

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

Test Case case1

julia
@named model_case1 = TranslationalComponents.Examples.Oscillator()
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 = 1e+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.damper1.s_rel])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TranslationalComponents.Examples.Oscillator_case1_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.damper1.s_rel], width=2, label="Actual value of damper1.s_rel")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of damper1.s_rel")
end
scatter!(plt, [df_case1.t[end]], [1.00086], label="Final Condition for `damper1.s_rel`")
<< @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.damper1.v_rel])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TranslationalComponents.Examples.Oscillator_case1_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.damper1.v_rel], width=2, label="Actual value of damper1.v_rel")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of damper1.v_rel")
end
scatter!(plt, [df_case1.t[end]], [0.0502], label="Final Condition for `damper1.v_rel`")
<< @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.mass1.s])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TranslationalComponents.Examples.Oscillator_case1_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.mass1.s], width=2, label="Actual value of mass1.s")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of mass1.s")
end
scatter!(plt, [df_case1.t[end]], [-0.50433], label="Final Condition for `mass1.s`")
<< @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.mass1.v])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TranslationalComponents.Examples.Oscillator_case1_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.mass1.v], width=2, label="Actual value of mass1.v")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of mass1.v")
end
scatter!(plt, [df_case1.t[end]], [-0.25276], label="Final Condition for `mass1.v`")
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
  • Examples

  • Experiments

  • Analyses