From c8d697c905167851eba3cd5499c7734b99e3f18f Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:47:00 +0530 Subject: [PATCH 01/80] units of analog --- src/Blocks/nonlinear.jl | 3 +-- src/Electrical/Analog/ideal_components.jl | 8 ++++---- src/Electrical/Analog/sensors.jl | 10 +++++----- src/Electrical/utils.jl | 12 ++++++------ 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Blocks/nonlinear.jl b/src/Blocks/nonlinear.jl index bab526a83..416166cc4 100644 --- a/src/Blocks/nonlinear.jl +++ b/src/Blocks/nonlinear.jl @@ -97,7 +97,6 @@ Initial value of state `Y` can be set with `int.y` rising = 1.0, [description = "Maximum rising slew rate of SlewRateLimiter"] falling = -rising, [description = "Derivative time constant of SlewRateLimiter"] Td = 0.001, [description = "Derivative time constant"] - y_start end begin getdefault(rising) ≥ getdefault(falling) || @@ -105,7 +104,7 @@ Initial value of state `Y` can be set with `int.y` getdefault(Td) > 0 || throw(ArgumentError("Time constant `Td` must be strictly positive")) end - @extend u, y = siso = SISO(y_start = y_start) + @extend u, y = siso = SISO(; y_start) @equations begin D(y) ~ max(min((u - y) / Td, rising), falling) end diff --git a/src/Electrical/Analog/ideal_components.jl b/src/Electrical/Analog/ideal_components.jl index 82a787e90..bc292b14f 100644 --- a/src/Electrical/Analog/ideal_components.jl +++ b/src/Electrical/Analog/ideal_components.jl @@ -38,7 +38,7 @@ See [OnePort](@ref) @mtkmodel Resistor begin @extend v, i = oneport = OnePort() @parameters begin - R, [description = "Resistance"] + R, [description = "Resistance", unit = u"Ω"] end @equations begin v ~ i * R @@ -66,7 +66,7 @@ See [OnePort](@ref) @mtkmodel Conductor begin @extend v, i = oneport = OnePort() @parameters begin - G, [description = "Conductance"] + G, [description = "Conductance", unit = u"S"] end @equations begin i ~ v * G @@ -94,7 +94,7 @@ See [OnePort](@ref) """ @mtkmodel Capacitor begin @parameters begin - C, [description = "Capacitance"] + C, [description = "Capacitance",unit = u"F"] end @variables begin v @@ -126,7 +126,7 @@ See [OnePort](@ref) """ @mtkmodel Inductor begin @parameters begin - L, [description = "Inductance"] + L, [description = "Inductance", unit = u"H"] end @variables begin i diff --git a/src/Electrical/Analog/sensors.jl b/src/Electrical/Analog/sensors.jl index 5c6e7f16d..231deeb60 100644 --- a/src/Electrical/Analog/sensors.jl +++ b/src/Electrical/Analog/sensors.jl @@ -46,7 +46,7 @@ Creates a circuit component which measures the potential at a pin. p = Pin() end @variables begin - phi(t) + phi(t), [description = "Measured potential", unit = u"V"] end @equations begin p.i ~ 0 @@ -74,7 +74,7 @@ Creates a circuit component that measures the voltage across it. Analogous to an n = Pin() end @variables begin - v(t) + v(t), [description = "Voltage difference from positive to negative pin", unit = u"V"] end @equations begin p.i ~ 0 @@ -112,7 +112,7 @@ consumed by a circuit. current_sensor = CurrentSensor() end @variables begin - power(t) + power(t), [description = "Power being consumed", unit = u"W"] end @equations begin connect(voltage_sensor.p, pv) @@ -150,8 +150,8 @@ Combines a [`VoltageSensor`](@ref) and a [`CurrentSensor`](@ref). current_sensor = CurrentSensor() end @variables begin - i(t) = 1.0 - v(t) = 1.0 + i(t) = 1.0, [description = "Current", unit = u"A"] + v(t) = 1.0, [description = "Voltage", unit = u"V"] end @equations begin connect(voltage_sensor.p, pv) diff --git a/src/Electrical/utils.jl b/src/Electrical/utils.jl index ba7d0f541..4c8ecd33a 100644 --- a/src/Electrical/utils.jl +++ b/src/Electrical/utils.jl @@ -33,8 +33,8 @@ Component with two electrical pins `p` and `n` and current `i` flows from `p` to n = Pin() end @variables begin - v(t) = 0.0 - i(t) = 0.0 + v(t) = 0.0, [description = "Voltage", unit = u"V"] + i(t) = 0.0, [description = "Current", unit = u"A"] end @equations begin v ~ p.v - n.v @@ -70,10 +70,10 @@ Current `i1` flows from `p1` to `n1` and `i2` from `p2` to `n2`. n2 = Pin() end @variables begin - v1(t) = 0.0 - i1(t) = 0.0 - v2(t) = 0.0 - i2(t) = 0.0 + v1(t) = 0.0, [description = "Voltage", unit = u"V"] + i1(t) = 0.0, [description = "Current", unit = u"A"] + v2(t) = 0.0, [description = "Voltage", unit = u"V"] + i2(t) = 0.0, [description = "Current", unit = u"A"] end @equations begin v1 ~ p1.v - n1.v From 2a1741e06aa24ef570d0503aba33d8f239944905 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:53:11 +0530 Subject: [PATCH 02/80] metadata(components): adds units to Magnetic/FluxTubes components --- src/Magnetic/FluxTubes/basic.jl | 24 +++++++++++++----------- src/Magnetic/FluxTubes/sources.jl | 8 ++++---- src/Magnetic/FluxTubes/utils.jl | 16 ++++++++++++---- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/Magnetic/FluxTubes/basic.jl b/src/Magnetic/FluxTubes/basic.jl index e19ab198c..661dd06b2 100644 --- a/src/Magnetic/FluxTubes/basic.jl +++ b/src/Magnetic/FluxTubes/basic.jl @@ -20,7 +20,7 @@ Idle running branch. @mtkmodel Idle begin @extend (Phi,) = two_port = TwoPort() @equations begin - Phi ~ 0 + Phi ~ 0.0 end end @@ -68,7 +68,7 @@ Constant permeance. @mtkmodel ConstantPermeance begin @extend V_m, Phi = two_port = TwoPort() @parameters begin - G_m = 1.0, [description = "Magnetic permeance"] + G_m = 1.0, [description = "Magnetic permeance", unit = u"H"] end @equations begin Phi ~ G_m * V_m @@ -87,7 +87,7 @@ Constant reluctance. @mtkmodel ConstantReluctance begin @extend V_m, Phi = two_port = TwoPort(; Phi = 0.0) @parameters begin - R_m = 1.0, [description = "Magnetic reluctance"] + R_m = 1.0, [description = "Magnetic reluctance", unit = u"H^-1"] end @equations begin V_m ~ Phi * R_m @@ -114,9 +114,10 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([Wb]) N, [description = "Number of turns"] end @variables begin - v(t) - i(t) - Phi + v(t), + [description = "Voltage difference from positive to negative pin", unit = u"V"] + i(t), [description = "Current", unit = u"A"] + Phi, [description = "Magnetic flux", unit = u"Wb"] end @extend V_m, Phi = two_port = TwoPort(; Phi = Phi) @components begin @@ -147,13 +148,14 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([`Wb`]) """ @mtkmodel EddyCurrent begin @variables begin - Phi + Phi, [description = "Magnetic flux", unit = u"Wb"] end @parameters begin - rho = 0.098e-6, [description = "Resistivity of flux tube material"] - l = 1, [description = "Average length of eddy current path"] - A = 1, [description = "Cross sectional area of eddy current path"] - R = rho * l / A # Electrical resistance of eddy current path + rho = 0.098e-6, [description = "Resistivity of flux tube material", unit = u"Ω*m"] + l = 1, [description = "Average length of eddy current path", unit = u"m"] + A = 1, [description = "Cross sectional area of eddy current path", unit = u"m^2"] + R = rho * l / A, + [description = "Electrical resistance of eddy current path", unit = u"Ω"] end @extend (V_m, Phi) = two_port = TwoPort(; Phi = Phi) @equations begin diff --git a/src/Magnetic/FluxTubes/sources.jl b/src/Magnetic/FluxTubes/sources.jl index d2c5f8dfe..9b68f4893 100644 --- a/src/Magnetic/FluxTubes/sources.jl +++ b/src/Magnetic/FluxTubes/sources.jl @@ -13,10 +13,10 @@ Parameters: port_n = NegativeMagneticPort() end @parameters begin - V_m = 0.0, [description = "Magnetic potential difference"] + V_m = 0.0, [description = "Magnetic potential difference", unit = u"A"] end @variables begin - Phi(t) + Phi(t), [description = "Magnetic flux", unit = u"Wb"] end @equations begin V_m ~ port_p.V_m - port_n.V_m @@ -40,10 +40,10 @@ Parameters: port_n = NegativeMagneticPort() end @parameters begin - Phi = 0.0, [description = "Magnetic flux"] + Phi = 0.0, [description = "Magnetic flux", unit = u"Wb"] end @variables begin - V_m(t) + V_m(t), [description = "Magnetic potential difference", unit = u"A"] end @equations begin V_m ~ port_p.V_m - port_n.V_m diff --git a/src/Magnetic/FluxTubes/utils.jl b/src/Magnetic/FluxTubes/utils.jl index f6154d81f..705900a89 100644 --- a/src/Magnetic/FluxTubes/utils.jl +++ b/src/Magnetic/FluxTubes/utils.jl @@ -1,6 +1,7 @@ @connector MagneticPort begin - V_m(t), [description = "Magnetic potential at the port"] - Phi(t), [connect = Flow, description = "Magnetic flux flowing into the port"] + V_m(t), [description = "Magnetic potential at the port", unit = u"A"] + Phi(t), + [connect = Flow, description = "Magnetic flux flowing into the port", unit = u"Wb"] end Base.@doc "Port for a Magnetic system." MagneticPort @@ -30,8 +31,15 @@ Partial component with magnetic potential difference between two magnetic ports port_n = NegativeMagneticPort() end @variables begin - V_m(t) = 0.0 - Phi(t) = 0.0 + V_m(t) = 0.0, [description = "Magnetic potential at the port", unit = u"A"] + function Phi(t) + 0.0, + [ + connect = Flow, + description = "Magnetic flux flowing into the port", + unit = u"Wb", + ] + end end @equations begin V_m ~ port_p.V_m - port_n.V_m From 38a4db7d8bd51cadbb142a85fcdc538cf233f53a Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:15:44 +0530 Subject: [PATCH 03/80] metadata(components): adds units and descriptions to Rotational components --- src/Mechanical/Rotational/components.jl | 43 ++++++++++++++----------- src/Mechanical/Rotational/sensors.jl | 4 +-- src/Mechanical/Rotational/sources.jl | 31 +++++++++++++----- src/Mechanical/Rotational/utils.jl | 43 ++++++++++++++++++------- 4 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index 6df5d3b1c..547505a38 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -16,7 +16,7 @@ Flange fixed in housing at a given angle. flange = Flange() end @parameters begin - phi0 = 0.0, [description = "Fixed offset angle of flange"] + phi0 = 0.0, [description = "Fixed offset angle of flange", unit = u"rad"] end @equations begin flange.phi ~ phi0 @@ -45,7 +45,7 @@ end """ @mtkmodel Inertia begin @parameters begin - J, [description = "Moment of inertia"] + J, [description = "Moment of inertia", unit = u"kg*m^2"] end @components begin flange_a = Flange() @@ -55,9 +55,9 @@ end @symcheck J > 0 || throw(ArgumentError("Expected `J` to be positive")) end @variables begin - phi(t) = 0.0, [description = "Absolute rotation angle"] - w(t) = 0.0, [description = "Absolute angular velocity"] - a(t) = 0.0, [description = "Absolute angular acceleration"] + phi(t) = 0.0, [description = "Absolute rotation angle", unit = u"rad"] + w(t) = 0.0, [description = "Absolute angular velocity", unit = u"rad*s^-1"] + a(t) = 0.0, [description = "Absolute angular acceleration", rad = u"rad*s^-2"] end @equations begin phi ~ flange_a.phi @@ -94,8 +94,8 @@ Linear 1D rotational spring @symcheck c > 0 || throw(ArgumentError("Expected `c` to be positive")) end @parameters begin - c, [description = "Spring constant"] - phi_rel0 = 0.0, [description = "Unstretched spring angle"] + c, [description = "Spring constant", unit = u"N*m*rad^-1"] + phi_rel0 = 0.0, [description = "Unstretched spring angle", unit = u"rad"] end @equations begin tau ~ c * (phi_rel - phi_rel0) @@ -129,7 +129,7 @@ Linear 1D rotational damper @symcheck d > 0 || throw(ArgumentError("Expected `d` to be positive")) end @parameters begin - d, [description = "Damping constant"] + d, [description = "Damping constant", unit = u"N*m*s*rad^-1"] end @equations begin tau ~ d * w_rel @@ -159,10 +159,10 @@ Linear 1D rotational spring and damper - `phi_rel0`: [`rad`] Unstretched spring angle """ @mtkmodel SpringDamper begin - @extend phi_rel, w_rel, tau = partial_comp = PartialCompliantWithRelativeStates() + @extend phi_rel, w_rel, a_rel, tau = partial_comp = PartialCompliantWithRelativeStates() @variables begin - tau_c(t), [description = "Spring torque"] - tau_d(t), [description = "Damper torque"] + tau_c(t), [description = "Spring torque", unit = u"N*m"] + tau_d(t), [description = "Damper torque", unit = u"N*m"] end @parameters begin d, [description = "Damping constant"] @@ -208,8 +208,14 @@ This element characterizes any type of gear box which is fixed in the ground and ratio, [description = "Transmission ratio"] end @variables begin - phi_a(t) = 0.0, [description = "Relative angle between shaft a and the support"] - phi_b(t) = 0.0, [description = "Relative angle between shaft b and the support"] + function phi_a(t) + 0.0, + [description = "Relative angle between shaft a and the support", unit = u"rad"] + end + function phi_b(t) + 0.0, + [description = "Relative angle between shaft b and the support", unit = u"rad"] + end end @equations begin phi_a ~ flange_a.phi - phi_support @@ -247,14 +253,13 @@ Friction model: "Armstrong, B. and C.C. de Wit, Friction Modeling and Compensati - `tau_brk`: [`N⋅m`] Breakaway friction torque """ @mtkmodel RotationalFriction begin - @extend w_rel, tau = partial_comp = PartialCompliantWithRelativeStates() + @extend phi_rel, w_rel, a_rel, tau = partial_comp = PartialCompliantWithRelativeStates() @parameters begin - f, [description = "Viscous friction coefficient"] - tau_c, [description = "Coulomb friction torque"] - w_brk, [description = "Breakaway friction velocity"] - tau_brk, [description = "Breakaway friction torque"] + f, [description = "Viscous friction coefficient", unit = u"N*m*s/rad"] + tau_c, [description = "Coulomb friction torque", unit = u"N*m"] + w_brk, [description = "Breakaway friction velocity", unit = u"rad*s^-1"] + tau_brk, [description = "Breakaway friction torque", unit = "N*m"] end - begin str_scale = sqrt(2 * exp(1)) * (tau_brk - tau_c) w_st = w_brk * sqrt(2) diff --git a/src/Mechanical/Rotational/sensors.jl b/src/Mechanical/Rotational/sensors.jl index d3be89feb..3f18b0cfd 100644 --- a/src/Mechanical/Rotational/sensors.jl +++ b/src/Mechanical/Rotational/sensors.jl @@ -72,7 +72,7 @@ Ideal sensor to measure the relative angular velocity - `flange_a`: [Flange](@ref) Flange of shaft from which sensor information shall be measured - `flange_b`: [Flange](@ref) Flange of shaft from which sensor information shall be measured - - `w`: [RealOutput](@ref) Absolute angular velocity of flange + - `w_rel`: [RealOutput](@ref) Relative angular velocity """ @mtkmodel RelSpeedSensor begin @components begin @@ -81,7 +81,7 @@ Ideal sensor to measure the relative angular velocity w_rel = RealOutput() end @variables begin - phi_rel(t) = 0.0 + phi_rel(t) = 0.0, [description = "Relative rotation angle", units = u"rad"] end @equations begin 0 ~ flange_a.tau + flange_b.tau diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 35abf2c52..005032d88 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -5,9 +5,8 @@ @extend flange, phi_support = partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) @variables begin phi(t), - [ - description = "Angle of flange with respect to support (= flange.phi - support.phi)", - ] + [description = "Angle of flange with respect to support", + unit = u"rad"] end @equations begin phi ~ flange.phi - phi_support @@ -68,14 +67,21 @@ Constant torque source tau_constant, [ description = "Constant torque (if negative, torque is acting as load in positive direction of rotation)", - ] + unit = u"N*m"] use_support end @extend flange, phi = partial_element = PartialTorque(; use_support = use_support) @variables begin - tau(t), [description = "Accelerating torque acting at flange (= -flange.tau)"] + tau(t), + [ + description = "Accelerating torque acting at flange (= -flange.tau)", + unit = u"N*m", + ] w(t), - [description = "Angular velocity of flange with respect to support (= der(phi))"] + [ + description = "Angular velocity of flange with respect to support", + unit = u"rad*s^-1", + ] end @equations begin w ~ D(phi) @@ -108,7 +114,13 @@ Forced movement of a flange according to a reference angular velocity signal @named partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) @unpack flange, phi_support = partial_element @named w_ref = RealInput() - @variables phi(t)=0.0 w(t)=0.0 a(t)=0.0 + @variables begin + function phi(t) + 0.0, [description = "Angle of flange with respect to support", unit = u"rad"] + end + w(t) = 0.0, [description = "Angular velocity", unit = u"rad*s^-1"] + a(t) = 0.0, [description = "Angular acceleration", unit = u"rad*s^-2"] + end eqs = [phi ~ flange.phi - phi_support D(phi) ~ w] if exact @@ -116,7 +128,10 @@ Forced movement of a flange according to a reference angular velocity signal push!(eqs, w ~ w_ref.u) push!(eqs, a ~ 0) else - pars = @parameters tau_filt = tau_filt + pars = @parameters tau_filt=tau_filt [ + description = "Time constant of low-pass filter", + unit = u"rad*s^-1", + ] push!(eqs, D(w) ~ a) push!(eqs, a ~ (w_ref.u - w) * tau_filt) end diff --git a/src/Mechanical/Rotational/utils.jl b/src/Mechanical/Rotational/utils.jl index b54bfd06d..cbb6f8671 100644 --- a/src/Mechanical/Rotational/utils.jl +++ b/src/Mechanical/Rotational/utils.jl @@ -1,6 +1,6 @@ @connector Flange begin - phi(t), [description = "Rotation angle of flange"] - tau(t), [connect = Flow, description = "Cut torque in flange"] + phi(t), [description = "Rotation angle of flange", unit = u"rad"] + tau(t), [connect = Flow, description = "Cut torque in flange", unit = u"N*m"] end Base.@doc """ @@ -14,8 +14,8 @@ Base.@doc """ """ Flange @connector Support begin - phi(t), [description = "Rotation angle of flange"] - tau(t), [connect = Flow, description = "Cut torque in flange"] + phi(t), [description = "Rotation angle of flange", unit = u"rad"] + tau(t), [connect = Flow, description = "Cut torque in flange", unit = u"N*m"] end # Base.@doc """ @@ -71,8 +71,10 @@ Partial model for the compliant connection of two rotational 1-dim. shaft flange flange_b = Flange() end @variables begin - phi_rel(t) = 0.0, [description = "Relative rotation angle between flanges"] - tau(t) = 0.0, [description = "Torque between flanges"] + function phi_rel(t) + 0.0, [description = "Relative rotation angle between flanges", units = u"rad"] + end + tau(t) = 0.0, [description = "Torque between flanges", u"N*m"] end @equations begin phi_rel ~ flange_b.phi - flange_a.phi @@ -104,10 +106,21 @@ Partial model for the compliant connection of two rotational 1-dim. shaft flange flange_b = Flange() end @variables begin - phi_rel(t) = 0.0, [description = "Relative rotation angle between flanges"] - w_rel(t) = 0.0, [description = "Relative angular velocity between flanges"] - a_rel(t) = 0.0, [description = "Relative angular acceleration between flanges"] - tau(t) = 0.0, [description = "Torque between flanges"] + function phi_rel(t) + 0.0, [description = "Relative rotation angle between flanges", unit = u"rad"] + end + function w_rel(t) + 0.0, + [description = "Relative angular velocity between flanges", unit = u"rad*s^-1"] + end + function a_rel(t) + 0.0, + [ + description = "Relative angular acceleration between flanges", + unit = u"rad*s^-2", + ] + end + tau(t) = 0.0, [description = "Torque between flanges", unit = u"N*m"] end @equations begin phi_rel ~ flange_b.phi - flange_a.phi @@ -138,7 +151,10 @@ Partial model for a component with one rotational 1-dim. shaft flange and a supp @component function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @named flange = Flange() sys = [flange] - @variables phi_support(t)=0.0 [description = "Absolute angle of support flange"] + @variables phi_support(t)=0.0 [ + description = "Absolute angle of support flange", + unit = u"rad", + ] if use_support @named support = Support() eqs = [support.phi ~ phi_support @@ -173,7 +189,10 @@ Partial model for a component with two rotational 1-dim. shaft flanges and a sup @named flange_a = Flange() @named flange_b = Flange() sys = [flange_a, flange_b] - @variables phi_support(t)=0.0 [description = "Absolute angle of support flange"] + @variables phi_support(t)=0.0 [ + description = "Absolute angle of support flange", + unit = u"rad", + ] if use_support @named support = Support() eqs = [support.phi ~ phi_support From 3122a2ea15631accf8acaea1844400c8ae58e134 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:13:32 +0530 Subject: [PATCH 04/80] metadata(components): adds units and descriptions to TranslationalPosition components --- .../TranslationalPosition/components.jl | 22 ++++++----- src/Mechanical/TranslationalPosition/utils.jl | 38 ++++++++----------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index e1901cb24..3cc366011 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -13,7 +13,7 @@ Flange fixed in housing at a given position. """ @mtkmodel Fixed begin @parameters begin - s_0 + s_0, [description = "Fixed offset position of housing", unit = u"m"] end @components begin flange = Flange(; s = s_0) @@ -31,13 +31,12 @@ Sliding mass with inertia # Parameters: - `m`: [kg] Mass of sliding mass - - `s_0`: [m] Initial value of absolute position of sliding mass - - `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass # States: - - `s`: [m] Absolute position of sliding mass - - `v`: [m/s] Absolute linear velocity of sliding mass (= der(s)) + - `s`: [m] Absolute position of sliding mass. It accepts an initial value, which defaults to 0.0. + - `v`: [m/s] Absolute linear velocity of sliding mass (= der(s)). It accepts an initial value, which defaults to 0.0. + - `f`: [N] Force. It accepts an initial value, which defaults to 0.0. # Connectors: @@ -45,12 +44,15 @@ Sliding mass with inertia """ @mtkmodel Mass begin @parameters begin - m + m, [description = "Mass of sliding mass", unit = u"kg"] end @variables begin - s(t) = 0.0 - v(t) = 0.0 - f(t) = 0.0 + s(t) = 0.0, [description = "Absolute position of sliding mass", unit = u"m"] + function v(t) + 0.0, + [description = "Absolute linear velocity of sliding mass", unit = u"m*s^-1"] + end + f(t) = 0.0, [description = "Force", unit = u"N"] end @components begin flange = Flange(; s = s) @@ -158,7 +160,7 @@ Linear 1D translational damper """ @mtkmodel Damper begin @parameters begin - d + d, [description = "Damping constant", unit = u"N.s/m"] end @variables begin va(t) = 0.0 diff --git a/src/Mechanical/TranslationalPosition/utils.jl b/src/Mechanical/TranslationalPosition/utils.jl index 4e3dd86d9..81c16431d 100644 --- a/src/Mechanical/TranslationalPosition/utils.jl +++ b/src/Mechanical/TranslationalPosition/utils.jl @@ -1,6 +1,6 @@ @connector Flange begin - s(t) - f(t), [connect = Flow] + s(t), [description = "Absolute position of flange", unit = u"m"] + f(t), [connect = Flow, description = " Cut force into the flange", unit = u"N"] end Base.@doc """ Flange(;name) @@ -13,8 +13,8 @@ Base.@doc """ """ Flange @connector Support begin - s(t) - f(t), [connect = Flow] + s(t), [description = "Absolute position of the support/housing", unit = u"m"] + f(t), [connect = Flow, description = "Cut force into the flange", unit = u"N"] end Base.@doc """ Support(;name) @@ -36,7 +36,7 @@ Partial model for the compliant connection of two translational 1-dim. flanges. - `s_rel`: [m] Relative distance (= flange_b.s - flange_a.s). It accepts an initial value, which defaults to 0.0. - `f`: [N] Force between flanges (= flange_b.f). It accepts an initial value, which defaults to 0.0. """ -@mtkmodel PartialCompliant begin#(; name, s_rel_start = 0.0, f_start = 0.0) +@mtkmodel PartialCompliant begin @components begin flange_a = Flange() flange_b = Flange() @@ -44,8 +44,8 @@ Partial model for the compliant connection of two translational 1-dim. flanges. @variables begin v_a(t) = 0.0 v_b(t) = 0.0 - s_rel(t) = 0.0 - f(t) = 0.0 + s_rel(t) = 0.0, [description = "Relative distance ", unit = u"m"] + f(t) = 0.0, [description = "Force between flanges", unit = u"N"] end @equations begin D(flange_a.s) ~ v_a @@ -61,18 +61,9 @@ end Partial model for the compliant connection of two translational 1-dim. flanges. -# Parameters: - - - `s_rel_start`: [m] Initial relative distance - - `v_rel_start`: [m/s] Initial relative linear velocity (= der(s_rel)) - - `a_rel_start`: [m/s²] Initial relative linear acceleration (= der(v_rel)) - - `f_start`: [N] Initial force between flanges + # States: -# States: - - - `s_rel`: [m] Relative distance (= flange_b.phi - flange_a.phi) - - `v_rel`: [m/s] Relative linear velocity (= der(s_rel)) - - `a_rel`: [m/s²] Relative linear acceleration (= der(v_rel)) + - `delta_s`: [m] - `f`: [N] Force between flanges (= flange_b.f) """ @mtkmodel PartialCompliantWithRelativeStates begin @@ -82,7 +73,7 @@ Partial model for the compliant connection of two translational 1-dim. flanges. end @variables begin delta_s(t) = 0.0 - f(t) = 0.0 + f(t) = 0.0, [description = "Force between flanges", unit = u"N"] end @equations begin delta_s ~ flange_a.s - flange_b.s @@ -107,7 +98,8 @@ Partial model for a component with one translational 1-dim. shaft flange and a s @component function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @named flange = Flange() sys = [flange] - @variables s_support(t) + @variables s_support(t), + [description = "Absolute position of support flange", unit = u"m"] if use_support @named support = Support() eqs = [support.s ~ s_support @@ -120,7 +112,7 @@ Partial model for a component with one translational 1-dim. shaft flange and a s end """ - PartialElementaryTwoFlangesAndSupport2(;name, use_support=false) + PartialElementaryTwoFlangesAndSupport2(; name, use_support = false) Partial model for a component with two translational 1-dim. flanges and a support used for textual modeling, i.e., for elementary models @@ -136,7 +128,9 @@ Partial model for a component with two translational 1-dim. flanges and a suppor @named flange_a = Flange() @named flange_b = Flange() sys = [flange_a, flange_b] - @variables s_support(t) = 0.0 + @variables function s_support(t) + 0.0, [description = "Absolute position of support flange", unit = u"m"] + end if use_support @named support = Support() eqs = [support.s ~ s_support From b4d2c9e68887cf97cc62e3c19216590843806265 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:07:31 +0530 Subject: [PATCH 05/80] f: magnetic --- src/Magnetic/Magnetic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnetic/Magnetic.jl b/src/Magnetic/Magnetic.jl index 4eefda950..825ecd081 100644 --- a/src/Magnetic/Magnetic.jl +++ b/src/Magnetic/Magnetic.jl @@ -1,6 +1,6 @@ module Magnetic -using ModelingToolkit +using ModelingToolkit, Unitful # FluxTubes include("FluxTubes/FluxTubes.jl") From 6af0dc6aef47c32e2d5ad311bb733862e74c3957 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:17:29 +0530 Subject: [PATCH 06/80] refactor: add units and descriptions to Blocks - `RealInput` and `RealOutput` now accept `unit`. - All function generators now have `ouptput__unit` to set the unit of output --- src/Blocks/Blocks.jl | 3 +- src/Blocks/math.jl | 14 +++--- src/Blocks/sources.jl | 46 ++++++++++--------- src/Blocks/utils.jl | 70 +++++++++++++++++++++-------- test/Blocks/continuous.jl | 3 +- test/Blocks/math.jl | 3 +- test/Blocks/nonlinear.jl | 3 +- test/Blocks/sources.jl | 11 +++-- test/Blocks/test_analysis_points.jl | 4 +- 9 files changed, 102 insertions(+), 55 deletions(-) diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index dbf067e3f..c391e3743 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -6,8 +6,9 @@ using ModelingToolkit, Symbolics import IfElse: ifelse import ..@symcheck using ModelingToolkit: getdefault +using ..DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export RealInput, RealOutput, SISO diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index 86045ec52..91280a91b 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -38,19 +38,19 @@ Output the product of a gain matrix with the input signal vector. - `output` """ @mtkmodel MatrixGain begin - @parameters begin - K, [description = "Matrix gain"] + @structural_parameters begin + K end begin - nout = size(getdefault(K), 1) - nin = size(getdefault(K), 2) + nout = size(K, 1) + nin = size(K, 2) end @components begin - input = RealInput(; nin = size(K, 2)) - output = RealOutput(; nout = size(K, 1)) + input = RealInput(; nin = nin) + output = RealOutput(; nout = nout) end @equations begin - [(@info i, j; output.u[i] ~ sum(getdefault(K)[i, j] * input.u[j])) for j in 1:nin + [(output.u[i] ~ sum(K[i, j] * input.u[j])) for j in 1:nin for i in 1:nout]... end end diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index 0b7b278a1..4a476d4a7 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -76,7 +76,7 @@ Generate constant signal. """ @mtkmodel Constant begin @components begin - output = RealOutput() + output = RealOutput(; unit) end @parameters begin k = 0.0, [description = "Constant output value of block"] @@ -101,7 +101,7 @@ The input variable `t` can be changed by passing a different variable as the key f end @components begin - output = RealOutput() + output = RealOutput(; unit) end @equations begin output.u ~ first(getdefault(f))(t) @@ -135,8 +135,9 @@ Generate sine signal. phase = 0, offset = 0, start_time = 0, - smooth = false) - @named output = RealOutput() + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase equation = if smooth == false offset + ifelse(t < start_time, 0, @@ -178,8 +179,9 @@ Cosine signal. phase = 0, offset = 0, start_time = 0, - smooth = false) - @named output = RealOutput() + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase equation = if smooth == false offset + ifelse(t < start_time, zero(t), @@ -209,8 +211,8 @@ Generate current time signal. - `output` """ -@component function ContinuousClock(; name, offset = 0, start_time = 0) - @named output = RealOutput() +@component function ContinuousClock(; name, offset = 0, start_time = 0, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time eqs = [ output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), @@ -242,8 +244,9 @@ Generate ramp signal. duration = 1, offset = 0, start_time = 0, - smooth = false) - @named output = RealOutput() + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time height=height duration=duration equation = if smooth == false offset + ifelse(t < start_time, 0, @@ -280,8 +283,9 @@ Generate smooth square signal. - `output` """ @component function Square(; name, frequency = 1.0, amplitude = 1.0, - offset = 0.0, start_time = 0.0, smooth = false) - @named output = RealOutput() + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters begin frequency = frequency amplitude = amplitude @@ -322,8 +326,8 @@ Generate step signal. - `output` """ @component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, - smooth = 1e-5) - @named output = RealOutput() + smooth = 1e-5, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) duration_numeric = duration pars = @parameters offset=offset start_time=start_time height=height duration=duration equation = if smooth == false # use comparison in case smooth is a float @@ -372,8 +376,9 @@ Exponentially damped sine signal. phase = 0, offset = 0, start_time = 0, - smooth = false) - @named output = RealOutput() + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping equation = if smooth == false @@ -413,8 +418,9 @@ Generate smooth triangular signal for frequencies less than or equal to 25 Hz - `output` """ @component function Triangular(; name, amplitude = 1.0, frequency = 1.0, - offset = 0.0, start_time = 0.0, smooth = false) - @named output = RealOutput() + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) pars = @parameters begin amplitude = amplitude frequency = frequency @@ -604,13 +610,13 @@ data input component. # Connectors: - `output` """ -@component function SampledData(; name, buffer) +@component function SampledData(; name, buffer, unit = nothing) pars = @parameters begin buffer = buffer end vars = [] systems = @named begin - output = RealOutput() + output = RealOutput(; unit) end eqs = [ output.u ~ get_sampled_data(t, buffer), diff --git a/src/Blocks/utils.jl b/src/Blocks/utils.jl index 59db3655b..9254db35a 100644 --- a/src/Blocks/utils.jl +++ b/src/Blocks/utils.jl @@ -1,14 +1,30 @@ -@connector function RealInput(; name, nin = 1, u_start = nin > 1 ? zeros(nin) : 0.0) +@connector function RealInput(; name, nin = 1, u_start = nin > 1 ? zeros(nin) : 0.0, unit = nothing) if nin == 1 - @variables u(t)=u_start [ - input = true, - description = "Inner variable in RealInput $name", - ] + if unit !== nothing + @variables u(t)=u_start [ + input = true, + description = "Inner variable in RealInput $name", + unit = unit + ] + else + @variables u(t)=u_start [ + input = true, + description = "Inner variable in RealInput $name", + ] + end else - @variables u(t)[1:nin]=u_start [ - input = true, - description = "Inner variable in RealInput $name", - ] + if unit !== nothing + @variables u(t)[1:nin]=u_start [ + input = true, + description = "Inner variable in RealInput $name", + unit = unit + ] + else + @variables u(t)[1:nin]=u_start [ + input = true, + description = "Inner variable in RealInput $name", + ] + end u = collect(u) end ODESystem(Equation[], t, [u...], []; name = name) @@ -26,18 +42,34 @@ Connector with one input signal of type Real. - `u`: Value of the connector; if nin=1 this is a scalar """ RealInput -@connector function RealOutput(; name, nout = 1, u_start = nout > 1 ? zeros(nout) : 0.0) +@connector function RealOutput(; name, nout = 1, u_start = nout > 1 ? zeros(nout) : 0.0, unit = nothing) if nout == 1 - @variables u(t)=u_start [ - output = true, - description = "Inner variable in RealOutput $name", - ] + if unit !== nothing + @variables u(t)=u_start [ + output = true, + description = "Inner variable in RealOutput $name", + unit = unit + ] + else + @variables u(t)=u_start [ + output = true, + description = "Inner variable in RealOutput $name", + ] + end else - @variables u(t)[1:nout]=u_start [ - output = true, - description = "Inner variable in RealOutput $name", - ] - u = collect(u) + if unit !== nothing + @variables u(t)[1:nout]=u_start [ + output = true, + description = "Inner variable in RealOutput $name", + unit = unit + ] + else + @variables u(t)[1:nout]=u_start [ + output = true, + description = "Inner variable in RealOutput $name", + ] + end + u = collect(u) end ODESystem(Equation[], t, [u...], []; name = name) end diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index bdd4739c8..8b778c518 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -1,8 +1,9 @@ using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] #= Testing strategy: diff --git a/test/Blocks/math.jl b/test/Blocks/math.jl index 1ad2317fc..e6945696e 100644 --- a/test/Blocks/math.jl +++ b/test/Blocks/math.jl @@ -3,8 +3,9 @@ using ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks: _clamp, _dead_zone using ModelingToolkit: inputs, unbound_inputs, bound_inputs using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] @testset "Gain" begin @named c = Constant(; k = 1) diff --git a/test/Blocks/nonlinear.jl b/test/Blocks/nonlinear.jl index e13a45047..ca14763ab 100644 --- a/test/Blocks/nonlinear.jl +++ b/test/Blocks/nonlinear.jl @@ -2,8 +2,9 @@ using ModelingToolkit, OrdinaryDiffEq using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Blocks: _clamp, _dead_zone using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] @testset "Limiter" begin @testset "Constant" begin diff --git a/test/Blocks/sources.jl b/test/Blocks/sources.jl index b2df09461..4bd864ba0 100644 --- a/test/Blocks/sources.jl +++ b/test/Blocks/sources.jl @@ -4,8 +4,9 @@ using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damp smooth_square, smooth_step, smooth_ramp, smooth_triangular, triangular, square using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) @testset "Constant" begin @@ -408,14 +409,15 @@ end amplitude, damping, phase, offset, start_time) atol=1e-3 end - +#### TODO +#= @testset "SampledData" begin using DataInterpolations dt = 4e-4 t_end = 10.0 - time = 0:dt:t_end - x = @. time^2 + 1.0 + time_span = 0:dt:t_end + x = @. time_span^2 + 1.0 vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 @named src = SampledData(Float64) @@ -441,3 +443,4 @@ end @test sol[dy][end]≈2 * time[end] atol=1e-3 @test sol[ddy][end]≈2 atol=1e-3 end +=# \ No newline at end of file diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index 39cc222ef..cdbd33e32 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -4,6 +4,7 @@ using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq using ModelingToolkit: get_eqs, vars, @set!, get_iv using ControlSystemsBase +using DynamicQuantities: @u_str @named P = FirstOrder(k = 1, T = 1) @named C = Gain(; k = -1) @@ -231,10 +232,11 @@ Si = ss(matrices...) @test tf(So) ≈ tf(Si) ## A simple multi-level system with loop openings -@parameters t +@parameters t [unit = u"s"] @named P_inner = FirstOrder(k = 1, T = 1) @named feedback = Feedback() @named ref = Step() +### TODO @named sys_inner = ODESystem([connect(P_inner.output, :y, feedback.input2) connect(feedback.output, :u, P_inner.input) connect(ref.output, :r, feedback.input1)], t, From 12160bf652dabe11083377a1ff0dd55786785853 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:18:16 +0530 Subject: [PATCH 07/80] refactor: update Electrical components with units and description --- src/Electrical/Analog/ideal_components.jl | 30 +++------ src/Electrical/Analog/sensors.jl | 2 +- src/Electrical/Analog/sources.jl | 4 +- src/Electrical/Electrical.jl | 7 +- src/Electrical/utils.jl | 6 +- test/Electrical/analog.jl | 80 +++++++++++++---------- 6 files changed, 66 insertions(+), 63 deletions(-) diff --git a/src/Electrical/Analog/ideal_components.jl b/src/Electrical/Analog/ideal_components.jl index bc292b14f..68d446339 100644 --- a/src/Electrical/Analog/ideal_components.jl +++ b/src/Electrical/Analog/ideal_components.jl @@ -93,13 +93,10 @@ See [OnePort](@ref) - `C`: [`F`] Capacitance """ @mtkmodel Capacitor begin + @extend v, i = oneport = OnePort(; v = 0.0) @parameters begin C, [description = "Capacitance",unit = u"F"] end - @variables begin - v - end - @extend v, i = oneport = OnePort(; v = v) @equations begin D(v) ~ i / C end @@ -125,13 +122,10 @@ See [OnePort](@ref) - `L`: [`H`] Inductance """ @mtkmodel Inductor begin + @extend v, i = oneport = OnePort(; i = 0.0) @parameters begin L, [description = "Inductance", unit = u"H"] end - @variables begin - i - end - @extend v, i = oneport = OnePort(; i = i) @equations begin D(i) ~ 1 / L * v end @@ -211,9 +205,9 @@ Temperature dependent electrical resistor heat_port = HeatPort() end @parameters begin - R_ref = 1.0, [description = "Reference resistance"] - T_ref = 300.15, [description = "Reference temperature"] - alpha = 0, [description = "Temperature coefficient of resistance"] + R_ref = 1.0, [description = "Reference resistance", unit = u"Ω"] + T_ref = 300.15, [description = "Reference temperature", unit = u"K"] + alpha = 0, [description = "Temperature coefficient of resistance", unit = u"1/K"] end @variables begin R(t) = R_ref @@ -248,25 +242,19 @@ Electromotoric force (electric/mechanic transformer) - `k`: [`N⋅m/A`] Transformation coefficient """ @mtkmodel EMF begin + @extend OnePort() @components begin - p = Pin() - n = Pin() flange = Flange() support = Support() end @parameters begin - k, [description = "Transformation coefficient"] + k, [description = "Transformation coefficient", unit = u"N*m/A"] end @variables begin - v(t) = 0.0 - i(t) = 0.0 - phi(t) = 0.0 - w(t) = 0.0 + phi(t) = 0.0, [description = "Rotation angle", unit = u"rad"] + w(t) = 0.0, [description = "Angular velocity", unit = u"rad/s"] end @equations begin - v ~ p.v - n.v - 0 ~ p.i + n.i - i ~ p.i phi ~ flange.phi - support.phi D(phi) ~ w k * w ~ v diff --git a/src/Electrical/Analog/sensors.jl b/src/Electrical/Analog/sensors.jl index 231deeb60..94af63487 100644 --- a/src/Electrical/Analog/sensors.jl +++ b/src/Electrical/Analog/sensors.jl @@ -19,7 +19,7 @@ an ideal ammeter. n = Pin() end @variables begin - i(t) + i(t), [description = "Measured current", unit = u"A"] end @equations begin p.v ~ n.v diff --git a/src/Electrical/Analog/sources.jl b/src/Electrical/Analog/sources.jl index 807fa91f1..4d3f20b3e 100644 --- a/src/Electrical/Analog/sources.jl +++ b/src/Electrical/Analog/sources.jl @@ -16,7 +16,7 @@ See [OnePort](@ref) @mtkmodel Voltage begin @extend v, i = oneport = OnePort() @components begin - V = RealInput() + V = RealInput(unit = u"V") end @equations begin v ~ V.u @@ -41,7 +41,7 @@ See [OnePort](@ref) @mtkmodel Current begin @extend v, i = oneport = OnePort() @components begin - I = RealInput() + I = RealInput(unit = u"A") end @equations begin i ~ I.u diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index 687f92b20..720febb03 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -8,8 +8,13 @@ using ModelingToolkit, Symbolics, IfElse using ..Thermal: HeatPort using ..Mechanical.Rotational: Flange, Support using ..Blocks: RealInput, RealOutput +using ..DynamicQuantities +using ..DynamicQuantities: @u_str -@parameters t +import ..rad +import ..S + +@parameters t [unit = u"s"] D = Differential(t) export Pin, OnePort diff --git a/src/Electrical/utils.jl b/src/Electrical/utils.jl index 4c8ecd33a..32151812a 100644 --- a/src/Electrical/utils.jl +++ b/src/Electrical/utils.jl @@ -1,6 +1,6 @@ @connector Pin begin - v(t) # Potential at the pin [V] - i(t), [connect = Flow] # Current flowing into the pin [A] + v(t), [description = "Voltage", unit = u"V"] # Potential at the pin [V] + i(t), [description = "Current", unit = u"A", connect = Flow] # Current flowing into the pin [A] end @doc """ Pin(; name) @@ -34,7 +34,7 @@ Component with two electrical pins `p` and `n` and current `i` flows from `p` to end @variables begin v(t) = 0.0, [description = "Voltage", unit = u"V"] - i(t) = 0.0, [description = "Current", unit = u"A"] + i(t) = 0.0, [description = "Current", unit = u"A", connect = Flow] end @equations begin v ~ p.v - n.v diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index 35fce60d5..46e427806 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -4,13 +4,15 @@ using ModelingToolkitStandardLibrary.Blocks: Step, Square, Triangular using ModelingToolkitStandardLibrary.Blocks: square, triangular using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -# using Plots +@parameters t [unit = u"s"] +D = Differential(t) -@parameters t +# using Plots @testset "sensors" begin - @named source = Sine(offset = 1, amplitude = 10, frequency = 5) + @named source = Sine(offset = 1, amplitude = 10, frequency = 5, output__unit = u"V") @named voltage = Voltage() @named resistor = Resistor(R = 1) @named capacitor = Capacitor(C = 1, v = 0.0) @@ -43,7 +45,7 @@ using OrdinaryDiffEq: ReturnCode.Success power_sensor, ]) sys = structural_simplify(model) - prob = ODAEProblem(sys, [], (0.0, 10.0)) + prob = ODEProblem(sys, [], (0.0, 10.0)) sol = solve(prob, Tsit5()) # Plots.plot(sol; vars=[capacitor.v, voltage_sensor.v]) @@ -57,7 +59,7 @@ end # simple voltage divider @testset "voltage divider with a short branch" begin - @named source = Constant(k = 10) + @named source = Constant(k = 10, output.unit = u"V") @named voltage = Voltage() @named R0 = Resistor(R = 1e3) @named R1 = Resistor(R = 1e3) @@ -86,7 +88,7 @@ end # simple RC @testset "RC" begin - @named source = Constant(k = 10) + @named source = Constant(k = 10, output.unit = u"V") @named voltage = Voltage() @named resistor = Resistor(R = 1) @named capacitor = Capacitor(C = 1, v = 0.0) @@ -100,7 +102,7 @@ end @named model = ODESystem(connections, t; systems = [resistor, capacitor, source, voltage, ground]) sys = structural_simplify(model) - prob = ODAEProblem(sys, Pair[], (0.0, 10.0)) + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob, Tsit5()) # Plots.plot(sol; vars=[source.v, capacitor.v]) @@ -110,7 +112,7 @@ end # simple RL @testset "RL" begin - @named source = Constant(k = 10) + @named source = Constant(k = 10, output.unit = u"V") @named voltage = Voltage() @named resistor = Resistor(R = 1) @named inductor = Inductor(L = 1.0, i = 0.0) @@ -124,7 +126,7 @@ end @named model = ODESystem(connections, t; systems = [resistor, inductor, source, voltage, ground]) sys = structural_simplify(model) - prob = ODAEProblem(sys, Pair[], (0.0, 10.0)) + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob, Tsit5()) # Plots.plot(sol; vars=[inductor.i, inductor.i]) @@ -135,15 +137,15 @@ end @testset "RC with voltage sources" begin R, C = 1, 1 @named voltage = Voltage() - @named source_const = Constant(k = 10) + @named source_const = Constant(k = 10, output.unit = u"V") @named source_sin = Sine(offset = 1, amplitude = 10, frequency = 2, start_time = 0.5, - phase = 0) - @named source_step = Step(offset = 1, height = 10, start_time = 0.5) + phase = 0, output__unit = u"V") + @named source_step = Step(offset = 1, height = 10, start_time = 0.5, output__unit = u"V") @named source_tri = Triangular(offset = 1, start_time = 0.5, amplitude = 10, - frequency = 2) + frequency = 2, output__unit = u"V") @named source_dsin = ExpSine(offset = 1, amplitude = 10, frequency = 2, - start_time = 0.5, phase = 0, damping = 0.5) - @named source_ramp = Ramp(offset = 1, height = 10, start_time = 0.5, duration = 1) + start_time = 0.5, phase = 0, damping = 0.5, output__unit = u"V") + @named source_ramp = Ramp(offset = 1, height = 10, start_time = 0.5, duration = 1, output__unit = u"V") sources = [source_const, source_sin, source_step, source_tri, source_dsin, source_ramp] @named resistor = Resistor(; R) @@ -159,7 +161,7 @@ end @named model = ODESystem(connections, t; systems = [resistor, capacitor, source, ground, voltage]) sys = structural_simplify(model) - prob = ODAEProblem(sys, Pair[], (0.0, 10.0)) + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob, Tsit5()) @test sol.retcode == Success sol = solve(prob, Rodas4()) @@ -173,7 +175,7 @@ end @testset "RC with current sources" begin start_time = 2 @named current = Current() - @named source = Step(start_time = 2) + @named source = Step(start_time = 2, output__unit = u"A") @named resistor = Resistor(R = 1) @named capacitor = Capacitor(C = 1, v = 0.0) @named ground = Ground() @@ -186,7 +188,7 @@ end @named model = ODESystem(connections, t; systems = [ground, resistor, current, capacitor, source]) sys = structural_simplify(model) - prob = ODAEProblem(sys, Pair[], (0.0, 10.0)) + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob, Tsit5()) y(x, st) = (x .> st) .* abs.(collect(x) .- st) @test sol.retcode == Success @@ -202,7 +204,7 @@ end @named R2 = Resistor(R = 100 * R) @named C1 = Capacitor(C = 1 / (2 * pi * f * R), v = 0.0) @named opamp = IdealOpAmp() - @named square_source = Square(amplitude = Vin) + @named square_source = Square(amplitude = Vin, output__unit = u"V") @named voltage = Voltage() @named sensor = VoltageSensor() @@ -251,15 +253,19 @@ _damped_sine_wave(x, f, A, st, ϕ, d) = exp((st - x) * d) * A * sin(2 * π * f * @named ground = Ground() @named voltage = Voltage() @named voltage_sensor = VoltageSensor() - @named step = Step(start_time = st, offset = o, height = h) + @named step = Step(start_time = st, offset = o, height = h, output__unit = u"V") @named cosine = Cosine(offset = o, amplitude = A, frequency = f, start_time = st, - phase = ϕ) - @named sine = Sine(offset = o, amplitude = A, frequency = f, start_time = st, phase = ϕ) + phase = ϕ, output__unit = u"V") + @named sine = Sine(offset = o, amplitude = A, frequency = f, start_time = st, + phase = ϕ, output__unit = u"V") @named damped_sine = ExpSine(offset = o, amplitude = A, frequency = f, start_time = st, - phase = ϕ, damping = d) - @named ramp = Ramp(offset = o, start_time = st, duration = et - st, height = h) - @named vsquare = Square(offset = o, start_time = st, amplitude = A, frequency = f) - @named tri = Triangular(offset = o, start_time = st, amplitude = A, frequency = f) + phase = ϕ, damping = d, output__unit = u"V") + @named ramp = Ramp(offset = o, start_time = st, duration = et - st, height = h, + output__unit = u"V") + @named vsquare = Square(offset = o, start_time = st, amplitude = A, frequency = f, + output__unit = u"V") + @named tri = Triangular(offset = o, start_time = st, amplitude = A, frequency = f, + output__unit = u"V") # @named vsawtooth = SawTooth(amplitude=A, start_time=st, frequency=f, offset=o) sources = [step, cosine, sine, damped_sine, ramp, tri, vsquare] #, vsawtooth] @@ -294,7 +300,7 @@ _damped_sine_wave(x, f, A, st, ϕ, d) = exp((st - x) * d) * A * sin(2 * π * f * u0 = [cap.v => 0.0] - prob = ODAEProblem(vsys, u0, (0, 10.0)) + prob = ODEProblem(vsys, u0, (0, 10.0)) sol = solve(prob, dt = 0.1, Tsit5()) @test sol.retcode == Success @@ -314,15 +320,19 @@ end @named cap = Capacitor(C = 1, v = 0.0) @named current_sensor = CurrentSensor() @named current = Current() - @named step = Step(start_time = st, offset = o, height = h) + @named step = Step(start_time = st, offset = o, height = h, output__unit = u"A") @named cosine = Cosine(offset = o, amplitude = A, frequency = f, start_time = st, - phase = ϕ) - @named sine = Sine(offset = o, amplitude = A, frequency = f, start_time = st, phase = ϕ) + phase = ϕ, output__unit = u"A") + @named sine = Sine(offset = o, amplitude = A, frequency = f, start_time = st, + phase = ϕ, output__unit = u"A") @named damped_sine = ExpSine(offset = o, amplitude = A, frequency = f, start_time = st, - phase = ϕ, damping = d) - @named ramp = Ramp(offset = o, start_time = st, duration = et - st, height = h) - @named vsquare = Square(offset = o, start_time = st, amplitude = A, frequency = f) - @named tri = Triangular(offset = o, start_time = st, amplitude = A, frequency = f) + phase = ϕ, damping = d, output__unit = u"A") + @named ramp = Ramp(offset = o, start_time = st, duration = et - st, height = h, + output__unit = u"A") + @named vsquare = Square(offset = o, start_time = st, amplitude = A, frequency = f, + output__unit = u"A") + @named tri = Triangular(offset = o, start_time = st, amplitude = A, frequency = f, + output__unit = u"A") # @named isawtooth = SawTooth(amplitude=A, start_time=st, frequency=f, offset=o) sources = [step, cosine, sine, damped_sine, ramp, tri, vsquare] #, idamped_sine] @@ -358,7 +368,7 @@ end u0 = [cap.v => 0.0] - prob = ODAEProblem(isys, u0, (0, 10.0)) + prob = ODEProblem(isys, u0, (0, 10.0)) sol = solve(prob, dt = 0.1, Tsit5()) @test sol.retcode == Success From ea1ca36f71a61c4d4bd2202070f77ccd35c1a3c1 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:18:44 +0530 Subject: [PATCH 08/80] refactor: update Rotational components with units and description --- src/Mechanical/Rotational/Rotational.jl | 6 ++++- src/Mechanical/Rotational/components.jl | 14 +++------- src/Mechanical/Rotational/sensors.jl | 10 +++---- src/Mechanical/Rotational/sources.jl | 35 ++++++++----------------- src/Mechanical/Rotational/utils.jl | 20 ++++---------- test/Mechanical/rotational.jl | 2 +- 6 files changed, 31 insertions(+), 56 deletions(-) diff --git a/src/Mechanical/Rotational/Rotational.jl b/src/Mechanical/Rotational/Rotational.jl index 9a6fda31f..4e0546263 100644 --- a/src/Mechanical/Rotational/Rotational.jl +++ b/src/Mechanical/Rotational/Rotational.jl @@ -6,8 +6,11 @@ module Rotational using ModelingToolkit, Symbolics, IfElse using ...Blocks: RealInput, RealOutput import ...@symcheck +using ...DynamicQuantities: @u_str +import ...Wb +import ...rad -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export Flange, Support @@ -22,4 +25,5 @@ include("sources.jl") export AngleSensor, SpeedSensor, TorqueSensor, RelSpeedSensor include("sensors.jl") + end diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index 547505a38..b667e257f 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -57,7 +57,7 @@ end @variables begin phi(t) = 0.0, [description = "Absolute rotation angle", unit = u"rad"] w(t) = 0.0, [description = "Absolute angular velocity", unit = u"rad*s^-1"] - a(t) = 0.0, [description = "Absolute angular acceleration", rad = u"rad*s^-2"] + a(t) = 0.0, [description = "Absolute angular acceleration", unit = u"rad*s^-2"] end @equations begin phi ~ flange_a.phi @@ -94,7 +94,7 @@ Linear 1D rotational spring @symcheck c > 0 || throw(ArgumentError("Expected `c` to be positive")) end @parameters begin - c, [description = "Spring constant", unit = u"N*m*rad^-1"] + c, [description = "Spring constant", unit = u"N*m"] phi_rel0 = 0.0, [description = "Unstretched spring angle", unit = u"rad"] end @equations begin @@ -208,14 +208,8 @@ This element characterizes any type of gear box which is fixed in the ground and ratio, [description = "Transmission ratio"] end @variables begin - function phi_a(t) - 0.0, - [description = "Relative angle between shaft a and the support", unit = u"rad"] - end - function phi_b(t) - 0.0, - [description = "Relative angle between shaft b and the support", unit = u"rad"] - end + phi_a(t) = 0.0, [description = "Relative angle between shaft a and the support", unit = u"rad"] + phi_b(t) = 0.0, [description = "Relative angle between shaft b and the support", unit = u"rad"] end @equations begin phi_a ~ flange_a.phi - phi_support diff --git a/src/Mechanical/Rotational/sensors.jl b/src/Mechanical/Rotational/sensors.jl index 3f18b0cfd..b90c5271c 100644 --- a/src/Mechanical/Rotational/sensors.jl +++ b/src/Mechanical/Rotational/sensors.jl @@ -11,7 +11,7 @@ Ideal sensor to measure the absolute flange angle @mtkmodel AngleSensor begin @components begin flange = Flange() - phi = RealOutput() + phi = RealOutput(unit = u"rad") end @equations begin phi.u ~ flange.phi @@ -32,7 +32,7 @@ Ideal sensor to measure the absolute flange angular velocity @mtkmodel SpeedSensor begin @components begin flange = Flange() - w = RealOutput() + w = RealOutput(unit = u"rad/s") end @equations begin D(flange.phi) ~ w.u @@ -55,7 +55,7 @@ Ideal sensor to measure the torque between two flanges (`= flange_a.tau`) @components begin flange_a = Flange() flange_b = Flange() - tau = RealOutput() + tau = RealOutput(unit = u"N*m") end @equations begin flange_a.phi ~ flange_b.phi @@ -78,10 +78,10 @@ Ideal sensor to measure the relative angular velocity @components begin flange_a = Flange() flange_b = Flange() - w_rel = RealOutput() + w_rel = RealOutput(unit = u"rad/s") end @variables begin - phi_rel(t) = 0.0, [description = "Relative rotation angle", units = u"rad"] + phi_rel(t) = 0.0, [description = "Relative rotation angle", unit = u"rad"] end @equations begin 0 ~ flange_a.tau + flange_b.tau diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 005032d88..8ac70636a 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -32,12 +32,9 @@ Input signal acting as external torque on a flange - `use_support` """ @mtkmodel Torque begin - @parameters begin - use_support - end - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) @components begin - tau = RealInput() + tau = RealInput(unit = u"N*m") end @equations begin flange.tau ~ -tau.u @@ -62,7 +59,7 @@ Constant torque source - `tau_constant`: The constant torque applied by the source - `use_support`: Whether or not an internal support flange is added. """ -@mtkmodel ConstantTorque begin #(; name, tau_constant, use_support = false) +@mtkmodel ConstantTorque begin @parameters begin tau_constant, [ @@ -72,16 +69,10 @@ Constant torque source end @extend flange, phi = partial_element = PartialTorque(; use_support = use_support) @variables begin - tau(t), - [ - description = "Accelerating torque acting at flange (= -flange.tau)", - unit = u"N*m", - ] - w(t), - [ - description = "Angular velocity of flange with respect to support", - unit = u"rad*s^-1", - ] + tau(t), [description = "Accelerating torque acting at flange (= -flange.tau)", + unit = u"N*m"] + w(t), [description = "Angular velocity of flange with respect to support", + unit = u"rad*s^-1"] end @equations begin w ~ D(phi) @@ -114,13 +105,9 @@ Forced movement of a flange according to a reference angular velocity signal @named partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) @unpack flange, phi_support = partial_element @named w_ref = RealInput() - @variables begin - function phi(t) - 0.0, [description = "Angle of flange with respect to support", unit = u"rad"] - end - w(t) = 0.0, [description = "Angular velocity", unit = u"rad*s^-1"] - a(t) = 0.0, [description = "Angular acceleration", unit = u"rad*s^-2"] - end + @variables phi(t) = 0.0 [description = "Angle of flange with respect to support", unit = u"rad"] + @variables w(t) = 0.0 [description = "Angular velocity", unit = u"rad*s^-1"] + @variables a(t) = 0.0 [description = "Angular acceleration", unit = u"rad*s^-2"] eqs = [phi ~ flange.phi - phi_support D(phi) ~ w] if exact @@ -135,6 +122,6 @@ Forced movement of a flange according to a reference angular velocity signal push!(eqs, D(w) ~ a) push!(eqs, a ~ (w_ref.u - w) * tau_filt) end - return extend(ODESystem(eqs, t, [phi, w, a], pars; name = name, systems = [w_ref]), + return extend(ODESystem(eqs, t, vars, pars; name = name, systems = [w_ref]), partial_element) end diff --git a/src/Mechanical/Rotational/utils.jl b/src/Mechanical/Rotational/utils.jl index cbb6f8671..1d6b23fd0 100644 --- a/src/Mechanical/Rotational/utils.jl +++ b/src/Mechanical/Rotational/utils.jl @@ -71,10 +71,8 @@ Partial model for the compliant connection of two rotational 1-dim. shaft flange flange_b = Flange() end @variables begin - function phi_rel(t) - 0.0, [description = "Relative rotation angle between flanges", units = u"rad"] - end - tau(t) = 0.0, [description = "Torque between flanges", u"N*m"] + phi_rel(t)= 0.0, [description = "Relative rotation angle between flanges", unit = u"rad"] + tau(t) = 0.0, [description = "Torque between flanges", unit = u"N*m"] end @equations begin phi_rel ~ flange_b.phi - flange_a.phi @@ -106,20 +104,12 @@ Partial model for the compliant connection of two rotational 1-dim. shaft flange flange_b = Flange() end @variables begin - function phi_rel(t) - 0.0, [description = "Relative rotation angle between flanges", unit = u"rad"] - end - function w_rel(t) - 0.0, - [description = "Relative angular velocity between flanges", unit = u"rad*s^-1"] - end - function a_rel(t) - 0.0, - [ + phi_rel(t) = 0.0, [description = "Relative rotation angle between flanges", unit = u"rad"] + w_rel(t) = 0.0, [description = "Relative angular velocity between flanges", unit = u"rad*s^-1"] + a_rel(t) = 0.0, [ description = "Relative angular acceleration between flanges", unit = u"rad*s^-2", ] - end tau(t) = 0.0, [description = "Torque between flanges", unit = u"N*m"] end @equations begin diff --git a/test/Mechanical/rotational.jl b/test/Mechanical/rotational.jl index 79bbbf3ae..6198d68d8 100644 --- a/test/Mechanical/rotational.jl +++ b/test/Mechanical/rotational.jl @@ -6,7 +6,7 @@ using OrdinaryDiffEq: ReturnCode.Success # using Plots -@parameters t +@parameters t [unit = u"s"] D = Differential(t) @testset "two inertias" begin From a5f4ea48de240477d8e83903bb04ad993391a5ed Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:19:03 +0530 Subject: [PATCH 09/80] refactor: update Translational components with units and description --- src/Mechanical/Translational/Translational.jl | 3 +- src/Mechanical/Translational/components.jl | 45 ++++++-------- src/Mechanical/Translational/sensors.jl | 6 +- src/Mechanical/Translational/sources.jl | 61 ++++++++++--------- src/Mechanical/Translational/utils.jl | 4 +- 5 files changed, 59 insertions(+), 60 deletions(-) diff --git a/src/Mechanical/Translational/Translational.jl b/src/Mechanical/Translational/Translational.jl index d20961a43..2560b002e 100644 --- a/src/Mechanical/Translational/Translational.jl +++ b/src/Mechanical/Translational/Translational.jl @@ -8,8 +8,9 @@ using ModelingToolkit: getdefault using ModelingToolkitStandardLibrary.Blocks: RealInput, RealOutput using IfElse: ifelse +using ...DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export MechanicalPort diff --git a/src/Mechanical/Translational/components.jl b/src/Mechanical/Translational/components.jl index 959c2aa5e..299b44786 100644 --- a/src/Mechanical/Translational/components.jl +++ b/src/Mechanical/Translational/components.jl @@ -12,7 +12,7 @@ Use to close a system that has un-connected `MechanicalPort`'s where the force s flange = MechanicalPort() end @variables begin - f(t) = 0.0 + f(t) = 0.0, [description = "Force", unit = u"N"] end @equations begin flange.f ~ f @@ -60,21 +60,20 @@ Sliding mass with inertia """ @component function Mass(; name, v = 0.0, m, s = nothing, g = nothing) pars = @parameters begin - m = m + m = m, [description = "Mass of sliding body", unit = u"kg"] end @named flange = MechanicalPort(; v = v) - vars = @variables begin - v(t) = v - f(t) = 0 - end + @variables v(t) = v [description = "Absolute linear velocity of sliding mass", unit = u"m/s"] + @variables f(t) = 0 [description = "Force", unit = u"N"] + vars = [v, f] eqs = [flange.v ~ v flange.f ~ f] # gravity option if g !== nothing - @parameters g = g + @parameters g = g [description = "Gravitational force", unit = u"N"] push!(pars, g) push!(eqs, D(v) ~ f / m + g) else @@ -83,7 +82,7 @@ Sliding mass with inertia # position option if s !== nothing - @variables s(t) = s + @variables s(t) = s [description = "Absolute position of sliding mass", unit = u"m"] push!(vars, s) push!(eqs, D(s) ~ v) @@ -103,7 +102,7 @@ Linear 1D translational spring # Parameters: - `k`: [N/m] Spring constant - - `delta_s`: initial spring stretch + - `delta_s`: [m] initial spring stretch - `va`: [m/s] Initial value of absolute linear velocity at flange_a (default 0 m/s) - `v_b_0`: [m/s] Initial value of absolute linear velocity at flange_b (default 0 m/s) @@ -119,12 +118,10 @@ end # default @component function Spring(::Val{:relative}; name, k, delta_s = 0.0, flange_a__v = 0.0, flange_b__v = 0.0) pars = @parameters begin - k = k - end - vars = @variables begin - delta_s(t) = delta_s - f(t) = 0 + k = k, [description = "Spring constant", unit = u"N/m"] end + @variables delta_s(t) = delta_s [description = "Initial spring stretch", unit = u"m"] + @variables f(t) = 0 [description = "Force", unit = u"N"] @named flange_a = MechanicalPort(; v = flange_a__v) @named flange_b = MechanicalPort(; v = flange_b__v) @@ -133,7 +130,7 @@ end # default f ~ k * delta_s flange_a.f ~ +f flange_b.f ~ -f] - return compose(ODESystem(eqs, t, vars, pars; name = name), + return compose(ODESystem(eqs, t, [delta_s, f], pars; name = name), flange_a, flange_b) #flange_a.f => +k*delta_s, flange_b.f => -k*delta_s end @@ -142,14 +139,12 @@ const ABS = Val(:absolute) @component function Spring(::Val{:absolute}; name, k, sa = 0, sb = 0, flange_a__v = 0.0, flange_b__v = 0.0, l = 0) pars = @parameters begin - k = k + k = k, [description = "Spring constant", unit = u"N/m"] l = l end - vars = @variables begin - sa(t) = sa - sb(t) = sb - f(t) = 0 - end + @variables sa(t) = sa [unit = u"m"] + @variables sb(t) = sb [unit = u"m"] + @variables f(t) = 0 [description = "Force", unit = u"N"] @named flange_a = MechanicalPort(; v = flange_a__v) @named flange_b = MechanicalPort(; v = flange_b__v) @@ -159,7 +154,7 @@ const ABS = Val(:absolute) f ~ k * (sa - sb - l) #delta_s flange_a.f ~ +f flange_b.f ~ -f] - return compose(ODESystem(eqs, t, vars, pars; name = name), + return compose(ODESystem(eqs, t, [sa, sb, f], pars; name = name), flange_a, flange_b) #, flange_a.f => k * (flange_a__s - flange_b__s - l) end @@ -180,11 +175,11 @@ Linear 1D translational damper """ @mtkmodel Damper begin @parameters begin - d + d, [description = "Damping constant", unit = u"N*s/m"] end @variables begin - v(t) - f(t) = 0.0 + v(t), [description = "Velocity", unit = u"m/s"] + f(t) = 0.0, [description = "Force", unit = u"N"] end @components begin diff --git a/src/Mechanical/Translational/sensors.jl b/src/Mechanical/Translational/sensors.jl index ec778ee58..1dab8adf5 100644 --- a/src/Mechanical/Translational/sensors.jl +++ b/src/Mechanical/Translational/sensors.jl @@ -11,7 +11,7 @@ Linear 1D force input sensor. @mtkmodel ForceSensor begin @components begin flange = MechanicalPort() - output = RealOutput() + output = RealOutput(unit = u"N") end @equations begin @@ -36,11 +36,11 @@ Linear 1D position input sensor. @mtkmodel PositionSensor begin @components begin flange = MechanicalPort() - output = RealOutput() + output = RealOutput(unit = u"m") end @variables begin - s(t) = 0.0 + s(t) = 0.0, [description = "Absolute position", unit = u"m"] end @equations begin diff --git a/src/Mechanical/Translational/sources.jl b/src/Mechanical/Translational/sources.jl index 71d4f62f4..07a4fd21c 100644 --- a/src/Mechanical/Translational/sources.jl +++ b/src/Mechanical/Translational/sources.jl @@ -11,7 +11,7 @@ Linear 1D force input source @mtkmodel Force begin @components begin flange = MechanicalPort(; v = 0.0) - f = RealInput() + f = RealInput(unit = u"N") end @equations begin @@ -22,35 +22,33 @@ end """ Position(solves_force = true; name) -Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the position is given, the respective force needed is already provided elsewhere in the model). +Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the position is given, the respective force needed is already provided elsewhere in the model). # Connectors: - `flange`: 1-dim. translational flange - `s`: real input """ -@component function Position(solves_force = true; name) - vars = [] +@mtkmodel Position begin + @structural_parameters begin + solves_force = true + end - systems = @named begin + @components begin flange = MechanicalPort(; v = 0) - s = RealInput() + s = RealInput(unit = u"m") end - eqs = [ - D(s.u) ~ flange.v, - ] - - !solves_force && push!(eqs, 0 ~ flange.f) - - ODESystem(eqs, t, vars, []; - name, systems) + @equations begin + D(s.u) ~ flange.v + 0 ~ flange.f + end end """ Velocity(solves_force = true; name) -Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the velocity is given, the respective force needed is already provided elsewhere in the model). +Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the velocity is given, the respective force needed is already provided elsewhere in the model). # Connectors: @@ -60,7 +58,7 @@ Linear 1D position input source. Set `solves_force=false` to force input force @component function Velocity(solves_force = true; name) systems = @named begin flange = MechanicalPort(; v = 0) - v = RealInput() + v = RealInput(unit = u"m/s") end eqs = [ @@ -75,25 +73,30 @@ end """ Acceleration(solves_force = true; name) -Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the acceleration is given, the respective force needed is already provided elsewhere in the model). +Linear 1D position input source. Set `solves_force=false` to force input force to 0 (i.e. only the acceleration is given, the respective force needed is already provided elsewhere in the model). # Connectors: - `flange`: 1-dim. translational flange - `a`: real input """ -@component function Acceleration(solves_force = true; name) - systems = @named begin + +@mtkmodel Acceleration begin#(solves_force = true; name) + @structural_parameters begin + solves_force = true + end + @variables begin + v(t) = 0, [unit = u"m/s"] + end + @components begin flange = MechanicalPort(; v = 0) - a = RealInput() + a = RealInput(unit = u"m/s^2") + end + @equations begin + v ~ flange.v + D(v) ~ a.u + if solves_force + 0 ~ flange.f + end end - - vars = @variables v(t) = 0 - - eqs = [v ~ flange.v - D(v) ~ a.u] - - !solves_force && push!(eqs, 0 ~ flange.f) - - ODESystem(eqs, t, vars, []; name, systems) end diff --git a/src/Mechanical/Translational/utils.jl b/src/Mechanical/Translational/utils.jl index 7184d6002..085aa3687 100644 --- a/src/Mechanical/Translational/utils.jl +++ b/src/Mechanical/Translational/utils.jl @@ -1,6 +1,6 @@ @connector MechanicalPort begin - v(t) = 0.0 - f(t) = 0.0, [connect = Flow] + v(t) = 0.0, [description = "Velocity of the node", unit = u"m/s"] + f(t) = 0.0, [connect = Flow, description = "Force entering the node", unit = u"N"] end Base.@doc """ MechanicalPort(;name) From d6f48ecb509febe6d4b682e5686269bffcadf5d5 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:19:20 +0530 Subject: [PATCH 10/80] refactor: update TranslationalModelica components with units and description --- .../TranslationalModelica.jl | 3 +- .../TranslationalModelica/components.jl | 19 ++++++----- .../TranslationalModelica/sources.jl | 7 ++-- src/Mechanical/TranslationalModelica/utils.jl | 32 ++++++++++--------- test/Mechanical/translational_modelica.jl | 25 ++++++++------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl index 2aec491b1..f2cf06b5a 100644 --- a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl +++ b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl @@ -5,8 +5,9 @@ module TranslationalModelica using ModelingToolkit, Symbolics, IfElse using ...Blocks: RealInput, RealOutput +using ...DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export Flange diff --git a/src/Mechanical/TranslationalModelica/components.jl b/src/Mechanical/TranslationalModelica/components.jl index ad8c92b82..f1ea11817 100644 --- a/src/Mechanical/TranslationalModelica/components.jl +++ b/src/Mechanical/TranslationalModelica/components.jl @@ -13,7 +13,7 @@ Flange fixed in housing at a given position. """ @mtkmodel Fixed begin @parameters begin - s0 + s0 = 0.0, [description = "Absolute position of sliding mass", unit = u"m"] end @components begin @@ -44,15 +44,14 @@ Sliding mass with inertia - `flange: 1-dim. translational flange of mass` """ @mtkmodel Mass begin + @extend flange_a, flange_b, s = pr = PartialRigid(; L = 0.0, s = 0.0) @parameters begin - m = 0.0, [description = "Mass of sliding mass [kg]"] + m = 0.0, [description = "Mass of sliding mass", unit = u"kg"] end @variables begin - s - v(t) = 0.0, [description = "Absolute linear velocity of sliding mass [m/s]"] - a(t) = 0.0, [description = "Absolute linear acceleration of sliding mass [m/s^2]"] + v(t) = 0.0, [description = "Absolute linear velocity of sliding mass", unit = u"m/s"] + a(t) = 0.0, [description = "Absolute linear acceleration of sliding mass", unit = u"m/s^2"] end - @extend flange_a, flange_b, s = pr = PartialRigid(; L = 0.0, s = s) @equations begin v ~ D(s) a ~ D(v) @@ -78,8 +77,8 @@ Linear 1D translational spring @mtkmodel Spring begin @extend flange_a, flange_b, s_rel, f = pc = PartialCompliant() @parameters begin - c = 0.0, [description = "Spring constant [N/m]"] - s_rel0 = 0.0, [description = "Unstretched spring length [m]"] + c = 0.0, [description = "Spring constant", unit = u"N/m"] + s_rel0 = 0.0, [description = "Unstretched spring length", unit = u"m"] end @equations begin @@ -104,10 +103,10 @@ Linear 1D translational damper @mtkmodel Damper begin @extend flange_a, flange_b, v_rel, f = pc = PartialCompliantWithRelativeStates() @parameters begin - d = 0.0, [description = "Damping constant [Ns/m]"] + d = 0.0, [description = "Damping constant", unit = u"N*s/m"] end @variables begin - lossPower(t) = 0.0, [description = "Power dissipated by the damper [W]"] + lossPower(t) = 0.0, [description = "Power dissipated by the damper", unit = u"W"] end @equations begin f ~ d * v_rel diff --git a/src/Mechanical/TranslationalModelica/sources.jl b/src/Mechanical/TranslationalModelica/sources.jl index 0110ed2d2..04c5a49dd 100644 --- a/src/Mechanical/TranslationalModelica/sources.jl +++ b/src/Mechanical/TranslationalModelica/sources.jl @@ -4,12 +4,9 @@ Input signal acting as external force on a flange """ @mtkmodel Force begin - @parameters begin - use_support - end - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) @components begin - f = RealInput() # Accelerating force acting at flange (= -flange.tau) + f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end @equations begin flange.f ~ -f.u diff --git a/src/Mechanical/TranslationalModelica/utils.jl b/src/Mechanical/TranslationalModelica/utils.jl index a12016087..c445b8b81 100644 --- a/src/Mechanical/TranslationalModelica/utils.jl +++ b/src/Mechanical/TranslationalModelica/utils.jl @@ -1,6 +1,6 @@ @connector Flange begin - s(t) - f(t), [connect = Flow] + s(t), [description = "Absolute position of sliding mass", unit = u"m"] + f(t), [description = "Cut force into the flange", unit = u"N", connect = Flow] end Base.@doc """ Flange(;name) @@ -13,8 +13,8 @@ Base.@doc """ """ Flange @connector Support begin - s(t) - f(t), [connect = Flow] + s(t), [description = "Absolute position of sliding mass", unit = u"m"] + f(t), [description = "Cut force into the flange", unit = u"N", connect = Flow] end Base.@doc """ Support(;name) @@ -46,8 +46,8 @@ Partial model for the compliant connection of two translational 1-dim. flanges. @mtkmodel PartialCompliant begin @extend (flange_a, flange_b) = pt = PartialTwoFlanges() @variables begin - s_rel(t) = 0.0, [description = "Relative distance between flanges"] - f(t) = 0.0, [description = "Force between flanges"] + s_rel(t) = 0.0, [description = "Relative distance between flanges", unit = u"m"] + f(t) = 0.0, [description = "Force between flanges", unit = u"N"] end @equations begin @@ -71,9 +71,9 @@ Partial model for the compliant connection of two translational 1-dim. flanges. @mtkmodel PartialCompliantWithRelativeStates begin @extend flange_a, flange_b = pt = PartialTwoFlanges() @variables begin - s_rel(t) = 0.0, [description = "Relative distance between flanges"] - v_rel(t) = 0.0, [description = "Relative linear velocity))"] - f(t) = 0.0, [description = "Forces between flanges"] + s_rel(t) = 0.0, [description = "Relative distance between flanges", unit = u"m"] + v_rel(t) = 0.0, [description = "Relative linear velocity", unit = u"m/s"] + f(t) = 0.0, [description = "Forces between flanges", unit = u"N"] end @equations begin @@ -99,9 +99,11 @@ Partial model for a component with one translational 1-dim. shaft flange and a s """ function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @named flange = Flange() - @variables s_support(t) [description = "Absolute position of support flange"] + @variables s_support(t) [description = "Absolute position of support flange", unit = u"m"] @variables s(t) [ description = "Distance between flange and support (= flange.s - support.s)", + unit = u"m" + ] eqs = [s ~ flange.s - s_support] if use_support @@ -130,9 +132,9 @@ Partial model for a component with two translational 1-dim. flanges and a suppor function PartialElementaryTwoFlangesAndSupport2(; name, use_support = false) @named flange = Flange() - @variables s_a(t) [description = "Distance between left flange and support"] - @variables s_b(t) [description = "Distance between right flange and support"] - @variables s_support(t) [description = "Absolute position of support flange"] + @variables s_a(t) [description = "Distance between left flange and support", unit = u"m"] + @variables s_b(t) [description = "Distance between right flange and support", unit = u"m"] + @variables s_support(t) [description = "Absolute position of support flange", unit = u"m"] eqs = [s_a ~ flange_a.s - s_support s_b ~ flange_b.s - s_support] @@ -149,10 +151,10 @@ end @mtkmodel PartialRigid begin @extend flange_a, flange_b = ptf = PartialTwoFlanges() @variables begin - s(t) = 0.0, [description = "Absolute position of center of component"] + s(t) = 0.0, [description = "Absolute position of center of component", unit = u"m"] end @parameters begin - L = 0.0, [description = "Length of component, from left flange to right flange"] + L = 0.0, [description = "Length of component, from left flange to right flange", unit = u"m"] end @equations begin flange_a.s ~ s - L / 2 diff --git a/test/Mechanical/translational_modelica.jl b/test/Mechanical/translational_modelica.jl index 015b36572..8cd466890 100644 --- a/test/Mechanical/translational_modelica.jl +++ b/test/Mechanical/translational_modelica.jl @@ -1,16 +1,17 @@ using ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks -import ModelingToolkitStandardLibrary.Mechanical.TranslationalModelica as TP +import ModelingToolkitStandardLibrary.Mechanical.TranslationalModelica as TM +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) @testset "spring damper mass fixed" begin - @named damper = TP.Damper(; d = 1) - @named spring = TP.Spring(; c = 1, s_rel0 = 1) - @named mass = TP.Mass(; m = 1, v = 1) - @named fixed = TP.Fixed(s0 = 1) + @named damper = TM.Damper(; d = 1) + @named spring = TM.Spring(; c = 1, s_rel0 = 1) + @named mass = TM.Mass(; m = 1, v = 1) + @named fixed = TM.Fixed(s0 = 1) eqs = [connect(spring.flange_a, mass.flange_a, damper.flange_a) connect(spring.flange_b, damper.flange_b, fixed.flange)] @@ -29,12 +30,12 @@ D = Differential(t) end @testset "driven spring damper mass" begin - @named damper = TP.Damper(; d = 1) - @named spring = TP.Spring(; c = 1, s_rel0 = 1) - @named mass = TP.Mass(; m = 1, v = 1) - @named fixed = TP.Fixed(; s0 = 1) - @named force = TP.Force(use_support = false) - @named source = Sine(frequency = 3, amplitude = 2) + @named damper = TM.Damper(; d = 1) + @named spring = TM.Spring(; c = 1, s_rel0 = 1) + @named mass = TM.Mass(; m = 1, v = 1) + @named fixed = TM.Fixed(; s0 = 1) + @named force = TM.Force(use_support = false) + @named source = Sine(frequency = 3, amplitude = 2, output__unit = u"N") eqs = [connect(force.f, source.output) connect(force.flange, mass.flange_a) From d27f3f9605a86a77eaacb35f18340637a14c6169 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:19:38 +0530 Subject: [PATCH 11/80] refactor: update Thermal components with units and description --- src/Thermal/HeatTransfer/ideal_components.jl | 16 ++++++++-------- src/Thermal/HeatTransfer/sensors.jl | 6 +++--- src/Thermal/HeatTransfer/sources.jl | 16 ++++++++-------- src/Thermal/Thermal.jl | 5 +++-- src/Thermal/utils.jl | 12 ++++++------ test/Thermal/demo.jl | 3 ++- test/Thermal/thermal.jl | 7 ++++--- 7 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/Thermal/HeatTransfer/ideal_components.jl b/src/Thermal/HeatTransfer/ideal_components.jl index f4e0e19c0..027e1e045 100644 --- a/src/Thermal/HeatTransfer/ideal_components.jl +++ b/src/Thermal/HeatTransfer/ideal_components.jl @@ -21,11 +21,11 @@ Lumped thermal element storing heat port = HeatPort() end @parameters begin - C, [description = "Heat capacity of element"] + C, [description = "Heat capacity of element", unit = u"J/K"] end @variables begin - T(t) = 273.15 + 20 - der_T(t) = 0.0 + T(t) = 273.15 + 20, [description = "Temperature of element", unit = u"K"] + der_T(t) = 0.0, [description = "Time derivative of temperature", unit = u"K/s"] end @equations begin @@ -56,7 +56,7 @@ see [`Element1D`](@ref) @mtkmodel ThermalConductor begin @extend Q_flow, dT = element1d = Element1D() @parameters begin - G + G, [description = "Constant thermal conductance of material", unit = u"W/K"] end @equations begin Q_flow ~ G * dT @@ -85,7 +85,7 @@ Lumped thermal element transporting heat without storing it. @mtkmodel ThermalResistor begin @extend Q_flow, dT = element1d = Element1D() @parameters begin - R + R, [description = "Constant thermal resistance of material", unit = u"K/W"] end @equations begin dT ~ R * Q_flow @@ -114,7 +114,7 @@ Lumped thermal element for heat convection. @mtkmodel ConvectiveConductor begin @extend Q_flow, dT = convective_element1d = ConvectiveElement1D() @parameters begin - G + G, [description = "Convective thermal conductance", unit = u"W/K"] end @equations begin Q_flow ~ G * dT @@ -143,7 +143,7 @@ Lumped thermal element for heat convection. @mtkmodel ConvectiveResistor begin @extend Q_flow, dT = convective_element1d = ConvectiveElement1D() @parameters begin - R + R, [description = "Constant thermal resistance of material", unit = u"K/W"] end @equations begin dT ~ R * Q_flow @@ -176,7 +176,7 @@ Lumped thermal element for radiation heat transfer. @extend Q_flow, dT, port_a, port_b = element1d = Element1D() @parameters begin - G + G, [description = "Net radiation conductance between two surfaces", unit = "m^2"] end @equations begin Q_flow ~ G * sigma * (port_a.T^4 - port_b.T^4) diff --git a/src/Thermal/HeatTransfer/sensors.jl b/src/Thermal/HeatTransfer/sensors.jl index 2282af693..3df90b753 100644 --- a/src/Thermal/HeatTransfer/sensors.jl +++ b/src/Thermal/HeatTransfer/sensors.jl @@ -20,7 +20,7 @@ lags are associated with this sensor model. port = HeatPort() end @variables begin - T(t) + T(t), [description = "Absolute temperature", unit = u"K"] end @equations begin T ~ port.T @@ -51,7 +51,7 @@ output signal in kelvin. port_b = HeatPort() end @variables begin - T(t) + T(t), [description = "Relative temperature", unit = u"K"] end @equations begin T ~ port_a.T - port_b.T @@ -85,7 +85,7 @@ The output signal is positive, if the heat flows from `port_a` to `port_b`. port_b = HeatPort() end @variables begin - Q_flow(t) + Q_flow(t), [connect = Flow, description = "Heat flow rate", unit = u"W"] end @equations begin port_a.T ~ port_b.T diff --git a/src/Thermal/HeatTransfer/sources.jl b/src/Thermal/HeatTransfer/sources.jl index 1ee8d36f7..74863af4e 100644 --- a/src/Thermal/HeatTransfer/sources.jl +++ b/src/Thermal/HeatTransfer/sources.jl @@ -19,9 +19,9 @@ the component FixedHeatFlow is connected, if parameter `Q_flow` is positive. """ @mtkmodel FixedHeatFlow begin @parameters begin - Q_flow = 1.0, [description = "Fixed heat flow rate at port"] - T_ref = 293.15, [description = "Reference temperature"] - alpha = 0.0, [description = "Temperature coefficient of heat flow rate"] + Q_flow = 1.0, [description = "Fixed heat flow rate at port", unit = u"W"] + T_ref = 293.15, [description = "Reference temperature", unit = u"K"] + alpha = 0.0, [description = "Temperature coefficient of heat flow rate", unit = u"1/K"] end @components begin port = HeatPort() @@ -52,7 +52,7 @@ This model defines a fixed temperature `T` at its port in kelvin, i.e., it defin port = HeatPort() end @parameters begin - T, [description = "Fixed temperature boundary condition"] + T, [description = "Fixed temperature boundary condition", unit = u"K"] end @equations begin port.T ~ T @@ -82,12 +82,12 @@ dependent losses (which are given a reference temperature T_ref). """ @mtkmodel PrescribedHeatFlow begin @parameters begin - T_ref = 293.15, [description = "Reference temperature"] - alpha = 0.0, [description = "Temperature coefficient of heat flow rate"] + T_ref = 293.15, [description = "Reference temperature", unit = u"K"] + alpha = 0.0, [description = "Temperature coefficient of heat flow rate", unit = u"1/K"] end @components begin port = HeatPort() - Q_flow = RealInput() + Q_flow = RealInput(; unit = u"W") end @equations begin port.Q_flow ~ -Q_flow.u * (1 + alpha * (port.T - T_ref)) @@ -111,7 +111,7 @@ the temperature at the specified value. @mtkmodel PrescribedTemperature begin @components begin port = HeatPort() - T = RealInput() + T = RealInput(; unit = u"K") end @equations begin port.T ~ T.u diff --git a/src/Thermal/Thermal.jl b/src/Thermal/Thermal.jl index 1a2958fd9..ce0900ac5 100644 --- a/src/Thermal/Thermal.jl +++ b/src/Thermal/Thermal.jl @@ -3,9 +3,10 @@ Library of thermal system components to model heat transfer. """ module Thermal using ModelingToolkit, Symbolics, IfElse -using ...Blocks: RealInput, RealOutput +using ..Blocks: RealInput, RealOutput +using ..DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export HeatPort, Element1D diff --git a/src/Thermal/utils.jl b/src/Thermal/utils.jl index fe50d4c7d..ba38a9731 100644 --- a/src/Thermal/utils.jl +++ b/src/Thermal/utils.jl @@ -1,6 +1,6 @@ @connector HeatPort begin - T(t) = 273.15 + 20.0 - Q_flow(t) = 0.0, [connect = Flow] + T(t) = 273.15 + 20.0, [description = "Temperature", unit = u"K"] + Q_flow(t) = 0.0, [connect = Flow, description = "Heat flow rate", unit = u"W"] end Base.@doc """ HeatPort(; name, T = 273.15 + 20.0, Q_flow = 0.0) @@ -35,8 +35,8 @@ flow rate through the element from `port_a` to `port_b`, `Q_flow`. port_b = HeatPort() end @variables begin - dT(t) = 0.0 - Q_flow(t) = 0.0 + dT(t) = 0.0, [description = "Temperature difference across the component", unit = u"K"] + Q_flow(t) = 0.0, [connect = Flow, description = "Heat flow rate", unit = u"W"] end @equations begin dT ~ port_a.T - port_b.T @@ -69,8 +69,8 @@ flow rate through the element from `solid` to `fluid`, `Q_flow`. fluid = HeatPort() end @variables begin - dT(t) = 0.0 - Q_flow(t) = 0.0 + dT(t) = 0.0, [description = "Temperature difference across the component", unit = u"K"] + Q_flow(t) = 0.0, [connect = Flow, description = "Heat flow rate", unit = u"W"] end @equations begin dT ~ solid.T - fluid.T diff --git a/test/Thermal/demo.jl b/test/Thermal/demo.jl index 0cc950c56..e27d7925b 100644 --- a/test/Thermal/demo.jl +++ b/test/Thermal/demo.jl @@ -1,7 +1,8 @@ using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, Test using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) # Modelica example diff --git a/test/Thermal/thermal.jl b/test/Thermal/thermal.jl index d8f69fd41..1c705272d 100644 --- a/test/Thermal/thermal.jl +++ b/test/Thermal/thermal.jl @@ -1,8 +1,9 @@ using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks: Constant, Step using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) #= # Test HeatCapacitor, TemperatureSensor, RelativeTemperatureSensor, FixedTemperature @@ -210,8 +211,8 @@ end @named T_core = TemperatureSensor() @named convection = ConvectiveConductor(G = 25) @named environment = PrescribedTemperature() - @named amb = Constant(k = T_amb) - @named core_losses_const = Constant(k = 500) + @named amb = Constant(k = T_amb, output.unit = u"K") + @named core_losses_const = Constant(k = 500, output.unit = u"") @named winding_losses = Step(height = 900, offset = 100, start_time = 360, duration = Inf, smooth = false) connections = [connect(windingLosses.port, winding.port) From 1ad657ab1f0728e699e204b66967f92ff1a75882 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:22:28 +0530 Subject: [PATCH 12/80] refactor: update TranslationalPosition components with units and description --- .../TranslationalPosition.jl | 3 ++- .../TranslationalPosition/components.jl | 25 ++++++++----------- .../TranslationalPosition/sources.jl | 7 ++---- src/Mechanical/TranslationalPosition/utils.jl | 10 +++----- test/Mechanical/translational.jl | 16 ++++++------ 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl index 72c91835c..eb6a58153 100644 --- a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl +++ b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl @@ -5,8 +5,9 @@ module TranslationalPosition using ModelingToolkit, Symbolics, IfElse using ...Blocks: RealInput, RealOutput +using ...DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export Flange diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index 3cc366011..2b5c7e495 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -48,10 +48,7 @@ Sliding mass with inertia end @variables begin s(t) = 0.0, [description = "Absolute position of sliding mass", unit = u"m"] - function v(t) - 0.0, - [description = "Absolute linear velocity of sliding mass", unit = u"m*s^-1"] - end + v(t) = 0.0, [description = "Absolute linear velocity of sliding mass", unit = u"m*s^-1"] f(t) = 0.0, [description = "Force", unit = u"N"] end @components begin @@ -107,7 +104,7 @@ Linear 1D translational spring # Parameters: - `k`: [N/m] Spring constant - - `l`: Unstretched spring length + - `l`: [m] Unstretched spring length - `flange_a__s`: [m] Initial value of absolute position of flange_a - `flange_b__s`: [m] Initial value of absolute position of flange_b @@ -124,12 +121,10 @@ end #default function name, k, flange_a__s = 0, flange_b__s = 0, l = 0) pars = @parameters begin - k = k - l = l - end - vars = @variables begin - f(t) = k * (flange_a__s - flange_b__s - l) + k = k, [description = "Spring constant", unit = u"N/m"] + l = l, [description = "Unstretched spring length", unit = u"m"] end + @variables f(t) = k * (flange_a__s - flange_b__s - l) [description = "Force", unit = u"N"] @named flange_a = Flange(; s = flange_a__s, f = k * (flange_a__s - flange_b__s - l)) @named flange_b = Flange(; s = flange_a__s, f = -k * (flange_a__s - flange_b__s - l)) @@ -139,7 +134,7 @@ end #default function f ~ k * (flange_a.s - flange_b.s - l) #delta_s flange_a.f ~ +f flange_b.f ~ -f] - return compose(ODESystem(eqs, t, vars, pars; name = name), flange_a, flange_b) + return compose(ODESystem(eqs, t, [f], pars; name = name), flange_a, flange_b) end """ @@ -160,12 +155,12 @@ Linear 1D translational damper """ @mtkmodel Damper begin @parameters begin - d, [description = "Damping constant", unit = u"N.s/m"] + d, [description = "Damping constant", unit = u"N*s/m"] end @variables begin - va(t) = 0.0 - vb(t) = 0.0 - f(t) = +(va - vb) * d + va(t) = 0.0, [description = "Velocity of flage a", unit = u"m/s"] + vb(t) = 0.0, [description = "Velocity of flage b", unit = u"m/s"] + f(t) = +(va - vb) * d, [description = "Force", unit = u"N"] end @components begin diff --git a/src/Mechanical/TranslationalPosition/sources.jl b/src/Mechanical/TranslationalPosition/sources.jl index 67a94a152..63c9eab89 100644 --- a/src/Mechanical/TranslationalPosition/sources.jl +++ b/src/Mechanical/TranslationalPosition/sources.jl @@ -4,12 +4,9 @@ Input signal acting as external force on a flange """ @mtkmodel Force begin - @parameters begin - use_support - end - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) @components begin - f = RealInput() # Accelerating force acting at flange (= -flange.tau) + f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end @equations begin flange.f ~ -f.u diff --git a/src/Mechanical/TranslationalPosition/utils.jl b/src/Mechanical/TranslationalPosition/utils.jl index 81c16431d..107d7be82 100644 --- a/src/Mechanical/TranslationalPosition/utils.jl +++ b/src/Mechanical/TranslationalPosition/utils.jl @@ -42,8 +42,8 @@ Partial model for the compliant connection of two translational 1-dim. flanges. flange_b = Flange() end @variables begin - v_a(t) = 0.0 - v_b(t) = 0.0 + v_a(t) = 0.0, [description = "Velocity", unit = u"m/s"] + v_b(t) = 0.0, [description = "Velocity", unit = u"m/s"] s_rel(t) = 0.0, [description = "Relative distance ", unit = u"m"] f(t) = 0.0, [description = "Force between flanges", unit = u"N"] end @@ -72,7 +72,7 @@ Partial model for the compliant connection of two translational 1-dim. flanges. flange_b = Flange() end @variables begin - delta_s(t) = 0.0 + delta_s(t) = 0.0, [unit = u"m"] f(t) = 0.0, [description = "Force between flanges", unit = u"N"] end @equations begin @@ -128,9 +128,7 @@ Partial model for a component with two translational 1-dim. flanges and a suppor @named flange_a = Flange() @named flange_b = Flange() sys = [flange_a, flange_b] - @variables function s_support(t) - 0.0, [description = "Absolute position of support flange", unit = u"m"] - end + @variables s_support(t) = 0.0 [description = "Absolute position of support flange", unit = u"m"] if use_support @named support = Support() eqs = [support.s ~ s_support diff --git a/test/Mechanical/translational.jl b/test/Mechanical/translational.jl index 2af419ea3..34f09e6b1 100644 --- a/test/Mechanical/translational.jl +++ b/test/Mechanical/translational.jl @@ -1,17 +1,19 @@ using ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks +import ModelingToolkitStandardLibrary: Mechanical import ModelingToolkitStandardLibrary.Mechanical.Translational as TV import ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition as TP +using DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) @testset "Free" begin function System(; name) systems = @named begin acc = TV.Acceleration() - a = Constant(; k = -10) + a = Constant(; k = -10, output.unit = u"m/s^2") mass = TV.Mass(; m = 100) free = TV.Free() end @@ -85,7 +87,7 @@ end @named fv = TV.Force() @named fp = TP.Force(use_support = false) - @named source = Sine(frequency = 3, amplitude = 2) + @named source = Sine(frequency = 3, amplitude = 2, unit = u"N") function System(damping, spring, body, ground, f, source) eqs = [connect(f.f, source.output) @@ -126,11 +128,11 @@ end spring = TV.Spring(; k = 1000) - src1 = Sine(frequency = 100, amplitude = 2) - src2 = Sine(frequency = 100, amplitude = -1) + src1 = Sine(frequency = 100, amplitude = 2, output__unit = u"m") + src2 = Sine(frequency = 100, amplitude = -1, output__unit = u"N") - pos_value = RealInput() - force_output = RealOutput() + pos_value = RealInput(unit = u"m") + force_output = RealOutput(unit = u"N") end eqs = [connect(pos.s, src1.output) From c345e6e46ed044bbb002aba92f620d584a2967ce Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:23:05 +0530 Subject: [PATCH 13/80] refactor: update MultiBody components with units and description --- src/Mechanical/MultiBody2D/MultiBody2D.jl | 3 ++- test/Mechanical/multibody.jl | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Mechanical/MultiBody2D/MultiBody2D.jl b/src/Mechanical/MultiBody2D/MultiBody2D.jl index ed9ccb80d..53dcf7ae0 100644 --- a/src/Mechanical/MultiBody2D/MultiBody2D.jl +++ b/src/Mechanical/MultiBody2D/MultiBody2D.jl @@ -2,8 +2,9 @@ module MultiBody2D using ModelingToolkit, Symbolics, IfElse using ..Translational +using ...DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export Link diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index c00203c84..46d2a0f53 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -5,8 +5,6 @@ using DifferentialEquations # using Setfield using Test -@parameters t - @named link1 = Link(; m = 1, l = 10, I = 84, g = -9.807) @named link2 = Link(; m = 1, l = 10, I = 84, g = -9.807, x1_0 = 10) @named cart = Mass(; m = 1, s_0 = 0) From 17dc7cb4bf756db95268bf1c7454c4e6c4207f98 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:23:58 +0530 Subject: [PATCH 14/80] feat: register new units --- src/ModelingToolkitStandardLibrary.jl | 4 +++- src/utils.jl | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/utils.jl diff --git a/src/ModelingToolkitStandardLibrary.jl b/src/ModelingToolkitStandardLibrary.jl index d764f8d81..bff0ab19b 100644 --- a/src/ModelingToolkitStandardLibrary.jl +++ b/src/ModelingToolkitStandardLibrary.jl @@ -1,9 +1,10 @@ module ModelingToolkitStandardLibrary import Symbolics: unwrap +using DynamicQuantities """ @symcheck J > 0 || throw(ArgumentError("Expected `J` to be positive")) - + Omits the check expression if the argument `J` is symbolic. """ macro symcheck(ex) @@ -16,6 +17,7 @@ macro symcheck(ex) end end +include("utils.jl") include("Blocks/Blocks.jl") include("Mechanical/Mechanical.jl") include("Thermal/Thermal.jl") diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 000000000..c98db13f3 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1,6 @@ +# Define units + +@register_unit Wb u"m^2*kg*s^-2*A^-1" +@register_unit H u"kg*m^2*s^−2*A^−2" +@register_unit rad u"1" +@register_unit S u"1/Ω" From e8e6a58d5047cff87f1524a86055c48780aefe79 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:24:10 +0530 Subject: [PATCH 15/80] fixup! electrical --- test/chua_circuit.jl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/test/chua_circuit.jl b/test/chua_circuit.jl index c12896d3c..15aa05c42 100644 --- a/test/chua_circuit.jl +++ b/test/chua_circuit.jl @@ -4,22 +4,25 @@ using ModelingToolkitStandardLibrary.Electrical: OnePort using OrdinaryDiffEq using OrdinaryDiffEq: ReturnCode.Success using IfElse: ifelse +using DynamicQuantities: @u_str @testset "Chua Circuit" begin - @parameters t + @parameters t [unit = u"s"] - @component function NonlinearResistor(; name, Ga, Gb, Ve) - @named oneport = OnePort() - @unpack v, i = oneport - pars = @parameters Ga=Ga Gb=Gb Ve=Ve - eqs = [ + @mtkmodel NonlinearResistor begin + @extend OnePort() + @parameters begin + Ga, [description = "Conductance in inner voltage range", unit = u"1/Ω"] + Gb, [description = "Conductance in outer voltage range", unit = u"1/Ω"] + Ve, [description = "Inner voltage range limit", unit = u"V"] + end + @equations begin i ~ ifelse(v < -Ve, Gb * (v + Ve) - Ga * Ve, ifelse(v > Ve, Gb * (v - Ve) + Ga * Ve, - Ga * v)), - ] - extend(ODESystem(eqs, t, [], pars; name = name), oneport) + Ga * v)) + end end @named L = Inductor(L = 18, i = 0.0) From 46ee19f7eaeb3695280443f5d5586232c51e05f5 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:25:09 +0530 Subject: [PATCH 16/80] refactor: update Magnetic components with units and description --- src/Magnetic/FluxTubes/FluxTubes.jl | 4 +++- src/Magnetic/FluxTubes/basic.jl | 4 ++-- src/Magnetic/FluxTubes/sources.jl | 1 + src/Magnetic/FluxTubes/utils.jl | 13 +++++-------- src/Magnetic/Magnetic.jl | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Magnetic/FluxTubes/FluxTubes.jl b/src/Magnetic/FluxTubes/FluxTubes.jl index c082b5f0a..4d39a3a9f 100644 --- a/src/Magnetic/FluxTubes/FluxTubes.jl +++ b/src/Magnetic/FluxTubes/FluxTubes.jl @@ -1,8 +1,10 @@ module FluxTubes using ModelingToolkit using ...Electrical: Pin +import ...Wb +using ...DynamicQuantities: @u_str -@parameters t +@parameters t [unit = u"s"] D = Differential(t) export PositiveMagneticPort, NegativeMagneticPort, TwoPort diff --git a/src/Magnetic/FluxTubes/basic.jl b/src/Magnetic/FluxTubes/basic.jl index 661dd06b2..e8d1a97f9 100644 --- a/src/Magnetic/FluxTubes/basic.jl +++ b/src/Magnetic/FluxTubes/basic.jl @@ -117,7 +117,7 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([Wb]) v(t), [description = "Voltage difference from positive to negative pin", unit = u"V"] i(t), [description = "Current", unit = u"A"] - Phi, [description = "Magnetic flux", unit = u"Wb"] + Phi, [description = "Magnetic flux", unit = Wb] end @extend V_m, Phi = two_port = TwoPort(; Phi = Phi) @components begin @@ -148,7 +148,7 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([`Wb`]) """ @mtkmodel EddyCurrent begin @variables begin - Phi, [description = "Magnetic flux", unit = u"Wb"] + Phi, [description = "Magnetic flux", unit = Wb] end @parameters begin rho = 0.098e-6, [description = "Resistivity of flux tube material", unit = u"Ω*m"] diff --git a/src/Magnetic/FluxTubes/sources.jl b/src/Magnetic/FluxTubes/sources.jl index 9b68f4893..dff4d228d 100644 --- a/src/Magnetic/FluxTubes/sources.jl +++ b/src/Magnetic/FluxTubes/sources.jl @@ -7,6 +7,7 @@ Parameters: - `V_m`: [A] Magnetic potential difference """ + @mtkmodel ConstantMagneticPotentialDifference begin @components begin port_p = PositiveMagneticPort() diff --git a/src/Magnetic/FluxTubes/utils.jl b/src/Magnetic/FluxTubes/utils.jl index 705900a89..15ee132d6 100644 --- a/src/Magnetic/FluxTubes/utils.jl +++ b/src/Magnetic/FluxTubes/utils.jl @@ -32,14 +32,11 @@ Partial component with magnetic potential difference between two magnetic ports end @variables begin V_m(t) = 0.0, [description = "Magnetic potential at the port", unit = u"A"] - function Phi(t) - 0.0, - [ - connect = Flow, - description = "Magnetic flux flowing into the port", - unit = u"Wb", - ] - end + Phi(t) = 0.0, [ + connect = Flow, + description = "Magnetic flux flowing into the port", + unit = u"Wb", + ] end @equations begin V_m ~ port_p.V_m - port_n.V_m diff --git a/src/Magnetic/Magnetic.jl b/src/Magnetic/Magnetic.jl index 825ecd081..4eefda950 100644 --- a/src/Magnetic/Magnetic.jl +++ b/src/Magnetic/Magnetic.jl @@ -1,6 +1,6 @@ module Magnetic -using ModelingToolkit, Unitful +using ModelingToolkit # FluxTubes include("FluxTubes/FluxTubes.jl") From 2847d4570ebdc26fc23258c6913c978504151124 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:25:28 +0530 Subject: [PATCH 17/80] test: update multi-domain circuit --- test/multi_domain.jl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/multi_domain.jl b/test/multi_domain.jl index 6b113c698..6027b45c4 100644 --- a/test/multi_domain.jl +++ b/test/multi_domain.jl @@ -6,9 +6,10 @@ using ModelingToolkitStandardLibrary.Thermal import ModelingToolkitStandardLibrary using ModelingToolkit, OrdinaryDiffEq, Test using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str # using Plots -@parameters t +@parameters t [unit = u"s"] D = Differential(t) @testset "DC motor" begin @@ -21,13 +22,13 @@ D = Differential(t) tau_L_step = -3 @named ground = Ground() @named source = Voltage() - @named voltage_step = Blocks.Step(height = V_step, start_time = 0) + @named voltage_step = Blocks.Step(height = V_step, start_time = 0, output__unit = u"V") @named R1 = Resistor(R = R) @named L1 = Inductor(L = L, i = 0.0) @named emf = EMF(k = k) @named fixed = Fixed() @named load = Torque(use_support = false) - @named load_step = Blocks.Step(height = tau_L_step, start_time = 3) + @named load_step = Blocks.Step(height = tau_L_step, start_time = 3, output__unit = u"N*m") @named inertia = Inertia(J = J) @named friction = Damper(d = f) @@ -72,7 +73,7 @@ D = Differential(t) @test sol[inertia.w][idx_t]≈(dc_gain * [V_step; -tau_L_step])[2] rtol=1e-3 @test sol[emf.i][idx_t]≈(dc_gain * [V_step; -tau_L_step])[1] rtol=1e-3 - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 6.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, Pair[], (0, 6.0)) sol = solve(prob, DFBDF()) @test sol.retcode == Success # EMF equations @@ -103,13 +104,13 @@ end tau_L_step = -3 @named ground = Ground() @named source = Voltage() - @named voltage_step = Blocks.Step(height = V_step, start_time = 0) + @named voltage_step = Blocks.Step(height = V_step, start_time = 0, output__unit = u"V") @named R1 = Resistor(R = R) @named L1 = Inductor(L = L, i = 0.0) @named emf = EMF(k = k) @named fixed = Fixed() @named load = Torque(use_support = false) - @named load_step = Blocks.Step(height = tau_L_step, start_time = 3) + @named load_step = Blocks.Step(height = tau_L_step, start_time = 3, output__unit = u"N*m") @named inertia = Inertia(J = J) @named friction = Damper(d = f) @named speed_sensor = SpeedSensor() @@ -161,7 +162,7 @@ end @test all(sol[inertia.w] .== sol[speed_sensor.w.u]) - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 6.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, Pair[], (0, 6.0)) sol = solve(prob, DFBDF()) @test sol.retcode == Success @@ -183,7 +184,7 @@ end @testset "El. Heating Circuit" begin @named ground = Ground() @named source = Voltage() - @named voltage_sine = Blocks.Sine(amplitude = 220, frequency = 1) + @named voltage_sine = Blocks.Sine(amplitude = 220, frequency = 1, output__unit = u"V") @named heating_resistor = HeatingResistor(R_ref = 100, alpha = 1e-3, T_ref = 293.15) @named thermal_conductor = ThermalConductor(G = 50) @named env = FixedTemperature(T = 273.15 + 20) @@ -207,5 +208,6 @@ end prob = ODEProblem(sys, Pair[], (0, 6.0)) sol = solve(prob, Rodas4()) @test sol.retcode == Success + ### TODO @test sol[source.v * source.i] == -sol[env.port.Q_flow] end From c37216b0ed65500d5035e84cdb863908cdae7994 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:58:13 +0530 Subject: [PATCH 18/80] refactor: kwargs of base system are now kwargs of main component --- src/Blocks/math.jl | 1 - src/Electrical/Analog/ideal_components.jl | 5 +++++ src/Magnetic/FluxTubes/basic.jl | 4 ++-- src/Mechanical/Rotational/components.jl | 8 +++----- src/Mechanical/Rotational/sources.jl | 13 ++++++------- src/Mechanical/TranslationalModelica/sources.jl | 3 ++- src/Mechanical/TranslationalPosition/sources.jl | 3 ++- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index 91280a91b..466c7e444 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -54,7 +54,6 @@ Output the product of a gain matrix with the input signal vector. for i in 1:nout]... end end -MatrixGain.f(K; name) = MatrixGain.f(; name, K) """ Sum(; input__nin::Int, name) diff --git a/src/Electrical/Analog/ideal_components.jl b/src/Electrical/Analog/ideal_components.jl index 68d446339..59b98cdda 100644 --- a/src/Electrical/Analog/ideal_components.jl +++ b/src/Electrical/Analog/ideal_components.jl @@ -254,6 +254,11 @@ Electromotoric force (electric/mechanic transformer) phi(t) = 0.0, [description = "Rotation angle", unit = u"rad"] w(t) = 0.0, [description = "Angular velocity", unit = u"rad/s"] end + @extend v, i = oneport = OnePort() + @components begin + flange = Flange() + support = Support() + end @equations begin phi ~ flange.phi - support.phi D(phi) ~ w diff --git a/src/Magnetic/FluxTubes/basic.jl b/src/Magnetic/FluxTubes/basic.jl index e8d1a97f9..d88c1b453 100644 --- a/src/Magnetic/FluxTubes/basic.jl +++ b/src/Magnetic/FluxTubes/basic.jl @@ -119,7 +119,7 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([Wb]) i(t), [description = "Current", unit = u"A"] Phi, [description = "Magnetic flux", unit = Wb] end - @extend V_m, Phi = two_port = TwoPort(; Phi = Phi) + @extend V_m, Phi = two_port = TwoPort(; Phi) @components begin p = Pin() n = Pin() @@ -157,7 +157,7 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([`Wb`]) R = rho * l / A, [description = "Electrical resistance of eddy current path", unit = u"Ω"] end - @extend (V_m, Phi) = two_port = TwoPort(; Phi = Phi) + @extend (V_m, Phi) = two_port = TwoPort(; Phi) @equations begin D(Phi) ~ V_m * R end diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index b667e257f..cd84b0db8 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -199,11 +199,9 @@ This element characterizes any type of gear box which is fixed in the ground and - `ratio`: Transmission ratio (flange_a.phi/flange_b.phi) - `use_support`: If support flange enabled, otherwise implicitly grounded """ -@mtkmodel IdealGear begin#(; name, ratio, use_support = false) - @parameters begin - use_support - end - @extend phi_support, flange_a, flange_b = partial_element = PartialElementaryTwoFlangesAndSupport2(use_support = use_support) +@mtkmodel IdealGear begin + @extend phi_support, flange_a, flange_b = partial_element = PartialElementaryTwoFlangesAndSupport2(; + use_support) @parameters begin ratio, [description = "Transmission ratio"] end diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 8ac70636a..63fdeea4d 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -1,8 +1,6 @@ @mtkmodel PartialTorque begin - @parameters begin - use_support - end - @extend flange, phi_support = partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) + @extend flange, phi_support = partial_element = PartialElementaryOneFlangeAndSupport2(; + use_support) @variables begin phi(t), [description = "Angle of flange with respect to support", @@ -32,7 +30,8 @@ Input signal acting as external torque on a flange - `use_support` """ @mtkmodel Torque begin - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; + use_support) @components begin tau = RealInput(unit = u"N*m") end @@ -65,9 +64,9 @@ Constant torque source [ description = "Constant torque (if negative, torque is acting as load in positive direction of rotation)", unit = u"N*m"] - use_support + ] end - @extend flange, phi = partial_element = PartialTorque(; use_support = use_support) + @extend flange, phi = partial_element = PartialTorque(; use_support) @variables begin tau(t), [description = "Accelerating torque acting at flange (= -flange.tau)", unit = u"N*m"] diff --git a/src/Mechanical/TranslationalModelica/sources.jl b/src/Mechanical/TranslationalModelica/sources.jl index 04c5a49dd..8b651974b 100644 --- a/src/Mechanical/TranslationalModelica/sources.jl +++ b/src/Mechanical/TranslationalModelica/sources.jl @@ -4,7 +4,8 @@ Input signal acting as external force on a flange """ @mtkmodel Force begin - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; + use_support) @components begin f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end diff --git a/src/Mechanical/TranslationalPosition/sources.jl b/src/Mechanical/TranslationalPosition/sources.jl index 63c9eab89..9974ded91 100644 --- a/src/Mechanical/TranslationalPosition/sources.jl +++ b/src/Mechanical/TranslationalPosition/sources.jl @@ -4,7 +4,8 @@ Input signal acting as external force on a flange """ @mtkmodel Force begin - @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; use_support) + @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; + use_support) @components begin f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end From e5c3ab78e96ae029eb3864e9ff44016b0acf6c95 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:09:11 +0530 Subject: [PATCH 19/80] refactor: set `use_support` to false --- docs/src/tutorials/dc_motor_pi.md | 4 ++-- src/Mechanical/Rotational/components.jl | 4 ++-- src/Mechanical/Rotational/sources.jl | 14 +++++++------- src/Mechanical/TranslationalModelica/sources.jl | 4 ++-- src/Mechanical/TranslationalPosition/sources.jl | 4 ++-- src/Mechanical/TranslationalPosition/utils.jl | 2 +- test/Blocks/test_analysis_points.jl | 2 +- test/Mechanical/rotational.jl | 4 ++-- test/Mechanical/translational.jl | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/src/tutorials/dc_motor_pi.md b/docs/src/tutorials/dc_motor_pi.md index 0dcc3af45..ae711ed0d 100644 --- a/docs/src/tutorials/dc_motor_pi.md +++ b/docs/src/tutorials/dc_motor_pi.md @@ -27,7 +27,7 @@ L = 4.5e-3 # [H] armature inductance k = 0.5 # [N.m/A] motor constant J = 0.02 # [kg.m²] inertia f = 0.01 # [N.m.s/rad] friction factor -tau_L_step = -0.3 # [N.m] amplitude of the load torque step +tau_L_step = -0.3 # [N.m] amplitude of the load torque step nothing # hide ``` @@ -43,7 +43,7 @@ The actual model can now be composed. @named L1 = Inductor(L = L) @named emf = EMF(k = k) @named fixed = Fixed() -@named load = Torque(use_support = false) +@named load = Torque() @named load_step = Blocks.Step(height = tau_L_step, start_time = 3) @named inertia = Inertia(J = J) @named friction = Damper(d = f) diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index cd84b0db8..554ce1c6b 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -197,11 +197,11 @@ This element characterizes any type of gear box which is fixed in the ground and # Parameters: - `ratio`: Transmission ratio (flange_a.phi/flange_b.phi) - - `use_support`: If support flange enabled, otherwise implicitly grounded + - `use_support`: If support flange enabled, otherwise implicitly grounded. By default it is `false` """ @mtkmodel IdealGear begin @extend phi_support, flange_a, flange_b = partial_element = PartialElementaryTwoFlangesAndSupport2(; - use_support) + use_support = false) @parameters begin ratio, [description = "Transmission ratio"] end diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 63fdeea4d..979b71821 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -1,6 +1,6 @@ @mtkmodel PartialTorque begin @extend flange, phi_support = partial_element = PartialElementaryOneFlangeAndSupport2(; - use_support) + use_support = false) @variables begin phi(t), [description = "Angle of flange with respect to support", @@ -12,7 +12,7 @@ end """ - Torque(; name, use_support) + Torque(; name, use_support = false) Input signal acting as external torque on a flange @@ -31,7 +31,7 @@ Input signal acting as external torque on a flange """ @mtkmodel Torque begin @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; - use_support) + use_support = false) @components begin tau = RealInput(unit = u"N*m") end @@ -41,7 +41,7 @@ Input signal acting as external torque on a flange end """ - ConstantTorque(; name, tau_constant, use_support) + ConstantTorque(; name, tau_constant, use_support = false) Constant torque source @@ -56,7 +56,7 @@ Constant torque source # Arguments: - `tau_constant`: The constant torque applied by the source -- `use_support`: Whether or not an internal support flange is added. +- `use_support`: Whether or not an internal support flange is added. By default, it is `false` """ @mtkmodel ConstantTorque begin @parameters begin @@ -66,7 +66,7 @@ Constant torque source unit = u"N*m"] ] end - @extend flange, phi = partial_element = PartialTorque(; use_support) + @extend flange, phi = partial_element = PartialTorque(; use_support = false) @variables begin tau(t), [description = "Accelerating torque acting at flange (= -flange.tau)", unit = u"N*m"] @@ -81,7 +81,7 @@ Constant torque source end """ - Speed(; name, use_support, exact = false, f_crit = 50) + Speed(; name, use_support = false, exact = false, f_crit = 50) Forced movement of a flange according to a reference angular velocity signal diff --git a/src/Mechanical/TranslationalModelica/sources.jl b/src/Mechanical/TranslationalModelica/sources.jl index 8b651974b..c744ed63f 100644 --- a/src/Mechanical/TranslationalModelica/sources.jl +++ b/src/Mechanical/TranslationalModelica/sources.jl @@ -1,11 +1,11 @@ """ - Force(;name) + Force(; name, use_support = false) Input signal acting as external force on a flange """ @mtkmodel Force begin @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; - use_support) + use_support = false) @components begin f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end diff --git a/src/Mechanical/TranslationalPosition/sources.jl b/src/Mechanical/TranslationalPosition/sources.jl index 9974ded91..c744ed63f 100644 --- a/src/Mechanical/TranslationalPosition/sources.jl +++ b/src/Mechanical/TranslationalPosition/sources.jl @@ -1,11 +1,11 @@ """ - Force(; name) + Force(; name, use_support = false) Input signal acting as external force on a flange """ @mtkmodel Force begin @extend (flange,) = partial_element = PartialElementaryOneFlangeAndSupport2(; - use_support) + use_support = false) @components begin f = RealInput(unit = u"N") # Accelerating force acting at flange (= -flange.tau) end diff --git a/src/Mechanical/TranslationalPosition/utils.jl b/src/Mechanical/TranslationalPosition/utils.jl index 107d7be82..bbd1a0249 100644 --- a/src/Mechanical/TranslationalPosition/utils.jl +++ b/src/Mechanical/TranslationalPosition/utils.jl @@ -118,7 +118,7 @@ Partial model for a component with two translational 1-dim. flanges and a suppor # Parameters: - - `use_support`: If support flange enabled, otherwise implicitly grounded + - `use_support`: If support flange enabled, otherwise implicitly grounded. By default it is `false` # States: diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index cdbd33e32..9a9331b52 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -160,7 +160,7 @@ c = 10 # Damping coefficient @named inertia2 = Inertia(; J = m2) @named spring = Spring(; c = k) @named damper = Damper(; d = c) -@named torque = Torque(; use_support = false) +@named torque = Torque() function SystemModel(u = nothing; name = :model) eqs = [connect(torque.flange, inertia1.flange_a) diff --git a/test/Mechanical/rotational.jl b/test/Mechanical/rotational.jl index 6198d68d8..47f588f5a 100644 --- a/test/Mechanical/rotational.jl +++ b/test/Mechanical/rotational.jl @@ -62,7 +62,7 @@ end J_motor = 0.1 # Motor inertia @named fixed = Fixed() - @named torque = Torque(use_support = true) + @named torque = Torque(; use_support = true) @named inertia1 = Inertia(J = 2, phi = pi / 2) @named spring = Rotational.Spring(c = 1e4) @named damper = Damper(d = 10) @@ -192,7 +192,7 @@ end @named friction = RotationalFriction(f = 0.001, tau_c = 20, w_brk = 0.06035, tau_brk = 25) @named vel_profile = VelocityProfile() - @named source = Speed(use_support = false) + @named source = Speed() @named angle_sensor = AngleSensor() connections = [connect(vel_profile.output, source.w_ref) diff --git a/test/Mechanical/translational.jl b/test/Mechanical/translational.jl index 34f09e6b1..1587a43a6 100644 --- a/test/Mechanical/translational.jl +++ b/test/Mechanical/translational.jl @@ -85,7 +85,7 @@ end @named gp = TP.Fixed(s_0 = 1) @named fv = TV.Force() - @named fp = TP.Force(use_support = false) + @named fp = TP.Force() @named source = Sine(frequency = 3, amplitude = 2, unit = u"N") From 540c246b5ec579694a997759f54f043c0efd8ad8 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 21 Aug 2023 11:44:40 -0400 Subject: [PATCH 20/80] Use lowest order differentiated variables to improve state selection Note that we can infer `D(x) = D(y)` from `x = y`, but we cannot infer `x = y` from `D(x) = D(y)`, so using the lowest variable in the connector provides more information to the compiler. --- src/Mechanical/MultiBody2D/components.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Mechanical/MultiBody2D/components.jl b/src/Mechanical/MultiBody2D/components.jl index 99fadd123..3a67dfd5c 100644 --- a/src/Mechanical/MultiBody2D/components.jl +++ b/src/Mechanical/MultiBody2D/components.jl @@ -41,11 +41,11 @@ end @components begin - TX1 = MechanicalPort() - TY1 = MechanicalPort() + TX1 = Flange() + TY1 = Flange() - TX2 = MechanicalPort() - TY2 = MechanicalPort() + TX2 = Flange() + TY2 = Flange() end @equations begin @@ -76,12 +76,12 @@ x_cm ~ l * cos(A) / 2 + x1 y_cm ~ l * sin(A) / 2 + y1 TX1.f ~ fx1 - TX1.v ~ dx1 + TX1.s ~ x1 TY1.f ~ fy1 - TY1.v ~ dy1 + TY1.s ~ y1 TX2.f ~ fx2 - TX2.v ~ dx2 + TX2.s ~ x2 TY2.f ~ fy2 - TY2.v ~ dy2 + TY2.s ~ y2 end end From c9a8a7d140da645910ad36e327c99ee914044860 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 21 Aug 2023 12:13:11 -0400 Subject: [PATCH 21/80] Add initial condition for Fixed --- src/Mechanical/TranslationalPosition/components.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index 2b5c7e495..8327187d2 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -13,7 +13,7 @@ Flange fixed in housing at a given position. """ @mtkmodel Fixed begin @parameters begin - s_0, [description = "Fixed offset position of housing", unit = u"m"] + s_0 = 0, [description = "Fixed offset position of housing", unit = u"m"] end @components begin flange = Flange(; s = s_0) From 9f598ebedccc530f6de81630d9716d963180e789 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 21 Aug 2023 12:26:33 -0400 Subject: [PATCH 22/80] Fix MatrixGain --- src/Blocks/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index 466c7e444..4381ca523 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -50,7 +50,7 @@ Output the product of a gain matrix with the input signal vector. output = RealOutput(; nout = nout) end @equations begin - [(output.u[i] ~ sum(K[i, j] * input.u[j])) for j in 1:nin + [output.u[i] ~ sum(getdefault(K)[i, j] * input.u[j] for j in 1:nin) for i in 1:nout]... end end From c46369d59e046e59697d3f56cc1fc14569cac29a Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 14:16:26 -0400 Subject: [PATCH 23/80] Update tests --- test/Mechanical/multibody.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index 46d2a0f53..b7c3ef791 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -1,6 +1,6 @@ using ModelingToolkit using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D -using ModelingToolkitStandardLibrary.Mechanical.Translational +using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition using DifferentialEquations # using Setfield using Test From 5a93249e5a339f56f84ed21d32c5a17d7c54adc8 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 14:29:31 -0400 Subject: [PATCH 24/80] Update tests and fix use_support support --- test/Mechanical/multibody.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index b7c3ef791..0b3d8a6ed 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -21,6 +21,7 @@ eqs = [connect(link1.TX1, cart.flange) #, force.flange) @named model = ODESystem(eqs, t, [], []; systems = [link1, link2, cart, fixed]) sys = structural_simplify(model) +@test length(states(sys)) == 6 # The below code does work... #= From 0af312d62d21100b7840e7f864c415ebaf16d45e Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 22 Aug 2023 16:09:22 -0400 Subject: [PATCH 25/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index af522a647..aea5f1a3d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.1.1" +version = "2.2.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From b94a8cc91d02cb77e19b21a11aaf9955d7a53428 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:05:07 +0530 Subject: [PATCH 26/80] refactor: use `@structural_parameters` to pass functions to `StaticNonLinearity`, `TimeVaryingFunction` --- src/Blocks/math.jl | 6 +++--- src/Blocks/sources.jl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index 4381ca523..cae7338d2 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -221,15 +221,15 @@ If the given function is not composed of simple core methods (e.g. sin, abs, ... - `output` """ @mtkmodel StaticNonLinearity begin - @parameters begin + @structural_parameters begin func end @extend u, y = siso = SISO() @equations begin - y ~ first(getdefault(func))(u) + y ~ func(u) end end -StaticNonLinearity.f(func; name) = StaticNonLinearity.f(; name = name, func = [func]) +StaticNonLinearity.f(func; name) = StaticNonLinearity(; func, name) """ Abs(; name) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index 4a476d4a7..f5d3185d7 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -97,17 +97,17 @@ The input variable `t` can be changed by passing a different variable as the key - `output` """ @mtkmodel TimeVaryingFunction begin - @parameters begin + @structural_parameters begin f end @components begin output = RealOutput(; unit) end @equations begin - output.u ~ first(getdefault(f))(t) + output.u ~ f(t) end end -TimeVaryingFunction.f(f; name) = TimeVaryingFunction.f(; f = [f], name) +TimeVaryingFunction.f(f; name) = TimeVaryingFunction(; f, name) """ Sine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, From 484476fbc260ec2506f12711cd7ac1cc094188ff Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:17:25 +0530 Subject: [PATCH 27/80] docs: Translational.Mass: `s_0` -> `s` --- src/Mechanical/Translational/components.jl | 6 +++--- test/Mechanical/multibody.jl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mechanical/Translational/components.jl b/src/Mechanical/Translational/components.jl index 299b44786..c93c28b81 100644 --- a/src/Mechanical/Translational/components.jl +++ b/src/Mechanical/Translational/components.jl @@ -38,7 +38,7 @@ Fixes a flange position (velocity = 0) end """ - Mass(; name, v_0 = 0.0, m, s_0 = nothing, g = nothing) + Mass(; name, v_0 = 0.0, m, s = nothing, g = nothing) Sliding mass with inertia @@ -46,13 +46,13 @@ Sliding mass with inertia - `m`: [kg] mass of sliding body - `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass (default 0 m/s) - - `s_0`: [m] (optional) initial value of absolute position of sliding mass + - `s`: [m] (optional) initial value of absolute position of sliding mass - `g`: [m/s²] (optional) gravity field acting on the mass, positive value acts in the positive direction # States: - `v`: [m/s] absolute linear velocity of sliding mass - - `s`: [m] (optional with parameter s_0) absolute position of sliding mass + - `s`: [m] (optional with parameter s) absolute position of sliding mass # Connectors: diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index 0b3d8a6ed..f53307344 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -1,13 +1,13 @@ using ModelingToolkit using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition -using DifferentialEquations +using OrdinaryDiffEq # using Setfield using Test @named link1 = Link(; m = 1, l = 10, I = 84, g = -9.807) @named link2 = Link(; m = 1, l = 10, I = 84, g = -9.807, x1_0 = 10) -@named cart = Mass(; m = 1, s_0 = 0) +@named cart = Mass(; m = 1, s = 0) # @named force = SineForce(;amp=3e3, freq=15) @named fixed = Fixed() # @named m1 = Mass(;m=0.5) From 9f3da716c005a5392b285c632f34d2cf97dab53e Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:59:51 +0530 Subject: [PATCH 28/80] chore: bump MTK bound to 8.67 --- Project.toml | 2 +- docs/Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index aea5f1a3d..23fd4a912 100644 --- a/Project.toml +++ b/Project.toml @@ -15,7 +15,7 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" ChainRulesCore = "1" DiffEqBase = "6" IfElse = "0.1" -ModelingToolkit = "8.50" +ModelingToolkit = "8.67" Symbolics = "4.9, 5" julia = "1.6" diff --git a/docs/Project.toml b/docs/Project.toml index 5a5c39a45..e38c82671 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -13,7 +13,7 @@ ControlSystemsBase = "1.1" DifferentialEquations = "7.6" Documenter = "0.27" IfElse = "0.1" -ModelingToolkit = "8" +ModelingToolkit = "8.67" ModelingToolkitStandardLibrary = "2" OrdinaryDiffEq = "6.31" Plots = "1.36" From 53c02a6c9c4bb451ae83e373253d918a9fc22692 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Tue, 5 Sep 2023 13:05:12 +0530 Subject: [PATCH 29/80] chore: bump version to v2.3.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 23fd4a912..cca441eae 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.2.0" +version = "2.3.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From e95af507e78cbbe3270ea08a35839ca3508c0a49 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 5 Sep 2023 09:35:15 +0200 Subject: [PATCH 30/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cca441eae..1c21a81ae 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.3.0" +version = "2.2.1" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From f5adb7d5ec99f5617e7f6f1478e1ac860b43d4f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 01:46:31 +0000 Subject: [PATCH 31/80] chore(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CI.yml | 2 +- .github/workflows/Documentation.yml | 2 +- .github/workflows/Downstream.yml | 4 ++-- .github/workflows/FormatCheck.yml | 2 +- .github/workflows/Invalidations.yml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 92b7ab8d7..023acbf5e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,7 +17,7 @@ jobs: - '1' - '1.6' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index bca716089..14bd20018 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: version: '1' diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index 5528e2cb0..932cfd3f5 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -19,14 +19,14 @@ jobs: package: - {user: SciML, repo: ModelingToolkit.jl} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.julia-version }} arch: x64 - uses: julia-actions/julia-buildpkg@latest - name: Clone Downstream - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} path: downstream diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index f80d0b18b..dd551501c 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -21,7 +21,7 @@ jobs: with: version: ${{ matrix.julia-version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install JuliaFormatter and format # This will use the latest version by default but you can set the version like so: # diff --git a/.github/workflows/Invalidations.yml b/.github/workflows/Invalidations.yml index 4d0004e83..28b9ce2fa 100644 --- a/.github/workflows/Invalidations.yml +++ b/.github/workflows/Invalidations.yml @@ -19,12 +19,12 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: '1' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-invalidations@v1 id: invs_pr - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.repository.default_branch }} - uses: julia-actions/julia-buildpkg@v1 From 2c2c200ed67b6c885248b8f275b7a56dbd6ec558 Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 13 Sep 2023 16:38:35 -0400 Subject: [PATCH 32/80] changes for ModelingToolkit tuple parameters --- src/Blocks/sources.jl | 1410 +++++++-------- .../IsothermalCompressible/components.jl | 1568 ++++++++--------- test/Blocks/sources.jl | 916 +++++----- 3 files changed, 1972 insertions(+), 1922 deletions(-) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index f5d3185d7..06cb734f6 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -1,692 +1,718 @@ -using DiffEqBase -import ChainRulesCore - -# Define and register smooth functions -# These are "smooth" aka differentiable and avoid Gibbs effect -# These follow: `offset` + `smooth_wave` * `smooth_step` with zero output for `t < start_time` -function smooth_cos(x, δ, f, amplitude, ϕ, offset, start_time) - offset + - amplitude * cos(2 * π * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_damped_sin(x, δ, f, amplitude, damping, ϕ, offset, start_time) - offset + - exp((start_time - x) * damping) * amplitude * sin(2 * π * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_ramp(x, δ, height, duration, offset, start_time) - offset + - height / (duration) * - (smooth_xH(x, δ, start_time) - smooth_xH(x, δ, start_time + duration)) -end - -function smooth_sin(x, δ, f, amplitude, ϕ, offset, start_time) - offset + - amplitude * sin(2 * pi * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_square(x, δ, f, amplitude, offset, start_time) - offset + - amplitude * 2atan(sin(2π * (x - start_time) * f) / δ) / π * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_step(x, δ, height, offset, start_time) - offset + height * (atan((x - start_time) / δ) / π + 0.5) -end - -function smooth_triangular(x, δ, f, amplitude, offset, start_time) - offset + - amplitude * (1 - 2acos((1 - δ)sin(2π * (x - start_time) * f)) / π) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_xH(x, δ, tₒ) - 0.5 * (x - tₒ) * (1 + ((x - tₒ) / sqrt((x - tₒ)^2 + δ^2))) -end - -function square(x, f, amplitude, offset, start_time) - offset + - (x > start_time) * (amplitude * - (4 * floor(f * (x - start_time)) - 2 * floor(2 * (x - start_time) * f) + 1)) -end - -function triangular(x, f, amplitude, offset, start_time) - p = 1 / f # period - offset + - (x > start_time) * - (4 * amplitude * f * abs(abs((x - p / 4 - start_time) % p) - p / 2) - amplitude) -end - -""" - Constant(; name, k = 0.0) - -Generate constant signal. - -# Parameters: - - - `k`: Constant output value - -# Connectors: - - - `output` -""" -@mtkmodel Constant begin - @components begin - output = RealOutput(; unit) - end - @parameters begin - k = 0.0, [description = "Constant output value of block"] - end - @equations begin - output.u ~ k - end -end - -""" - TimeVaryingFunction(f; name) - -Outputs ``f(t)``. - -The input variable `t` can be changed by passing a different variable as the keyword argument `t`. - -# Connectors: -- `output` -""" -@mtkmodel TimeVaryingFunction begin - @structural_parameters begin - f - end - @components begin - output = RealOutput(; unit) - end - @equations begin - output.u ~ f(t) - end -end -TimeVaryingFunction.f(f; name) = TimeVaryingFunction(; f, name) - -""" - Sine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, - smooth = false) - -Generate sine signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of sine wave - - `amplitude`: Amplitude of sine wave - - `phase`: [rad] Phase of sine wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Sine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase - equation = if smooth == false - offset + ifelse(t < start_time, 0, - amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_sin(t, smooth, frequency, amplitude, phase, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Cosine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, - smooth = false) - -Cosine signal. - -# Parameters: -- `frequency`: [Hz] Frequency of sine wave -- `amplitude`: Amplitude of sine wave -- `phase`: [rad] Phase of sine wave -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: -- `output` -""" - -@component function Cosine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase - equation = if smooth == false - offset + ifelse(t < start_time, zero(t), - amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_cos(t, smooth, frequency, amplitude, phase, offset, start_time) - end - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - ContinuousClock(; name, offset = 0, start_time = 0) - -Generate current time signal. - -# Parameters: - - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - -# Connectors: - - - `output` -""" -@component function ContinuousClock(; name, offset = 0, start_time = 0, output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time - eqs = [ - output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" -Ramp(; name, height = 1, duration = 1, offset = 0, start_time = 0, smooth = false) - -Generate ramp signal. - -# Parameters: - - - `height`: Height of ramp - - `duration`: [s] Duration of ramp (= 0.0 gives a Step) - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Ramp(; name, - height = 1, - duration = 1, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time height=height duration=duration - equation = if smooth == false - offset + ifelse(t < start_time, 0, - ifelse(t < (start_time + duration), (t - start_time) * height / duration, - height)) - else - smooth === true && (smooth = 1e-5) - smooth_ramp(t, smooth, height, duration, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Square(; name, frequency = 1.0, amplitude = 1.0, offset = 0.0, start_time = 0.0, - smooth = false) -Generate smooth square signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of square wave - - `amplitude`: Amplitude of square wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Square(; name, frequency = 1.0, amplitude = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters begin - frequency = frequency - amplitude = amplitude - offset = offset - start_time = start_time - end - - equation = if smooth == false - square(t, frequency, amplitude, offset, start_time) - else - smooth === true && (smooth = 1e-5) - smooth_square(t, smooth, frequency, amplitude, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Step(;name, height=1, offset=0, start_time=0, duration=Inf, smooth=true) - -Generate step signal. - -# Parameters: - - - `height`: Height of step - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. - - `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. - - `smooth`: If `true`, returns a smooth wave. Defaults to `true` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, - smooth = 1e-5, output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - duration_numeric = duration - pars = @parameters offset=offset start_time=start_time height=height duration=duration - equation = if smooth == false # use comparison in case smooth is a float - offset + ifelse((start_time < t) & (t < start_time + duration), height, 0) - else - smooth === true && (smooth = 1e-5) - if duration_numeric == Inf - smooth_step(t, smooth, height, offset, start_time) - else - smooth_step(t, smooth, height, offset, start_time) - - smooth_step(t, smooth, height, 0, start_time + duration) - end - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - ExpSine(; name, frequency, amplitude = 1, damping = 0.1, phase = 0, offset = 0, start_time = 0, smooth = false) - -Exponentially damped sine signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of sine wave - - `amplitude`: Amplitude of sine wave - - `damping`: [1/s] Damping coefficient of sine wave - - `phase`: [rad] Phase of sine wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function ExpSine(; name, - frequency, - amplitude = 1, - damping = 0.1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping - - equation = if smooth == false - offset + ifelse(t < start_time, 0, - amplitude * exp(-damping * (t - start_time)) * - sin(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_damped_sin(t, smooth, frequency, amplitude, damping, phase, offset, - start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Triangular(; name, amplitude = 1.0, frequency = 1.0, offset = 0.0, - start_time = 0.0, smooth = false) - -Generate smooth triangular signal for frequencies less than or equal to 25 Hz - -# Parameters: - - - `frequency`: [Hz] Frequency of square wave - - `amplitude`: Amplitude of square wave - - `offset`: Offset of output signal. - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Triangular(; name, amplitude = 1.0, frequency = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters begin - amplitude = amplitude - frequency = frequency - offset = offset - start_time = start_time - end - - equation = if smooth == false - triangular(t, frequency, amplitude, offset, start_time) - else - smooth === true && (smooth = 1e-5) - smooth_triangular(t, smooth, frequency, amplitude, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -# TODO: -# - Exponentials Generate a rising and falling exponential signal -# - Pulse Generate pulse signal of type Real -# - SawTooth Generate saw tooth signal -# - Trapezoid Generate trapezoidal signal of type Real - -function linear_interpolation(x1::T, x2::T, t1::T, t2::T, t) where {T <: Real} - if t1 != t2 - slope = (x2 - x1) / (t2 - t1) - intercept = x1 - slope * t1 - - return slope * t + intercept - else - @assert x1==x2 "x1 ($x1) and x2 ($x2) should be equal if t1 == t2" - - return x2 - end -end - -struct Parameter{T <: Real} - data::Vector{T} - ref::T - circular_buffer::Bool -end - -Parameter(data::Vector{T}, ref::T) where {T <: Real} = Parameter(data, ref, true) -Parameter(x::Parameter) = x -function Parameter(x::T; tofloat = true) where {T <: Real} - if tofloat - x = float(x) - P = typeof(x) - else - P = T - end - - return Parameter(P[], x) -end - -function Base.isequal(x::Parameter, y::Parameter) - b0 = length(x.data) == length(y.data) - if b0 - b1 = all(x.data .== y.data) - b2 = x.ref == y.ref - return b1 & b2 - else - return false - end -end - -Base.:*(x::Number, y::Parameter) = x * y.ref -Base.:*(y::Parameter, x::Number) = Base.:*(x, y) -Base.:*(x::Parameter, y::Parameter) = x.ref * y.ref - -Base.:/(x::Number, y::Parameter) = x / y.ref -Base.:/(y::Parameter, x::Number) = y.ref / x -Base.:/(x::Parameter, y::Parameter) = x.ref / y.ref - -Base.:+(x::Number, y::Parameter) = x + y.ref -Base.:+(y::Parameter, x::Number) = Base.:+(x, y) -Base.:+(x::Parameter, y::Parameter) = x.ref + y.ref - -Base.:-(y::Parameter) = -y.ref -Base.:-(x::Number, y::Parameter) = x - y.ref -Base.:-(y::Parameter, x::Number) = y.ref - x -Base.:-(x::Parameter, y::Parameter) = x.ref - y.ref - -Base.:^(x::Number, y::Parameter) = Base.:^(x, y.ref) -Base.:^(y::Parameter, x::Number) = Base.:^(y.ref, x) -Base.:^(x::Parameter, y::Parameter) = Base.:^(x.ref, y.ref) - -Base.isless(x::Parameter, y::Number) = Base.isless(x.ref, y) -Base.isless(y::Number, x::Parameter) = Base.isless(y, x.ref) - -Base.copy(x::Parameter{T}) where {T} = Parameter{T}(copy(x.data), x.ref) - -ifelse(c::Bool, x::Parameter, y::Parameter) = ifelse(c, x.ref, y.ref) -ifelse(c::Bool, x::Parameter, y::Number) = ifelse(c, x.ref, y) -ifelse(c::Bool, x::Number, y::Parameter) = ifelse(c, x, y.ref) - -Base.max(x::Number, y::Parameter) = max(x, y.ref) -Base.max(x::Parameter, y::Number) = max(x.ref, y) -Base.max(x::Parameter, y::Parameter) = max(x.ref, y.ref) - -Base.min(x::Number, y::Parameter) = min(x, y.ref) -Base.min(x::Parameter, y::Number) = min(x.ref, y) -Base.min(x::Parameter, y::Parameter) = min(x.ref, y.ref) - -function Base.show(io::IO, m::MIME"text/plain", p::Parameter) - if !isempty(p.data) - print(io, p.data) - else - print(io, p.ref) - end -end - -function get_sampled_data(t, memory::Parameter{T}) where {T} - if t < 0 - t = zero(t) - end - - if isempty(memory.data) - if T <: AbstractFloat - return T(NaN) - else - return zero(T) - end - end - - i1 = floor(Int, t / memory.ref) + 1 #expensive - i2 = i1 + 1 - - t1 = (i1 - 1) * memory.ref - x1 = @inbounds memory.data[i1] - - if t == t1 - return x1 - else - n = length(memory.data) - - if memory.circular_buffer - i1 = (i1 - 1) % n + 1 - i2 = (i2 - 1) % n + 1 - else - if i2 > n - i2 = n - i1 = i2 - 1 - end - end - - t2 = (i2 - 1) * memory.ref - x2 = @inbounds memory.data[i2] - return linear_interpolation(x1, x2, t1, t2, t) - end -end - -get_sample_time(memory::Parameter) = memory.ref -Symbolics.@register_symbolic get_sample_time(memory) - -Symbolics.@register_symbolic get_sampled_data(t, memory) - -function first_order_backwards_difference(t, memory) - Δt = get_sample_time(memory) - x1 = get_sampled_data(t, memory) - x0 = get_sampled_data(t - Δt, memory) - - return (x1 - x0) / Δt -end - -function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{2, Any}, ::Val{1}) - t = @inbounds args[1] - memory = @inbounds args[2] - first_order_backwards_difference(t, memory) -end -function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, memory) - first_order_backwards_difference(t, memory) * ẋ -end - -""" - SampledData(; name, buffer) - -data input component. - -# Parameters: - - `buffer`: a `Parameter` type which holds the data and sample time - -# Connectors: - - `output` -""" -@component function SampledData(; name, buffer, unit = nothing) - pars = @parameters begin - buffer = buffer - end - vars = [] - systems = @named begin - output = RealOutput(; unit) - end - eqs = [ - output.u ~ get_sampled_data(t, buffer), - ] - return ODESystem(eqs, t, vars, pars; name, systems, - defaults = [output.u => get_sampled_data(0.0, buffer)]) -end -@deprecate Input SampledData - -function SampledData(T::Type, circular_buffer = true; name) - SampledData(T[], zero(T), circular_buffer; name) -end -function SampledData(dt::T, circular_buffer = true) where {T <: Real} - SampledData(T[], dt, circular_buffer; name) -end -function SampledData(data::Vector{T}, dt::T, circular_buffer = true; name) where {T <: Real} - SampledData(; name, buffer = Parameter(data, dt, circular_buffer)) -end - -Base.convert(::Type{T}, x::Parameter{T}) where {T <: Real} = x.ref -function Base.convert(::Type{<:Parameter{T}}, x::Number) where {T <: Real} - Parameter{T}(T[], x, true) -end - -# Beta Code for potential AE Hack ---------------------- -function set_sampled_data!(memory::Parameter{T}, t, x, Δt::Parameter{T}) where {T} - if t < 0 - t = zero(t) - end - - if t == zero(t) - empty!(memory.data) - end - - n = length(memory.data) - i = round(Int, t / Δt) + 1 #expensive - if i == n + 1 - push!(memory.data, DiffEqBase.value(x)) - elseif i <= n - @inbounds memory.data[i] = DiffEqBase.value(x) - else - error("Memory buffer skipped a step: n=$n, i=$i") - end - - # memory.ref = Δt - - return x -end -Symbolics.@register_symbolic set_sampled_data!(memory, t, x, Δt) - -function Symbolics.derivative(::typeof(set_sampled_data!), args::NTuple{4, Any}, ::Val{2}) - memory = @inbounds args[1] - t = @inbounds args[2] - x = @inbounds args[3] - Δt = @inbounds args[4] - first_order_backwards_difference(t, x, Δt, memory) -end -Symbolics.derivative(::typeof(set_sampled_data!), args::NTuple{4, Any}, ::Val{3}) = 1 #set_sampled_data returns x, therefore d/dx (x) = 1 -function ChainRulesCore.frule((_, _, ṫ, ẋ, _), - ::typeof(set_sampled_data!), - memory, - t, - x, - Δt) - first_order_backwards_difference(t, x, Δt, memory) * ṫ + ẋ -end - -function first_order_backwards_difference(t, x, Δt, memory) - x1 = set_sampled_data!(memory, t, x, Δt) - x0 = get_sampled_data(t - Δt, memory) - - return (x1 - x0) / Δt -end +using DiffEqBase +import ChainRulesCore + +# Define and register smooth functions +# These are "smooth" aka differentiable and avoid Gibbs effect +# These follow: `offset` + `smooth_wave` * `smooth_step` with zero output for `t < start_time` +function smooth_cos(x, δ, f, amplitude, ϕ, offset, start_time) + offset + + amplitude * cos(2 * π * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_damped_sin(x, δ, f, amplitude, damping, ϕ, offset, start_time) + offset + + exp((start_time - x) * damping) * amplitude * sin(2 * π * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_ramp(x, δ, height, duration, offset, start_time) + offset + + height / (duration) * + (smooth_xH(x, δ, start_time) - smooth_xH(x, δ, start_time + duration)) +end + +function smooth_sin(x, δ, f, amplitude, ϕ, offset, start_time) + offset + + amplitude * sin(2 * pi * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_square(x, δ, f, amplitude, offset, start_time) + offset + + amplitude * 2atan(sin(2π * (x - start_time) * f) / δ) / π * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_step(x, δ, height, offset, start_time) + offset + height * (atan((x - start_time) / δ) / π + 0.5) +end + +function smooth_triangular(x, δ, f, amplitude, offset, start_time) + offset + + amplitude * (1 - 2acos((1 - δ)sin(2π * (x - start_time) * f)) / π) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_xH(x, δ, tₒ) + 0.5 * (x - tₒ) * (1 + ((x - tₒ) / sqrt((x - tₒ)^2 + δ^2))) +end + +function square(x, f, amplitude, offset, start_time) + offset + + (x > start_time) * (amplitude * + (4 * floor(f * (x - start_time)) - 2 * floor(2 * (x - start_time) * f) + 1)) +end + +function triangular(x, f, amplitude, offset, start_time) + p = 1 / f # period + offset + + (x > start_time) * + (4 * amplitude * f * abs(abs((x - p / 4 - start_time) % p) - p / 2) - amplitude) +end + +""" + Constant(; name, k = 0.0) + +Generate constant signal. + +# Parameters: + + - `k`: Constant output value + +# Connectors: + + - `output` +""" +@mtkmodel Constant begin + @components begin + output = RealOutput(; unit) + end + @parameters begin + k = 0.0, [description = "Constant output value of block"] + end + @equations begin + output.u ~ k + end +end + +""" + TimeVaryingFunction(f; name) + +Outputs ``f(t)``. + +The input variable `t` can be changed by passing a different variable as the keyword argument `t`. + +# Connectors: +- `output` +""" +@mtkmodel TimeVaryingFunction begin + @structural_parameters begin + f + end + @components begin + output = RealOutput(; unit) + end + @equations begin + output.u ~ f(t) + end +end +TimeVaryingFunction.f(f; name) = TimeVaryingFunction(; f, name) + +""" + Sine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, + smooth = false) + +Generate sine signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Sine(; name, + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase + equation = if smooth == false + offset + ifelse(t < start_time, 0, + amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_sin(t, smooth, frequency, amplitude, phase, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Cosine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, + smooth = false) + +Cosine signal. + +# Parameters: +- `frequency`: [Hz] Frequency of sine wave +- `amplitude`: Amplitude of sine wave +- `phase`: [rad] Phase of sine wave +- `offset`: Offset of output signal +- `start_time`: [s] Output `y = offset` for `t < start_time` +- `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: +- `output` +""" + +@component function Cosine(; name, + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase + equation = if smooth == false + offset + ifelse(t < start_time, zero(t), + amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_cos(t, smooth, frequency, amplitude, phase, offset, start_time) + end + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + ContinuousClock(; name, offset = 0, start_time = 0) + +Generate current time signal. + +# Parameters: + + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + +# Connectors: + + - `output` +""" +@component function ContinuousClock(; name, offset = 0, start_time = 0, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time + eqs = [ + output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" +Ramp(; name, height = 1, duration = 1, offset = 0, start_time = 0, smooth = false) + +Generate ramp signal. + +# Parameters: + + - `height`: Height of ramp + - `duration`: [s] Duration of ramp (= 0.0 gives a Step) + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Ramp(; name, + height = 1, + duration = 1, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time height=height duration=duration + equation = if smooth == false + offset + ifelse(t < start_time, 0, + ifelse(t < (start_time + duration), (t - start_time) * height / duration, + height)) + else + smooth === true && (smooth = 1e-5) + smooth_ramp(t, smooth, height, duration, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Square(; name, frequency = 1.0, amplitude = 1.0, offset = 0.0, start_time = 0.0, + smooth = false) +Generate smooth square signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Square(; name, frequency = 1.0, amplitude = 1.0, + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters begin + frequency = frequency + amplitude = amplitude + offset = offset + start_time = start_time + end + + equation = if smooth == false + square(t, frequency, amplitude, offset, start_time) + else + smooth === true && (smooth = 1e-5) + smooth_square(t, smooth, frequency, amplitude, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Step(;name, height=1, offset=0, start_time=0, duration=Inf, smooth=true) + +Generate step signal. + +# Parameters: + + - `height`: Height of step + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. + - `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. + - `smooth`: If `true`, returns a smooth wave. Defaults to `true` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, + smooth = 1e-5, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + duration_numeric = duration + pars = @parameters offset=offset start_time=start_time height=height duration=duration + equation = if smooth == false # use comparison in case smooth is a float + offset + ifelse((start_time < t) & (t < start_time + duration), height, 0) + else + smooth === true && (smooth = 1e-5) + if duration_numeric == Inf + smooth_step(t, smooth, height, offset, start_time) + else + smooth_step(t, smooth, height, offset, start_time) - + smooth_step(t, smooth, height, 0, start_time + duration) + end + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + ExpSine(; name, frequency, amplitude = 1, damping = 0.1, phase = 0, offset = 0, start_time = 0, smooth = false) + +Exponentially damped sine signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `damping`: [1/s] Damping coefficient of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function ExpSine(; name, + frequency, + amplitude = 1, + damping = 0.1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping + + equation = if smooth == false + offset + ifelse(t < start_time, 0, + amplitude * exp(-damping * (t - start_time)) * + sin(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_damped_sin(t, smooth, frequency, amplitude, damping, phase, offset, + start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Triangular(; name, amplitude = 1.0, frequency = 1.0, offset = 0.0, + start_time = 0.0, smooth = false) + +Generate smooth triangular signal for frequencies less than or equal to 25 Hz + +# Parameters: + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal. + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Triangular(; name, amplitude = 1.0, frequency = 1.0, + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters begin + amplitude = amplitude + frequency = frequency + offset = offset + start_time = start_time + end + + equation = if smooth == false + triangular(t, frequency, amplitude, offset, start_time) + else + smooth === true && (smooth = 1e-5) + smooth_triangular(t, smooth, frequency, amplitude, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +# TODO: +# - Exponentials Generate a rising and falling exponential signal +# - Pulse Generate pulse signal of type Real +# - SawTooth Generate saw tooth signal +# - Trapezoid Generate trapezoidal signal of type Real + + + +# SampledData Parameter struct ---------------- + +struct Parameter{T <: Real} + data::Vector{T} + ref::T + circular_buffer::Bool +end + +Parameter(data::Vector{T}, ref::T) where {T <: Real} = Parameter(data, ref, true) +Parameter(x::Parameter) = x +function Parameter(x::T; tofloat = true) where {T <: Real} + if tofloat + x = float(x) + P = typeof(x) + else + P = T + end + + return Parameter(P[], x) +end + +function Base.isequal(x::Parameter, y::Parameter) + b0 = length(x.data) == length(y.data) + if b0 + b1 = all(x.data .== y.data) + b2 = x.ref == y.ref + return b1 & b2 + else + return false + end +end + +Base.:*(x::Number, y::Parameter) = x * y.ref +Base.:*(y::Parameter, x::Number) = Base.:*(x, y) +Base.:*(x::Parameter, y::Parameter) = x.ref * y.ref + +Base.:/(x::Number, y::Parameter) = x / y.ref +Base.:/(y::Parameter, x::Number) = y.ref / x +Base.:/(x::Parameter, y::Parameter) = x.ref / y.ref + +Base.:+(x::Number, y::Parameter) = x + y.ref +Base.:+(y::Parameter, x::Number) = Base.:+(x, y) +Base.:+(x::Parameter, y::Parameter) = x.ref + y.ref + +Base.:-(y::Parameter) = -y.ref +Base.:-(x::Number, y::Parameter) = x - y.ref +Base.:-(y::Parameter, x::Number) = y.ref - x +Base.:-(x::Parameter, y::Parameter) = x.ref - y.ref + +Base.:^(x::Number, y::Parameter) = Base.:^(x, y.ref) +Base.:^(y::Parameter, x::Number) = Base.:^(y.ref, x) +Base.:^(x::Parameter, y::Parameter) = Base.:^(x.ref, y.ref) + +Base.isless(x::Parameter, y::Number) = Base.isless(x.ref, y) +Base.isless(y::Number, x::Parameter) = Base.isless(y, x.ref) + +Base.copy(x::Parameter{T}) where {T} = Parameter{T}(copy(x.data), x.ref) + +ifelse(c::Bool, x::Parameter, y::Parameter) = ifelse(c, x.ref, y.ref) +ifelse(c::Bool, x::Parameter, y::Number) = ifelse(c, x.ref, y) +ifelse(c::Bool, x::Number, y::Parameter) = ifelse(c, x, y.ref) + +Base.max(x::Number, y::Parameter) = max(x, y.ref) +Base.max(x::Parameter, y::Number) = max(x.ref, y) +Base.max(x::Parameter, y::Parameter) = max(x.ref, y.ref) + +Base.min(x::Number, y::Parameter) = min(x, y.ref) +Base.min(x::Parameter, y::Number) = min(x.ref, y) +Base.min(x::Parameter, y::Parameter) = min(x.ref, y.ref) + +function Base.show(io::IO, m::MIME"text/plain", p::Parameter) + if !isempty(p.data) + print(io, p.data) + else + print(io, p.ref) + end +end + +get_sample_time(memory::Parameter) = memory.ref +Symbolics.@register_symbolic get_sample_time(memory) + +Base.convert(::Type{T}, x::Parameter{T}) where {T <: Real} = x.ref +function Base.convert(::Type{<:Parameter{T}}, x::Number) where {T <: Real} + Parameter{T}(T[], x, true) +end + + + +# SampledData utilities ---------------- + +function linear_interpolation(x1::Real, x2::Real, t1::Real, t2::Real, t) + if t1 != t2 + slope = (x2 - x1) / (t2 - t1) + intercept = x1 - slope * t1 + + return slope * t + intercept + else + @assert x1==x2 "x1 ($x1) and x2 ($x2) should be equal if t1 == t2" + + return x2 + end +end + +function first_order_backwards_difference(t, memory) + Δt = get_sample_time(memory) + x1 = get_sampled_data(t, memory) + x0 = get_sampled_data(t - Δt, memory) + + return (x1 - x0) / Δt +end + +function first_order_backwards_difference(t, buffer, Δt, circular_buffer) + x1 = get_sampled_data(t , buffer, Δt, circular_buffer) + x0 = get_sampled_data(t - Δt, buffer, Δt, circular_buffer) + + return (x1 - x0) / Δt +end + + + +function get_sampled_data(t, buffer::Vector{<:Real}, dt::Real, circular_buffer=true) + if t < 0 + t = zero(t) + end + + if isempty(buffer) + if T <: AbstractFloat + return T(NaN) + else + return zero(T) + end + end + + i1 = floor(Int, t / dt) + 1 #expensive + i2 = i1 + 1 + + t1 = (i1 - 1) * dt + x1 = @inbounds buffer[i1] + + if t == t1 + return x1 + else + n = length(buffer) + + if circular_buffer + i1 = (i1 - 1) % n + 1 + i2 = (i2 - 1) % n + 1 + else + if i2 > n + i2 = n + i1 = i2 - 1 + end + end + + t2 = (i2 - 1) * dt + x2 = @inbounds buffer[i2] + return linear_interpolation(x1, x2, t1, t2, t) + end +end +get_sampled_data(t, buffer) = get_sampled_data(t, buffer.data, buffer.ref, buffer.circular_buffer) +Symbolics.@register_symbolic get_sampled_data(t, buffer) +Symbolics.@register_symbolic get_sampled_data(t, buffer, dt, circular_buffer) false + +function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{2, Any}, ::Val{1}) + t = @inbounds args[1] + buffer = @inbounds args[2] + first_order_backwards_difference(t, buffer) +end +function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer) + first_order_backwards_difference(t, buffer) * ẋ +end + +function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{4, Any}, ::Val{1}) + t = @inbounds args[1] + buffer = @inbounds args[2] + sample_time = @inbounds args[3] + circular_buffer = @inbounds args[4] + first_order_backwards_difference(t, buffer, sample_time, circular_buffer) +end +function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer, sample_time, circular_buffer) + first_order_backwards_difference(t, buffer, sample_time, circular_buffer) * ẋ +end + + + +# SampledData component ---------------- + +module SampledDataType +@enum Option vector_based struct_based +end + +""" + SampledData(; name, buffer, sample_time, circular_buffer=true) + +data input component. + +# Parameters: + - `buffer::Vector{Real}`: holds the data sampled at `sample_time` + - `sample_time::Real` + - `circular_buffer::Bool = true`: how to handle `t > length(buffer)*sample_time`. If true data is considered circular, otherwise last data point is held. + +# Connectors: + - `output` +""" +@component function SampledData(::Val{SampledDataType.vector_based}; name, buffer, sample_time, circular_buffer=true) + pars = @parameters begin + buffer = buffer #::Vector{Real} + sample_time = sample_time #::Real + circular_buffer = circular_buffer #::Bool + end + vars = [] + systems = @named begin + output = RealOutput() + end + eqs = [ + output.u ~ get_sampled_data(t, buffer, sample_time, circular_buffer), + ] + return ODESystem(eqs, t, vars, pars; name, systems, + defaults = [output.u => get_sampled_data(0.0, buffer, sample_time, circular_buffer)]) +end + +""" + SampledData(; name, buffer) + +data input component. + +# Parameters: + - `buffer`: a `Parameter` type which holds the data and sample time + +# Connectors: + - `output` +""" +@component function SampledData(::Val{SampledDataType.struct_based}; name, buffer, unit = nothing) + pars = @parameters begin + buffer = buffer #::Parameter + end + vars = [] + systems = @named begin + output = RealOutput(; unit) + end + eqs = [ + output.u ~ get_sampled_data(t, buffer), + ] + return ODESystem(eqs, t, vars, pars; name, systems, + defaults = [output.u => get_sampled_data(0.0, buffer)]) +end + +SampledData(x::SampledDataType.Option; kwargs...) = SampledData(Val(x); kwargs...) + +# struct_based +SampledData(T::Type, circular_buffer = true; name) = SampledData(SampledDataType.struct_based; name, buffer = Parameter(T[], zero(T), circular_buffer)) + +# vector_based +SampledData(sample_time::T, circular_buffer = true; name) where {T <: Real} = SampledData(SampledDataType.vector_based; name, buffer=T[], sample_time, circular_buffer) +SampledData(buffer::Vector{<:Real}, sample_time::Real, circular_buffer = true; name) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) +SampledData(; name, buffer, sample_time, circular_buffer) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) + + + + + + + + + + diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index d5ded6358..ecc54345f 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -1,784 +1,784 @@ - -""" - Cap(; p_int, name) - -Caps a hydraulic port to prevent mass flow in or out. - -# Parameters: -- `p_int`: [Pa] initial pressure (set by `p_int` argument) - -# Connectors: -- `port`: hydraulic port -""" -@component function Cap(; p_int, name) - pars = @parameters p_int = p_int - - vars = @variables p(t) = p_int - - systems = @named begin - port = HydraulicPort(; p_int = p_int) - end - - eqs = [port.p ~ p - port.dm ~ 0] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -@component function Open(; p_int, name) - pars = @parameters p_int = p_int - - vars = @variables begin - p(t) = p_int - dm(t) = 0 - end - - systems = @named begin - port = HydraulicPort(; p_int = p_int) - end - - eqs = [port.p ~ p - port.dm ~ dm] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - TubeBase(add_inertia = true; p_int, area, length_int, head_factor = 1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) - -Variable length internal flow model of the fully developed incompressible flow friction. Includes optional inertia term when `add_inertia = true` to model wave propagation. Hydraulic ports have equal flow but variable pressure. Density is averaged over the pressures, used to calculated average flow velocity and flow friction. - -# States: -- `x`: [m] length of the pipe -- `ddm`: [kg/s^2] Rate of change of mass flow rate in control volume. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `area`: [m^2] tube cross sectional area -- `length_int`: [m] initial tube length -- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -""" -@component function TubeBase(add_inertia = true, variable_length = true; p_int, area, - length_int, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) - pars = @parameters begin - p_int = p_int - area = area - length_int = length_int - perimeter = perimeter - shape_factor = shape_factor - head_factor = head_factor - end - - @variables begin - x(t) = length_int - ddm(t) = 0 - end - - vars = [] - if variable_length - push!(vars, x) - c = x - else - c = length_int - end - add_inertia && push!(vars, ddm) - - systems = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - end - - # let ---------------------- - Δp = port_a.p - port_b.p - dm = port_a.dm - - d_h = 4 * area / perimeter - - # Opting for a more numerically stable constant density (use head factor to compensate if needed) - ρ = density_ref(port_a) # (full_density(port_a) + full_density(port_b)) / 2 - μ = viscosity(port_a) - - f = friction_factor(dm, area, d_h, μ, shape_factor) - u = dm / (ρ * area) - - shear = (1 / 2) * ρ * regPow(u, 2) * f * head_factor * (c / d_h) - inertia = if add_inertia - (c / area) * ddm - else - 0 - end - - eqs = [0 ~ port_a.dm + port_b.dm] - - if variable_length - push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) - else - push!(eqs, Δp ~ shear + inertia) - end - - if add_inertia - push!(eqs, D(dm) ~ ddm) - end - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - Tube(N, add_inertia=true; p_int, area, length, head_factor=1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) - -Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `TubeBase`:`N-1`) which models the fully developed flow friction, compressibility (when `N>1`), and inertia effects when `add_inertia = true`. See `TubeBase` and `FixedVolume` for more information. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `area`: [m^2] tube cross sectional area -- `length`: [m] real length of the tube -- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -""" -@component function Tube(N, add_inertia = true; p_int, area, length, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) - @assert(N>0, - "the Tube component must be defined with at least 1 segment (i.e. N>0), found N=$N") - - if N == 1 - return TubeBase(add_inertia, - false; - shape_factor, - p_int, - area, - length_int = length, - head_factor, - perimeter, - name) - end - - #TODO: How to set an assert effective_length >= length ?? - pars = @parameters begin - p_int = p_int - area = area - length = length - head_factor = head_factor - perimeter = perimeter - shape_factor = shape_factor - end - - vars = [] - - ports = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - end - - pipe_bases = [] - for i in 1:(N - 1) - x = TubeBase(add_inertia; name = Symbol("p$i"), - shape_factor = ParentScope(shape_factor), - p_int = ParentScope(p_int), area = ParentScope(area), - length_int = ParentScope(length) / (N - 1), - head_factor = ParentScope(head_factor), - perimeter = ParentScope(perimeter)) - push!(pipe_bases, x) - end - - volumes = [] - for i in 1:N - x = FixedVolume(; name = Symbol("v$i"), - vol = ParentScope(area) * ParentScope(length) / N, - p_int = ParentScope(p_int)) - push!(volumes, x) - end - - eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a) - connect(volumes[end].port, pipe_bases[end].port_b, port_b)] - - for i in 2:(N - 1) - push!(eqs, - connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) - end - - for i in 1:(N - 1) - push!(eqs, pipe_bases[i].x ~ length / (N - 1)) - end - - return ODESystem(eqs, t, vars, pars; name, systems = [ports; pipe_bases; volumes]) -end -@deprecate Pipe Tube - -""" - FlowDivider(;p_int, n, name) - -Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel tubes efficiently by placing a `FlowDivider` on each end of a tube. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `n`: divide flow from `port_a` to `port_b` by `n` - -# Connectors: -- `port_a`: full flow hydraulic port -- `port_b`: part flow hydraulic port -""" -@component function FlowDivider(; p_int, n, name) - - #TODO: assert n >= 1 - - pars = @parameters begin - n = n - p_int = p_int - end - - vars = @variables begin - dm_a(t) = 0 - dm_b(t) = 0 - end - - systems = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - open = Open(; p_int) - end - - eqs = [connect(port_a, port_b, open.port) - dm_a ~ port_a.dm - dm_b ~ dm_a / n - open.dm ~ dm_a - dm_b # extra flow dumps into an open port - # port_b.dm ~ dm_b # divided flow goes to port_b - ] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -@component function ValveBase(reversible = false; p_a_int, p_b_int, minimum_area = 0, - area_int, Cd, Cd_reverse = Cd, name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_int = area_int - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - end - - vars = @variables begin - area(t) = area_int - y(t) = area_int - end - - # let - # Opting for a more numerically stable constant density (use head factor to compensate if needed) - ρ = density_ref(port_a) #(full_density(port_a) + full_density(port_b)) / 2 - - x = if reversible - area - else - ifelse(area > minimum_area, area, minimum_area) - end - - # let ------ - Δp = port_a.p - port_b.p - dm = port_a.dm - c = if reversible - Cd - else - ifelse(Δp > 0, Cd, Cd_reverse) - end - - eqs = [0 ~ port_a.dm + port_b.dm - dm ~ regRoot(2 * Δp * ρ / c) * x - y ~ x] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - Valve(reversible = false; p_a_int, p_b_int, area_int, Cd, Cd_reverse = Cd, minimum_area = 0, name) - -Valve with `area` input and discharge coefficient `Cd` defined by https://en.wikipedia.org/wiki/Discharge_coefficient. The `Cd_reverse` parameter allows for directional flow restriction, making it possible to define a check valve. - -# Parameters: -- `p_a_int`: [Pa] initial pressure for `port_a` -- `p_b_int`: [Pa] initial pressure for `port_b` -- `area_int`: [m^2] initial valve opening -- `Cd`: discharge coefficient flowing from `a → b` -- `Cd_reverse`: discharge coefficient flowing from `b → a` -- `minimum_area`: when `reversible = false` applies a forced minimum area - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -- `area`: real input setting the valve `area`. When `reversible = true`, negative input reverses flow direction, otherwise a floor of `minimum_area` is enforced. -""" -@component function Valve(reversible = false; p_a_int, p_b_int, - area_int, Cd, Cd_reverse = Cd, - minimum_area = 0, - name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_int = area_int - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - area = RealInput() - base = ValveBase(reversible; p_a_int, p_b_int, area_int, Cd, Cd_reverse, - minimum_area) - end - - vars = [] - - eqs = [connect(base.port_a, port_a) - connect(base.port_b, port_b) - base.area ~ area.u] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [area.u => area_int]) -end - -@component function VolumeBase(; p_int, x_int = 0, area, dead_volume = 0, Χ1 = 1, Χ2 = 1, - name) - pars = @parameters begin - p_int = p_int - x_int = x_int - area = area - dead_volume = dead_volume - end - - systems = @named begin - port = HydraulicPort(; p_int) - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - rho(t) = liquid_density(port) - drho(t) = 0 - vol(t) = dead_volume + area * x_int - end - - # let - dm = port.dm - p = port.p - - eqs = [vol ~ dead_volume + area * x - D(x) ~ dx - D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - FixedVolume(; vol, p_int, name) - -Fixed fluid volume. - -# Parameters: -- `vol`: [m^3] fixed volume -- `p_int`: [Pa] initial pressure - -# Connectors: -- `port`: hydraulic port -""" -@component function FixedVolume(; vol, p_int, name) - pars = @parameters begin - p_int = p_int - vol = vol - end - - systems = @named begin - port = HydraulicPort(; p_int) - end - - vars = @variables begin - rho(t) = liquid_density(port) - drho(t) = 0 - end - - # let - dm = port.dm - p = port.p - - eqs = [D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" -DynamicVolume(N, add_inertia=true; p_int, area, x_int = 0, x_max, x_min = 0, x_damp = x_min, direction = +1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, head_factor = 1, Cd = 1e2, Cd_reverse = Cd, name) - -Volume with moving wall with `flange` connector for converting hydraulic energy to 1D mechanical. The `direction` argument aligns the mechanical port with the hydraulic port, useful when connecting two dynamic volumes together in oppsing directions to create an actuator. - -``` - ┌─────────────────┐ ─── - │ │ ▲ - │ │ -dm ────► │ │ area - │ │ - │ │ ▼ - └─────────────────┤ ─── - │ - └─► x (= ∫ flange.v * direction) -``` - -# Features: -- volume discretization with flow resistance and inertia: use `N` to control number of volume and resistance elements. Set `N=0` to turn off volume discretization. See `TubeBase` for more information about flow resistance. -- minimum volume flow shutoff with damping and directional resistance. Use `reversible=false` when problem defines volume position `x` and solves for `dm` to prevent numerical instability. - -# Parameters: -## volume -- `p_int`: [Pa] initial pressure -- `area`: [m^2] moving wall area -- `x_int`: [m] initial wall position -- `x_max`: [m] max wall position, needed for volume discretization to apply the correct volume sizing as a function of `x` -- `x_min`: [m] wall position that shuts off flow and prevents negative volume. -- `x_damp`: [m] wall position that initiates a linear damping region before reaching full flow shut off. Helps provide a smooth end stop. - -- `direction`: [+/-1] applies the direction conversion from the `flange` to `x` - -## flow resistance -- `perimeter`: [m] perimeter of the cross section (needed only for non-circular volumes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -## flow shut off and damping -- `Cd`: discharge coefficient for flow out of the volume. *Note: area is 1m² when valve is fully open. Ensure this does not induce unwanted flow resistance.* -- `Cd_reverse`: discharge coefficient for flow into the volume. Use a lower value to allow easy wall release, in some cases the wall can "stick". - - -# Connectors: -- `port`: hydraulic port -- `flange`: mechanical translational port -""" -@component function DynamicVolume(N, add_inertia = true, reversible = false; - p_int, - area, - x_int = 0, - x_max, - x_min = 0, - x_damp = x_min, - direction = +1, - - # Tube - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, - head_factor = 1, - - # Valve - Cd = 1e2, - Cd_reverse = Cd, - minimum_area = 0, - name) - @assert(N>=0, - "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") - @assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction" - - #TODO: How to set an assert effective_length >= length ?? - pars = @parameters begin - p_int = p_int - area = area - - x_int = x_int - x_max = x_max - x_min = x_min - x_damp = x_damp - - direction = direction - - perimeter = perimeter - shape_factor = shape_factor - head_factor = head_factor - - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - vars = @variables x(t)=x_int vol(t)=x_int * area - - ports = @named begin - port = HydraulicPort(; p_int) - flange = MechanicalPort(; f = -direction * p_int * area) - damper = ValveBase(reversible; - p_a_int = p_int, - p_b_int = p_int, - area_int = 1, - Cd, - Cd_reverse, - minimum_area) - end - - pipe_bases = [] - for i in 1:N - comp = TubeBase(add_inertia; name = Symbol("p$i"), - shape_factor = ParentScope(shape_factor), - p_int = ParentScope(p_int), area = ParentScope(area), - length_int = 0, #set in equations - head_factor = ParentScope(head_factor), - perimeter = ParentScope(perimeter)) - push!(pipe_bases, comp) - end - - #TODO: How to handle x_int? - #TODO: Handle direction - @named moving_volume = VolumeBase(; - p_int, - x_int = 0, - area, - dead_volume = N == 0 ? area * x_max : 0, - Χ1 = N == 0 ? 1 : 0, - Χ2 = 1) - - ratio = (x - x_min) / (x_damp - x_min) - - damper_area = if reversible - one(x) - else - ifelse(x >= x_damp, one(x), ifelse((x < x_damp) & (x > x_min), ratio, zero(x))) - end - - eqs = [vol ~ x * area - D(x) ~ flange.v * direction - damper.area ~ damper_area - connect(port, damper.port_b)] - - volumes = [] - if N > 0 - Δx = ParentScope(x_max) / N - x₀ = ParentScope(x_int) - - for i in 1:N - length = ifelse(x₀ > Δx * i, - Δx, - ifelse(x₀ - Δx * (i - 1) > 0, - x₀ - Δx * (i - 1), - zero(Δx))) - - comp = VolumeBase(; name = Symbol("v$i"), p_int = ParentScope(p_int), - x_int = 0, - area = ParentScope(area), - dead_volume = ParentScope(area) * length, Χ1 = 1, Χ2 = 0) - - push!(volumes, comp) - end - - push!(eqs, connect(moving_volume.port, volumes[1].port, pipe_bases[1].port_a)) - push!(eqs, connect(pipe_bases[end].port_b, damper.port_a)) - for i in 2:N - push!(eqs, - connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) - end - - for i in 1:N - push!(eqs, - volumes[i].dx ~ ifelse((vol >= (i - 1) * (x_max / N) * area) & - (vol < (i) * (x_max / N) * area), - flange.v * direction, 0)) - push!(eqs, pipe_bases[i].x ~ volumes[i].vol / volumes[i].area) - end - else - push!(eqs, connect(moving_volume.port, damper.port_a)) - end - - push!(eqs, moving_volume.dx ~ flange.v * direction) - push!(eqs, -moving_volume.port.p * area * direction ~ flange.f) - - ODESystem(eqs, t, vars, pars; name, - systems = [ports; pipe_bases; volumes; moving_volume], - defaults = [flange.v => 0]) -end - -@component function SpoolValve(reversible = false; p_a_int, p_b_int, x_int, Cd, d, name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - d = d - x_int = x_int - Cd = Cd - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - flange = MechanicalPort() - valve = ValveBase(reversible; p_a_int, p_b_int, - area_int = ParentScope(x_int) * 2π * ParentScope(d), Cd) - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - end - - eqs = [D(x) ~ dx - flange.v ~ dx - flange.f ~ 0 #TODO: model flow force - connect(valve.port_a, port_a) - connect(valve.port_b, port_b) - valve.area ~ x * 2π * d] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end - -@component function SpoolValve2Way(reversible = false; p_s_int, p_a_int, p_b_int, p_r_int, - m, g, x_int, Cd, d, name) - pars = @parameters begin - p_s_int = p_s_int - p_a_int = p_a_int - p_b_int = p_b_int - p_r_int = p_r_int - - m = m - g = g - - x_int = x_int - - d = d - - Cd = Cd - end - - vars = [] - - systems = @named begin - vSA = SpoolValve(reversible; p_a_int = p_s_int, p_b_int = p_a_int, x_int, Cd, d) - vBR = SpoolValve(reversible; p_a_int = p_b_int, p_b_int = p_r_int, x_int, Cd, d) - - port_s = HydraulicPort(; p_int = p_s_int) - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - port_r = HydraulicPort(; p_int = p_r_int) - - mass = Mass(; m = m, g = g) - - flange = MechanicalPort() - end - - eqs = [connect(vSA.port_a, port_s) - connect(vSA.port_b, port_a) - connect(vBR.port_a, port_b) - connect(vBR.port_b, port_r) - connect(vSA.flange, vBR.flange, mass.flange, flange)] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end - -@component function Actuator(N, add_inertia = true, reversible = false; - p_a_int, - p_b_int, - area_a, - area_b, - perimeter_a = 2 * sqrt(area_a * pi), - perimeter_b = 2 * sqrt(area_b * pi), - length_a_int, - length_b_int, - shape_factor_a = 64, - shape_factor_b = 64, - head_factor_a = 1, - head_factor_b = 1, - m, - g, - x_int = 0, - minimum_volume_a = 0, - minimum_volume_b = 0, - damping_volume_a = minimum_volume_a, - damping_volume_b = minimum_volume_b, - Cd = 1e4, - Cd_reverse = Cd, - name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_a = area_a - area_b = area_b - perimeter_a = perimeter_a - perimeter_b = perimeter_b - shape_factor_a = shape_factor_a - shape_factor_b = shape_factor_b - head_factor_a = head_factor_a - head_factor_b = head_factor_b - x_int = x_int - length_a_int = length_a_int - length_b_int = length_b_int - minimum_volume_a = minimum_volume_a - minimum_volume_b = minimum_volume_b - damping_volume_a = damping_volume_a - damping_volume_b = damping_volume_b - Cd = Cd - Cd_reverse = Cd_reverse - m = m - g = g - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - end - - total_length = length_a_int + length_b_int - - #TODO: include effective_length - systems = @named begin - vol_a = DynamicVolume(N, add_inertia, reversible; direction = +1, - p_int = p_a_int, - area = area_a, - x_int = length_a_int, - x_max = total_length, - x_min = minimum_volume_a / area_a, - x_damp = damping_volume_a / area_a, - perimeter = perimeter_a, - shape_factor = shape_factor_a, - head_factor = head_factor_a, - Cd, - Cd_reverse) - - vol_b = DynamicVolume(N, add_inertia, reversible; direction = -1, - p_int = p_b_int, - area = area_b, - x_int = length_b_int, - x_max = total_length, - x_min = minimum_volume_b / area_b, - x_damp = damping_volume_b / area_b, - perimeter = perimeter_b, - shape_factor = shape_factor_b, - head_factor = head_factor_b, - Cd, - Cd_reverse) - mass = Mass(; m, g) - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - flange = MechanicalPort() - end - - eqs = [connect(vol_a.port, port_a) - connect(vol_b.port, port_b) - connect(vol_a.flange, vol_b.flange, mass.flange, flange) - D(x) ~ dx - dx ~ vol_a.flange.v] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end + +""" + Cap(; p_int, name) + +Caps a hydraulic port to prevent mass flow in or out. + +# Parameters: +- `p_int`: [Pa] initial pressure (set by `p_int` argument) + +# Connectors: +- `port`: hydraulic port +""" +@component function Cap(; p_int, name) + pars = @parameters p_int = p_int + + vars = @variables p(t) = p_int + + systems = @named begin + port = HydraulicPort(; p_int = p_int) + end + + eqs = [port.p ~ p + port.dm ~ 0] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +@component function Open(; p_int, name) + pars = @parameters p_int = p_int + + vars = @variables begin + p(t) = p_int + dm(t) = 0 + end + + systems = @named begin + port = HydraulicPort(; p_int = p_int) + end + + eqs = [port.p ~ p + port.dm ~ dm] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + TubeBase(add_inertia = true; p_int, area, length_int, head_factor = 1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) + +Variable length internal flow model of the fully developed incompressible flow friction. Includes optional inertia term when `add_inertia = true` to model wave propagation. Hydraulic ports have equal flow but variable pressure. Density is averaged over the pressures, used to calculated average flow velocity and flow friction. + +# States: +- `x`: [m] length of the pipe +- `ddm`: [kg/s^2] Rate of change of mass flow rate in control volume. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `area`: [m^2] tube cross sectional area +- `length_int`: [m] initial tube length +- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +""" +@component function TubeBase(add_inertia = true, variable_length = true; p_int, area, + length_int, head_factor = 1, + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) + pars = @parameters begin + p_int = p_int + area = area + length_int = length_int + perimeter = perimeter + shape_factor = shape_factor + head_factor = head_factor + end + + @variables begin + x(t) = length_int + ddm(t) = 0 + end + + vars = [] + if variable_length + push!(vars, x) + c = x + else + c = length_int + end + add_inertia && push!(vars, ddm) + + systems = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + end + + # let ---------------------- + Δp = port_a.p - port_b.p + dm = port_a.dm + + d_h = 4 * area / perimeter + + # Opting for a more numerically stable constant density (use head factor to compensate if needed) + ρ = density_ref(port_a) # (full_density(port_a) + full_density(port_b)) / 2 + μ = viscosity(port_a) + + f = friction_factor(dm, area, d_h, μ, shape_factor) + u = dm / (ρ * area) + + shear = (1 / 2) * ρ * regPow(u, 2) * f * head_factor * (c / d_h) + inertia = if add_inertia + (c / area) * ddm + else + 0 + end + + eqs = [0 ~ port_a.dm + port_b.dm] + + if variable_length + push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) + else + push!(eqs, Δp ~ shear + inertia) + end + + if add_inertia + push!(eqs, D(dm) ~ ddm) + end + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + Tube(N, add_inertia=true; p_int, area, length, head_factor=1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) + +Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `TubeBase`:`N-1`) which models the fully developed flow friction, compressibility (when `N>1`), and inertia effects when `add_inertia = true`. See `TubeBase` and `FixedVolume` for more information. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `area`: [m^2] tube cross sectional area +- `length`: [m] real length of the tube +- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +""" +@component function Tube(N, add_inertia = true; p_int, area, length, head_factor = 1, + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) + @assert(N>0, + "the Tube component must be defined with at least 1 segment (i.e. N>0), found N=$N") + + if N == 1 + return TubeBase(add_inertia, + false; + shape_factor, + p_int, + area, + length_int = length, + head_factor, + perimeter, + name) + end + + #TODO: How to set an assert effective_length >= length ?? + pars = @parameters begin + p_int = p_int + area = area + length = length + head_factor = head_factor + perimeter = perimeter + shape_factor = shape_factor + end + + vars = [] + + ports = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + end + + pipe_bases = [] + for i in 1:(N - 1) + x = TubeBase(add_inertia; name = Symbol("p$i"), + shape_factor = ParentScope(shape_factor), + p_int = ParentScope(p_int), area = ParentScope(area), + length_int = ParentScope(length) / (N - 1), + head_factor = ParentScope(head_factor), + perimeter = ParentScope(perimeter)) + push!(pipe_bases, x) + end + + volumes = [] + for i in 1:N + x = FixedVolume(; name = Symbol("v$i"), + vol = ParentScope(area) * ParentScope(length) / N, + p_int = ParentScope(p_int)) + push!(volumes, x) + end + + eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a) + connect(volumes[end].port, pipe_bases[end].port_b, port_b)] + + for i in 2:(N - 1) + push!(eqs, + connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) + end + + for i in 1:(N - 1) + push!(eqs, pipe_bases[i].x ~ length / (N - 1)) + end + + return ODESystem(eqs, t, vars, pars; name, systems = [ports; pipe_bases; volumes]) +end +@deprecate Pipe Tube + +""" + FlowDivider(;p_int, n, name) + +Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel tubes efficiently by placing a `FlowDivider` on each end of a tube. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `n`: divide flow from `port_a` to `port_b` by `n` + +# Connectors: +- `port_a`: full flow hydraulic port +- `port_b`: part flow hydraulic port +""" +@component function FlowDivider(; p_int, n, name) + + #TODO: assert n >= 1 + + pars = @parameters begin + n = n + p_int = p_int + end + + vars = @variables begin + dm_a(t) = 0 + dm_b(t) = 0 + end + + systems = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + open = Open(; p_int) + end + + eqs = [connect(port_a, port_b, open.port) + dm_a ~ port_a.dm + dm_b ~ dm_a / n + open.dm ~ dm_a - dm_b # extra flow dumps into an open port + # port_b.dm ~ dm_b # divided flow goes to port_b + ] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +@component function ValveBase(reversible = false; p_a_int, p_b_int, minimum_area = 0, + area_int, Cd, Cd_reverse = Cd, name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_int = area_int + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + end + + vars = @variables begin + area(t) = area_int + y(t) = area_int + end + + # let + # Opting for a more numerically stable constant density (use head factor to compensate if needed) + ρ = density_ref(port_a) #(full_density(port_a) + full_density(port_b)) / 2 + + x = if reversible + area + else + ifelse(area > minimum_area, area, minimum_area) + end + + # let ------ + Δp = port_a.p - port_b.p + dm = port_a.dm + c = if reversible + Cd + else + ifelse(Δp > 0, Cd, Cd_reverse) + end + + eqs = [0 ~ port_a.dm + port_b.dm + dm ~ regRoot(2 * Δp * ρ / c) * x + y ~ x] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + Valve(reversible = false; p_a_int, p_b_int, area_int, Cd, Cd_reverse = Cd, minimum_area = 0, name) + +Valve with `area` input and discharge coefficient `Cd` defined by https://en.wikipedia.org/wiki/Discharge_coefficient. The `Cd_reverse` parameter allows for directional flow restriction, making it possible to define a check valve. + +# Parameters: +- `p_a_int`: [Pa] initial pressure for `port_a` +- `p_b_int`: [Pa] initial pressure for `port_b` +- `area_int`: [m^2] initial valve opening +- `Cd`: discharge coefficient flowing from `a → b` +- `Cd_reverse`: discharge coefficient flowing from `b → a` +- `minimum_area`: when `reversible = false` applies a forced minimum area + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +- `area`: real input setting the valve `area`. When `reversible = true`, negative input reverses flow direction, otherwise a floor of `minimum_area` is enforced. +""" +@component function Valve(reversible = false; p_a_int, p_b_int, + area_int, Cd, Cd_reverse = Cd, + minimum_area = 0, + name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_int = area_int + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + area = RealInput() + base = ValveBase(reversible; p_a_int, p_b_int, area_int, Cd, Cd_reverse, + minimum_area) + end + + vars = [] + + eqs = [connect(base.port_a, port_a) + connect(base.port_b, port_b) + base.area ~ area.u] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [area.u => area_int]) +end + +@component function VolumeBase(; p_int, x_int = 0, area, dead_volume = 0, Χ1 = 1, Χ2 = 1, + name) + pars = @parameters begin + p_int = p_int + x_int = x_int + area = area + dead_volume = dead_volume + end + + systems = @named begin + port = HydraulicPort(; p_int) + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + rho(t) = liquid_density(port) + drho(t) = 0 + vol(t) = dead_volume + area * x_int + end + + # let + dm = port.dm + p = port.p + + eqs = [vol ~ dead_volume + area * x + D(x) ~ dx + D(rho) ~ drho + rho ~ full_density(port, p) + dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + FixedVolume(; vol, p_int, name) + +Fixed fluid volume. + +# Parameters: +- `vol`: [m^3] fixed volume +- `p_int`: [Pa] initial pressure + +# Connectors: +- `port`: hydraulic port +""" +@component function FixedVolume(; vol, p_int, name) + pars = @parameters begin + p_int = p_int + vol = vol + end + + systems = @named begin + port = HydraulicPort(; p_int) + end + + vars = @variables begin + rho(t) = liquid_density(port) + drho(t) = 0 + end + + # let + dm = port.dm + p = port.p + + eqs = [D(rho) ~ drho + rho ~ full_density(port, p) + dm ~ drho * vol] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" +DynamicVolume(N, add_inertia=true; p_int, area, x_int = 0, x_max, x_min = 0, x_damp = x_min, direction = +1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, head_factor = 1, Cd = 1e2, Cd_reverse = Cd, name) + +Volume with moving wall with `flange` connector for converting hydraulic energy to 1D mechanical. The `direction` argument aligns the mechanical port with the hydraulic port, useful when connecting two dynamic volumes together in oppsing directions to create an actuator. + +``` + ┌─────────────────┐ ─── + │ │ ▲ + │ │ +dm ────► │ │ area + │ │ + │ │ ▼ + └─────────────────┤ ─── + │ + └─► x (= ∫ flange.v * direction) +``` + +# Features: +- volume discretization with flow resistance and inertia: use `N` to control number of volume and resistance elements. Set `N=0` to turn off volume discretization. See `TubeBase` for more information about flow resistance. +- minimum volume flow shutoff with damping and directional resistance. Use `reversible=false` when problem defines volume position `x` and solves for `dm` to prevent numerical instability. + +# Parameters: +## volume +- `p_int`: [Pa] initial pressure +- `area`: [m^2] moving wall area +- `x_int`: [m] initial wall position +- `x_max`: [m] max wall position, needed for volume discretization to apply the correct volume sizing as a function of `x` +- `x_min`: [m] wall position that shuts off flow and prevents negative volume. +- `x_damp`: [m] wall position that initiates a linear damping region before reaching full flow shut off. Helps provide a smooth end stop. + +- `direction`: [+/-1] applies the direction conversion from the `flange` to `x` + +## flow resistance +- `perimeter`: [m] perimeter of the cross section (needed only for non-circular volumes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +## flow shut off and damping +- `Cd`: discharge coefficient for flow out of the volume. *Note: area is 1m² when valve is fully open. Ensure this does not induce unwanted flow resistance.* +- `Cd_reverse`: discharge coefficient for flow into the volume. Use a lower value to allow easy wall release, in some cases the wall can "stick". + + +# Connectors: +- `port`: hydraulic port +- `flange`: mechanical translational port +""" +@component function DynamicVolume(N, add_inertia = true, reversible = false; + p_int, + area, + x_int = 0, + x_max, + x_min = 0, + x_damp = x_min, + direction = +1, + + # Tube + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, + head_factor = 1, + + # Valve + Cd = 1e2, + Cd_reverse = Cd, + minimum_area = 0, + name) + @assert(N>=0, + "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") + @assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction" + + #TODO: How to set an assert effective_length >= length ?? + pars = @parameters begin + p_int = p_int + area = area + + x_int = x_int + x_max = x_max + x_min = x_min + x_damp = x_damp + + direction = direction + + perimeter = perimeter + shape_factor = shape_factor + head_factor = head_factor + + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + vars = @variables x(t)=x_int vol(t)=x_int * area + + ports = @named begin + port = HydraulicPort(; p_int) + flange = MechanicalPort(; f = -direction * p_int * area) + damper = ValveBase(reversible; + p_a_int = p_int, + p_b_int = p_int, + area_int = 1, + Cd, + Cd_reverse, + minimum_area) + end + + pipe_bases = [] + for i in 1:N + comp = TubeBase(add_inertia; name = Symbol("p$i"), + shape_factor = ParentScope(shape_factor), + p_int = ParentScope(p_int), area = ParentScope(area), + length_int = 0, #set in equations + head_factor = ParentScope(head_factor), + perimeter = ParentScope(perimeter)) + push!(pipe_bases, comp) + end + + #TODO: How to handle x_int? + #TODO: Handle direction + @named moving_volume = VolumeBase(; + p_int, + x_int = 0, + area, + dead_volume = N == 0 ? area * x_int : 0, + Χ1 = N == 0 ? 1 : 0, + Χ2 = 1) + + ratio = (x - x_min) / (x_damp - x_min) + + damper_area = if reversible + one(x) + else + ifelse(x >= x_damp, one(x), ifelse((x < x_damp) & (x > x_min), ratio, zero(x))) + end + + eqs = [vol ~ x * area + D(x) ~ flange.v * direction + damper.area ~ damper_area + connect(port, damper.port_b)] + + volumes = [] + if N > 0 + Δx = ParentScope(x_max) / N + x₀ = ParentScope(x_int) + + for i in 1:N + length = ifelse(x₀ > Δx * i, + Δx, + ifelse(x₀ - Δx * (i - 1) > 0, + x₀ - Δx * (i - 1), + zero(Δx))) + + comp = VolumeBase(; name = Symbol("v$i"), p_int = ParentScope(p_int), + x_int = 0, + area = ParentScope(area), + dead_volume = ParentScope(area) * length, Χ1 = 1, Χ2 = 0) + + push!(volumes, comp) + end + + push!(eqs, connect(moving_volume.port, volumes[1].port, pipe_bases[1].port_a)) + push!(eqs, connect(pipe_bases[end].port_b, damper.port_a)) + for i in 2:N + push!(eqs, + connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) + end + + for i in 1:N + push!(eqs, + volumes[i].dx ~ ifelse((vol >= (i - 1) * (x_max / N) * area) & + (vol < (i) * (x_max / N) * area), + flange.v * direction, 0)) + push!(eqs, pipe_bases[i].x ~ volumes[i].vol / volumes[i].area) + end + else + push!(eqs, connect(moving_volume.port, damper.port_a)) + end + + push!(eqs, moving_volume.dx ~ flange.v * direction) + push!(eqs, -moving_volume.port.p * area * direction ~ flange.f) + + ODESystem(eqs, t, vars, pars; name, + systems = [ports; pipe_bases; volumes; moving_volume], + defaults = [flange.v => 0]) +end + +@component function SpoolValve(reversible = false; p_a_int, p_b_int, x_int, Cd, d, name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + d = d + x_int = x_int + Cd = Cd + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + flange = MechanicalPort() + valve = ValveBase(reversible; p_a_int, p_b_int, + area_int = ParentScope(x_int) * 2π * ParentScope(d), Cd) + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + end + + eqs = [D(x) ~ dx + flange.v ~ dx + flange.f ~ 0 #TODO: model flow force + connect(valve.port_a, port_a) + connect(valve.port_b, port_b) + valve.area ~ x * 2π * d] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end + +@component function SpoolValve2Way(reversible = false; p_s_int, p_a_int, p_b_int, p_r_int, + m, g, x_int, Cd, d, name) + pars = @parameters begin + p_s_int = p_s_int + p_a_int = p_a_int + p_b_int = p_b_int + p_r_int = p_r_int + + m = m + g = g + + x_int = x_int + + d = d + + Cd = Cd + end + + vars = [] + + systems = @named begin + vSA = SpoolValve(reversible; p_a_int = p_s_int, p_b_int = p_a_int, x_int, Cd, d) + vBR = SpoolValve(reversible; p_a_int = p_b_int, p_b_int = p_r_int, x_int, Cd, d) + + port_s = HydraulicPort(; p_int = p_s_int) + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + port_r = HydraulicPort(; p_int = p_r_int) + + mass = Mass(; m = m, g = g) + + flange = MechanicalPort() + end + + eqs = [connect(vSA.port_a, port_s) + connect(vSA.port_b, port_a) + connect(vBR.port_a, port_b) + connect(vBR.port_b, port_r) + connect(vSA.flange, vBR.flange, mass.flange, flange)] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end + +@component function Actuator(N, add_inertia = true, reversible = false; + p_a_int, + p_b_int, + area_a, + area_b, + perimeter_a = 2 * sqrt(area_a * pi), + perimeter_b = 2 * sqrt(area_b * pi), + length_a_int, + length_b_int, + shape_factor_a = 64, + shape_factor_b = 64, + head_factor_a = 1, + head_factor_b = 1, + m, + g, + x_int = 0, + minimum_volume_a = 0, + minimum_volume_b = 0, + damping_volume_a = minimum_volume_a, + damping_volume_b = minimum_volume_b, + Cd = 1e4, + Cd_reverse = Cd, + name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_a = area_a + area_b = area_b + perimeter_a = perimeter_a + perimeter_b = perimeter_b + shape_factor_a = shape_factor_a + shape_factor_b = shape_factor_b + head_factor_a = head_factor_a + head_factor_b = head_factor_b + x_int = x_int + length_a_int = length_a_int + length_b_int = length_b_int + minimum_volume_a = minimum_volume_a + minimum_volume_b = minimum_volume_b + damping_volume_a = damping_volume_a + damping_volume_b = damping_volume_b + Cd = Cd + Cd_reverse = Cd_reverse + m = m + g = g + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + end + + total_length = length_a_int + length_b_int + + #TODO: include effective_length + systems = @named begin + vol_a = DynamicVolume(N, add_inertia, reversible; direction = +1, + p_int = p_a_int, + area = area_a, + x_int = length_a_int, + x_max = total_length, + x_min = minimum_volume_a / area_a, + x_damp = damping_volume_a / area_a, + perimeter = perimeter_a, + shape_factor = shape_factor_a, + head_factor = head_factor_a, + Cd, + Cd_reverse) + + vol_b = DynamicVolume(N, add_inertia, reversible; direction = -1, + p_int = p_b_int, + area = area_b, + x_int = length_b_int, + x_max = total_length, + x_min = minimum_volume_b / area_b, + x_damp = damping_volume_b / area_b, + perimeter = perimeter_b, + shape_factor = shape_factor_b, + head_factor = head_factor_b, + Cd, + Cd_reverse) + mass = Mass(; m, g) + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + flange = MechanicalPort() + end + + eqs = [connect(vol_a.port, port_a) + connect(vol_b.port, port_b) + connect(vol_a.flange, vol_b.flange, mass.flange, flange) + D(x) ~ dx + dx ~ vol_a.flange.v] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end diff --git a/test/Blocks/sources.jl b/test/Blocks/sources.jl index 4bd864ba0..90e8e58aa 100644 --- a/test/Blocks/sources.jl +++ b/test/Blocks/sources.jl @@ -1,446 +1,470 @@ -using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq -using ModelingToolkitStandardLibrary.Blocks -using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, - smooth_square, smooth_step, smooth_ramp, - smooth_triangular, triangular, square -using OrdinaryDiffEq: ReturnCode.Success -using DynamicQuantities: @u_str - -@parameters t [unit = u"s"] -D = Differential(t) - -@testset "Constant" begin - @named src = Constant(k = 2) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u][end]≈2 atol=1e-3 -end - -@testset "TimeVaryingFunction" begin - f(t) = t^2 + 1 - vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 - @named src = TimeVaryingFunction(f) - @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈f.(sol.t) atol=1e-3 - @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10 atol=1e-3 # closed-form solution to integral -end - -@testset "Sine" begin - function sine(t, frequency, amplitude, phase, offset, start_time) - offset + ifelse(t < start_time, 0, - amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency = 1 - amplitude = 2 - phase = 0 - offset = 1 - start_time = 2 - δ = 1e-5 - @named int = Integrator() - - @named src = Sine(frequency = frequency, amplitude = amplitude, phase = phase, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈sine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 - - @named smooth_src = Sine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_sin.(smooth_sol.t, δ, frequency, amplitude, - phase, offset, start_time) atol=1e-3 -end - -@testset "Cosine" begin - function cosine(t, frequency, amplitude, phase, offset, start_time) - offset + ifelse(t < start_time, 0, - amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency = 1 - amplitude = 2 - phase = 0 - offset = 1 - start_time = 2 - δ = 1e-5 - @named int = Integrator() - - @named src = Cosine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈cosine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 - - @named smooth_src = Cosine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_cos.(smooth_sol.t, δ, frequency, amplitude, - phase, offset, start_time) atol=1e-3 -end - -@testset "ContinuousClock" begin - cont_clock(t, offset, start_time) = offset + ifelse(t < start_time, 0, t - start_time) - - offset, start_time = 1, 0 - - @named src = ContinuousClock(offset = offset, start_time = start_time) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈cont_clock.(sol.t, offset, start_time) atol=1e-3 -end - -@testset "Ramp" begin - function ramp(t, offset, height, duration, start_time) - offset + ifelse(t < start_time, 0, - ifelse(t < (start_time + duration), (t - start_time) * height / duration, - height)) - end - - offset, height, duration, start_time, δ = 1, 2, 2, 0, 1e-5 - @named int = Integrator() - - @named src = Ramp(offset = offset, height = height, duration = duration, - start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈ramp.(sol.t, offset, height, duration, start_time) atol=1e-3 - - start_time = 2 - @named smooth_src = Ramp(offset = offset, height = height, duration = duration, - start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_ramp.(smooth_sol.t, δ, height, duration, - offset, start_time) atol=1e-3 -end - -@testset "Step" begin - step(t, offset, height, start_time) = offset + ifelse(t < start_time, 0, height) - - offset, height, start_time, δ = 1, 2, 5, 1e-5 - @named int = Integrator() - - @named src = Step(offset = offset, height = height, start_time = start_time, - smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - - @test sol.retcode == Success - @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) atol=1e-2 - - # test with duration - duration = 1.2 - @named src = Step(offset = offset, height = height, start_time = start_time, - duration = duration, smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance - - @test sol.retcode == Success - @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) - - step.(sol.t, 0, height, start_time + duration) atol=1e-2 - - @named smooth_src = Step(offset = offset, height = height, start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u] ≈ - smooth_step.(smooth_sol.t, δ, height, offset, start_time) - - # with duration - @named smooth_src = Step(offset = offset, height = height, start_time = start_time, - smooth = true, duration = duration) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u] ≈ - smooth_step.(smooth_sol.t, δ, height, offset, start_time) - - smooth_step.(smooth_sol.t, δ, height, 0, start_time + duration) -end - -@testset "Square" begin - frequency = 1 - amplitude = 2 - offset = 1 - start_time = 2.5 - δ = 1e-5 - @named int = Integrator() - - @named src = Square(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - - @test sol.retcode == Success - @test sol[src.output.u]≈square.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 - - @named smooth_src = Square(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_square.(smooth_sol.t, δ, frequency, - amplitude, offset, start_time) atol=1e-3 -end - -@testset "Triangular" begin - frequency = 5 - amplitude = 1 - offset = 2 - start_time = 1 - δ = 1e-5 - @named int = Integrator() - - @named src = Triangular(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 4.0)) - sol = solve(prob, Rodas4(), saveat = 0.01) - - @test sol.retcode == Success - @test sol[src.output.u]≈triangular.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 - - @named smooth_src = Triangular(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 4.0)) - smooth_sol = solve(smooth_prob, Rodas4(), saveat = 0.01) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_triangular.(smooth_sol.t, δ, frequency, - amplitude, offset, start_time) atol=1e-3 -end - -@testset "ExpSine" begin - function exp_sine(t, amplitude, frequency, damping, phase, start_time) - offset + ifelse(t < start_time, 0, - amplitude * exp(-damping * (t - start_time)) * - sin(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency, amplitude, damping = 3, 2, 0.10 - phase, offset, start_time, δ = 0, 0, 0, 1e-5 - @named src = ExpSine(frequency = frequency, amplitude = amplitude, damping = damping, - phase = phase, offset = offset, start_time = start_time) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈exp_sine.(sol.t, amplitude, frequency, damping, phase, - start_time) atol=1e-3 - - offset, start_time = 1, 2 - @named smooth_src = ExpSine(frequency = frequency, amplitude = amplitude, - damping = damping, phase = phase, offset = offset, - start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_damped_sin.(smooth_sol.t, δ, frequency, - amplitude, damping, phase, - offset, start_time) atol=1e-3 -end -#### TODO -#= -@testset "SampledData" begin - using DataInterpolations - - dt = 4e-4 - t_end = 10.0 - time_span = 0:dt:t_end - x = @. time_span^2 + 1.0 - - vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 - @named src = SampledData(Float64) - @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - s = complete(iosys) - prob = ODEProblem(sys, [], (0.0, t_end), [s.src.buffer => Parameter(x, dt)]; - tofloat = false) - prob = remake(prob; p = Parameter.(prob.p)) - - sol = solve(prob, Rodas4(); initializealg = NoInit()) - @test sol.retcode == Success - @test sol[src.output.u][1] == 1.0 #check correct initial condition - - @test sol(time)[src.output.u]≈x atol=1e-3 - @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral - @test sol[dy][end]≈2 * time[end] atol=1e-3 - @test sol[ddy][end]≈2 atol=1e-3 -end -=# \ No newline at end of file +using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq +using ModelingToolkitStandardLibrary.Blocks +using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, + smooth_square, smooth_step, smooth_ramp, + smooth_triangular, triangular, square +using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str + +@parameters t [unit = u"s"] +D = Differential(t) + +@testset "Constant" begin + @named src = Constant(k = 2) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u][end]≈2 atol=1e-3 +end + +@testset "TimeVaryingFunction" begin + f(t) = t^2 + 1 + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = TimeVaryingFunction(f) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈f.(sol.t) atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10 atol=1e-3 # closed-form solution to integral +end + +@testset "Sine" begin + function sine(t, frequency, amplitude, phase, offset, start_time) + offset + ifelse(t < start_time, 0, + amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency = 1 + amplitude = 2 + phase = 0 + offset = 1 + start_time = 2 + δ = 1e-5 + @named int = Integrator() + + @named src = Sine(frequency = frequency, amplitude = amplitude, phase = phase, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈sine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 + + @named smooth_src = Sine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_sin.(smooth_sol.t, δ, frequency, amplitude, + phase, offset, start_time) atol=1e-3 +end + +@testset "Cosine" begin + function cosine(t, frequency, amplitude, phase, offset, start_time) + offset + ifelse(t < start_time, 0, + amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency = 1 + amplitude = 2 + phase = 0 + offset = 1 + start_time = 2 + δ = 1e-5 + @named int = Integrator() + + @named src = Cosine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈cosine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 + + @named smooth_src = Cosine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_cos.(smooth_sol.t, δ, frequency, amplitude, + phase, offset, start_time) atol=1e-3 +end + +@testset "ContinuousClock" begin + cont_clock(t, offset, start_time) = offset + ifelse(t < start_time, 0, t - start_time) + + offset, start_time = 1, 0 + + @named src = ContinuousClock(offset = offset, start_time = start_time) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈cont_clock.(sol.t, offset, start_time) atol=1e-3 +end + +@testset "Ramp" begin + function ramp(t, offset, height, duration, start_time) + offset + ifelse(t < start_time, 0, + ifelse(t < (start_time + duration), (t - start_time) * height / duration, + height)) + end + + offset, height, duration, start_time, δ = 1, 2, 2, 0, 1e-5 + @named int = Integrator() + + @named src = Ramp(offset = offset, height = height, duration = duration, + start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈ramp.(sol.t, offset, height, duration, start_time) atol=1e-3 + + start_time = 2 + @named smooth_src = Ramp(offset = offset, height = height, duration = duration, + start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_ramp.(smooth_sol.t, δ, height, duration, + offset, start_time) atol=1e-3 +end + +@testset "Step" begin + step(t, offset, height, start_time) = offset + ifelse(t < start_time, 0, height) + + offset, height, start_time, δ = 1, 2, 5, 1e-5 + @named int = Integrator() + + @named src = Step(offset = offset, height = height, start_time = start_time, + smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + + @test sol.retcode == Success + @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) atol=1e-2 + + # test with duration + duration = 1.2 + @named src = Step(offset = offset, height = height, start_time = start_time, + duration = duration, smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance + + @test sol.retcode == Success + @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) - + step.(sol.t, 0, height, start_time + duration) atol=1e-2 + + @named smooth_src = Step(offset = offset, height = height, start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u] ≈ + smooth_step.(smooth_sol.t, δ, height, offset, start_time) + + # with duration + @named smooth_src = Step(offset = offset, height = height, start_time = start_time, + smooth = true, duration = duration) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u] ≈ + smooth_step.(smooth_sol.t, δ, height, offset, start_time) - + smooth_step.(smooth_sol.t, δ, height, 0, start_time + duration) +end + +@testset "Square" begin + frequency = 1 + amplitude = 2 + offset = 1 + start_time = 2.5 + δ = 1e-5 + @named int = Integrator() + + @named src = Square(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + + @test sol.retcode == Success + @test sol[src.output.u]≈square.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 + + @named smooth_src = Square(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_square.(smooth_sol.t, δ, frequency, + amplitude, offset, start_time) atol=1e-3 +end + +@testset "Triangular" begin + frequency = 5 + amplitude = 1 + offset = 2 + start_time = 1 + δ = 1e-5 + @named int = Integrator() + + @named src = Triangular(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 4.0)) + sol = solve(prob, Rodas4(), saveat = 0.01) + + @test sol.retcode == Success + @test sol[src.output.u]≈triangular.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 + + @named smooth_src = Triangular(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 4.0)) + smooth_sol = solve(smooth_prob, Rodas4(), saveat = 0.01) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_triangular.(smooth_sol.t, δ, frequency, + amplitude, offset, start_time) atol=1e-3 +end + +@testset "ExpSine" begin + function exp_sine(t, amplitude, frequency, damping, phase, start_time) + offset + ifelse(t < start_time, 0, + amplitude * exp(-damping * (t - start_time)) * + sin(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency, amplitude, damping = 3, 2, 0.10 + phase, offset, start_time, δ = 0, 0, 0, 1e-5 + @named src = ExpSine(frequency = frequency, amplitude = amplitude, damping = damping, + phase = phase, offset = offset, start_time = start_time) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈exp_sine.(sol.t, amplitude, frequency, damping, phase, + start_time) atol=1e-3 + + offset, start_time = 1, 2 + @named smooth_src = ExpSine(frequency = frequency, amplitude = amplitude, + damping = damping, phase = phase, offset = offset, + start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_damped_sin.(smooth_sol.t, δ, frequency, + amplitude, damping, phase, + offset, start_time) atol=1e-3 +end + +@testset "SampledData" begin + using DataInterpolations + + dt = 4e-4 + t_end = 10.0 + time_span = 0:dt:t_end + x = @. time_span^2 + 1.0 + + @testset "using Parameter type" begin + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = SampledData(Float64) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + s = complete(iosys) + prob = ODEProblem(sys, [], (0.0, t_end), [s.src.buffer => Parameter(x, dt)]; tofloat=false) + # prob = remake(prob; p = Parameter.(prob.p)) #<-- no longer needed with ModelingToolkit.jl PR #2231 + + sol = solve(prob, Rodas4(); initializealg = NoInit()) + @test sol.retcode == Success + @test sol[src.output.u][1] == 1.0 #check correct initial condition + + @test sol(time)[src.output.u]≈x atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral + @test sol[dy][end]≈2 * time[end] atol=1e-3 + @test sol[ddy][end]≈2 atol=1e-3 + end + + @testset "using Vector Based" begin + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = SampledData(dt) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + s = complete(iosys) + prob = ODEProblem(sys, [], (0.0, t_end), [s.src.buffer => x, s.src.sample_time => dt]; tofloat=false) + + sol = solve(prob, Rodas4(); initializealg = NoInit()) + @test sol.retcode == Success + @test sol[src.output.u][1] == 1.0 #check correct initial condition + + @test sol(time)[src.output.u]≈x atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral + @test sol[dy][end]≈2 * time[end] atol=1e-3 + @test sol[ddy][end]≈2 atol=1e-3 + end +end + From 57d365c02b73af95cae5d38745b39a5615c6eeaa Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 13 Sep 2023 16:40:29 -0400 Subject: [PATCH 33/80] format --- src/Blocks/sources.jl | 1436 +++++++-------- .../IsothermalCompressible/components.jl | 1568 ++++++++--------- test/Blocks/sources.jl | 947 +++++----- 3 files changed, 1979 insertions(+), 1972 deletions(-) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index 06cb734f6..188097a0f 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -1,718 +1,718 @@ -using DiffEqBase -import ChainRulesCore - -# Define and register smooth functions -# These are "smooth" aka differentiable and avoid Gibbs effect -# These follow: `offset` + `smooth_wave` * `smooth_step` with zero output for `t < start_time` -function smooth_cos(x, δ, f, amplitude, ϕ, offset, start_time) - offset + - amplitude * cos(2 * π * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_damped_sin(x, δ, f, amplitude, damping, ϕ, offset, start_time) - offset + - exp((start_time - x) * damping) * amplitude * sin(2 * π * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_ramp(x, δ, height, duration, offset, start_time) - offset + - height / (duration) * - (smooth_xH(x, δ, start_time) - smooth_xH(x, δ, start_time + duration)) -end - -function smooth_sin(x, δ, f, amplitude, ϕ, offset, start_time) - offset + - amplitude * sin(2 * pi * f * (x - start_time) + ϕ) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_square(x, δ, f, amplitude, offset, start_time) - offset + - amplitude * 2atan(sin(2π * (x - start_time) * f) / δ) / π * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_step(x, δ, height, offset, start_time) - offset + height * (atan((x - start_time) / δ) / π + 0.5) -end - -function smooth_triangular(x, δ, f, amplitude, offset, start_time) - offset + - amplitude * (1 - 2acos((1 - δ)sin(2π * (x - start_time) * f)) / π) * - smooth_step(x, δ, one(x), zero(x), start_time) -end - -function smooth_xH(x, δ, tₒ) - 0.5 * (x - tₒ) * (1 + ((x - tₒ) / sqrt((x - tₒ)^2 + δ^2))) -end - -function square(x, f, amplitude, offset, start_time) - offset + - (x > start_time) * (amplitude * - (4 * floor(f * (x - start_time)) - 2 * floor(2 * (x - start_time) * f) + 1)) -end - -function triangular(x, f, amplitude, offset, start_time) - p = 1 / f # period - offset + - (x > start_time) * - (4 * amplitude * f * abs(abs((x - p / 4 - start_time) % p) - p / 2) - amplitude) -end - -""" - Constant(; name, k = 0.0) - -Generate constant signal. - -# Parameters: - - - `k`: Constant output value - -# Connectors: - - - `output` -""" -@mtkmodel Constant begin - @components begin - output = RealOutput(; unit) - end - @parameters begin - k = 0.0, [description = "Constant output value of block"] - end - @equations begin - output.u ~ k - end -end - -""" - TimeVaryingFunction(f; name) - -Outputs ``f(t)``. - -The input variable `t` can be changed by passing a different variable as the keyword argument `t`. - -# Connectors: -- `output` -""" -@mtkmodel TimeVaryingFunction begin - @structural_parameters begin - f - end - @components begin - output = RealOutput(; unit) - end - @equations begin - output.u ~ f(t) - end -end -TimeVaryingFunction.f(f; name) = TimeVaryingFunction(; f, name) - -""" - Sine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, - smooth = false) - -Generate sine signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of sine wave - - `amplitude`: Amplitude of sine wave - - `phase`: [rad] Phase of sine wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Sine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase - equation = if smooth == false - offset + ifelse(t < start_time, 0, - amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_sin(t, smooth, frequency, amplitude, phase, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Cosine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, - smooth = false) - -Cosine signal. - -# Parameters: -- `frequency`: [Hz] Frequency of sine wave -- `amplitude`: Amplitude of sine wave -- `phase`: [rad] Phase of sine wave -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: -- `output` -""" - -@component function Cosine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase - equation = if smooth == false - offset + ifelse(t < start_time, zero(t), - amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_cos(t, smooth, frequency, amplitude, phase, offset, start_time) - end - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - ContinuousClock(; name, offset = 0, start_time = 0) - -Generate current time signal. - -# Parameters: - - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - -# Connectors: - - - `output` -""" -@component function ContinuousClock(; name, offset = 0, start_time = 0, output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time - eqs = [ - output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" -Ramp(; name, height = 1, duration = 1, offset = 0, start_time = 0, smooth = false) - -Generate ramp signal. - -# Parameters: - - - `height`: Height of ramp - - `duration`: [s] Duration of ramp (= 0.0 gives a Step) - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Ramp(; name, - height = 1, - duration = 1, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time height=height duration=duration - equation = if smooth == false - offset + ifelse(t < start_time, 0, - ifelse(t < (start_time + duration), (t - start_time) * height / duration, - height)) - else - smooth === true && (smooth = 1e-5) - smooth_ramp(t, smooth, height, duration, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Square(; name, frequency = 1.0, amplitude = 1.0, offset = 0.0, start_time = 0.0, - smooth = false) -Generate smooth square signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of square wave - - `amplitude`: Amplitude of square wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Square(; name, frequency = 1.0, amplitude = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters begin - frequency = frequency - amplitude = amplitude - offset = offset - start_time = start_time - end - - equation = if smooth == false - square(t, frequency, amplitude, offset, start_time) - else - smooth === true && (smooth = 1e-5) - smooth_square(t, smooth, frequency, amplitude, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Step(;name, height=1, offset=0, start_time=0, duration=Inf, smooth=true) - -Generate step signal. - -# Parameters: - - - `height`: Height of step - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. - - `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. - - `smooth`: If `true`, returns a smooth wave. Defaults to `true` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, - smooth = 1e-5, output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - duration_numeric = duration - pars = @parameters offset=offset start_time=start_time height=height duration=duration - equation = if smooth == false # use comparison in case smooth is a float - offset + ifelse((start_time < t) & (t < start_time + duration), height, 0) - else - smooth === true && (smooth = 1e-5) - if duration_numeric == Inf - smooth_step(t, smooth, height, offset, start_time) - else - smooth_step(t, smooth, height, offset, start_time) - - smooth_step(t, smooth, height, 0, start_time + duration) - end - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - ExpSine(; name, frequency, amplitude = 1, damping = 0.1, phase = 0, offset = 0, start_time = 0, smooth = false) - -Exponentially damped sine signal. - -# Parameters: - - - `frequency`: [Hz] Frequency of sine wave - - `amplitude`: Amplitude of sine wave - - `damping`: [1/s] Damping coefficient of sine wave - - `phase`: [rad] Phase of sine wave - - `offset`: Offset of output signal - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function ExpSine(; name, - frequency, - amplitude = 1, - damping = 0.1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping - - equation = if smooth == false - offset + ifelse(t < start_time, 0, - amplitude * exp(-damping * (t - start_time)) * - sin(2 * pi * frequency * (t - start_time) + phase)) - else - smooth === true && (smooth = 1e-5) - smooth_damped_sin(t, smooth, frequency, amplitude, damping, phase, offset, - start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -""" - Triangular(; name, amplitude = 1.0, frequency = 1.0, offset = 0.0, - start_time = 0.0, smooth = false) - -Generate smooth triangular signal for frequencies less than or equal to 25 Hz - -# Parameters: - - - `frequency`: [Hz] Frequency of square wave - - `amplitude`: Amplitude of square wave - - `offset`: Offset of output signal. - - `start_time`: [s] Output `y = offset` for `t < start_time` - - `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. - -# Connectors: - - - `output` -""" -@component function Triangular(; name, amplitude = 1.0, frequency = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) - @named output = RealOutput(; unit = output__unit) - pars = @parameters begin - amplitude = amplitude - frequency = frequency - offset = offset - start_time = start_time - end - - equation = if smooth == false - triangular(t, frequency, amplitude, offset, start_time) - else - smooth === true && (smooth = 1e-5) - smooth_triangular(t, smooth, frequency, amplitude, offset, start_time) - end - - eqs = [ - output.u ~ equation, - ] - - compose(ODESystem(eqs, t, [], pars; name = name), [output]) -end - -# TODO: -# - Exponentials Generate a rising and falling exponential signal -# - Pulse Generate pulse signal of type Real -# - SawTooth Generate saw tooth signal -# - Trapezoid Generate trapezoidal signal of type Real - - - -# SampledData Parameter struct ---------------- - -struct Parameter{T <: Real} - data::Vector{T} - ref::T - circular_buffer::Bool -end - -Parameter(data::Vector{T}, ref::T) where {T <: Real} = Parameter(data, ref, true) -Parameter(x::Parameter) = x -function Parameter(x::T; tofloat = true) where {T <: Real} - if tofloat - x = float(x) - P = typeof(x) - else - P = T - end - - return Parameter(P[], x) -end - -function Base.isequal(x::Parameter, y::Parameter) - b0 = length(x.data) == length(y.data) - if b0 - b1 = all(x.data .== y.data) - b2 = x.ref == y.ref - return b1 & b2 - else - return false - end -end - -Base.:*(x::Number, y::Parameter) = x * y.ref -Base.:*(y::Parameter, x::Number) = Base.:*(x, y) -Base.:*(x::Parameter, y::Parameter) = x.ref * y.ref - -Base.:/(x::Number, y::Parameter) = x / y.ref -Base.:/(y::Parameter, x::Number) = y.ref / x -Base.:/(x::Parameter, y::Parameter) = x.ref / y.ref - -Base.:+(x::Number, y::Parameter) = x + y.ref -Base.:+(y::Parameter, x::Number) = Base.:+(x, y) -Base.:+(x::Parameter, y::Parameter) = x.ref + y.ref - -Base.:-(y::Parameter) = -y.ref -Base.:-(x::Number, y::Parameter) = x - y.ref -Base.:-(y::Parameter, x::Number) = y.ref - x -Base.:-(x::Parameter, y::Parameter) = x.ref - y.ref - -Base.:^(x::Number, y::Parameter) = Base.:^(x, y.ref) -Base.:^(y::Parameter, x::Number) = Base.:^(y.ref, x) -Base.:^(x::Parameter, y::Parameter) = Base.:^(x.ref, y.ref) - -Base.isless(x::Parameter, y::Number) = Base.isless(x.ref, y) -Base.isless(y::Number, x::Parameter) = Base.isless(y, x.ref) - -Base.copy(x::Parameter{T}) where {T} = Parameter{T}(copy(x.data), x.ref) - -ifelse(c::Bool, x::Parameter, y::Parameter) = ifelse(c, x.ref, y.ref) -ifelse(c::Bool, x::Parameter, y::Number) = ifelse(c, x.ref, y) -ifelse(c::Bool, x::Number, y::Parameter) = ifelse(c, x, y.ref) - -Base.max(x::Number, y::Parameter) = max(x, y.ref) -Base.max(x::Parameter, y::Number) = max(x.ref, y) -Base.max(x::Parameter, y::Parameter) = max(x.ref, y.ref) - -Base.min(x::Number, y::Parameter) = min(x, y.ref) -Base.min(x::Parameter, y::Number) = min(x.ref, y) -Base.min(x::Parameter, y::Parameter) = min(x.ref, y.ref) - -function Base.show(io::IO, m::MIME"text/plain", p::Parameter) - if !isempty(p.data) - print(io, p.data) - else - print(io, p.ref) - end -end - -get_sample_time(memory::Parameter) = memory.ref -Symbolics.@register_symbolic get_sample_time(memory) - -Base.convert(::Type{T}, x::Parameter{T}) where {T <: Real} = x.ref -function Base.convert(::Type{<:Parameter{T}}, x::Number) where {T <: Real} - Parameter{T}(T[], x, true) -end - - - -# SampledData utilities ---------------- - -function linear_interpolation(x1::Real, x2::Real, t1::Real, t2::Real, t) - if t1 != t2 - slope = (x2 - x1) / (t2 - t1) - intercept = x1 - slope * t1 - - return slope * t + intercept - else - @assert x1==x2 "x1 ($x1) and x2 ($x2) should be equal if t1 == t2" - - return x2 - end -end - -function first_order_backwards_difference(t, memory) - Δt = get_sample_time(memory) - x1 = get_sampled_data(t, memory) - x0 = get_sampled_data(t - Δt, memory) - - return (x1 - x0) / Δt -end - -function first_order_backwards_difference(t, buffer, Δt, circular_buffer) - x1 = get_sampled_data(t , buffer, Δt, circular_buffer) - x0 = get_sampled_data(t - Δt, buffer, Δt, circular_buffer) - - return (x1 - x0) / Δt -end - - - -function get_sampled_data(t, buffer::Vector{<:Real}, dt::Real, circular_buffer=true) - if t < 0 - t = zero(t) - end - - if isempty(buffer) - if T <: AbstractFloat - return T(NaN) - else - return zero(T) - end - end - - i1 = floor(Int, t / dt) + 1 #expensive - i2 = i1 + 1 - - t1 = (i1 - 1) * dt - x1 = @inbounds buffer[i1] - - if t == t1 - return x1 - else - n = length(buffer) - - if circular_buffer - i1 = (i1 - 1) % n + 1 - i2 = (i2 - 1) % n + 1 - else - if i2 > n - i2 = n - i1 = i2 - 1 - end - end - - t2 = (i2 - 1) * dt - x2 = @inbounds buffer[i2] - return linear_interpolation(x1, x2, t1, t2, t) - end -end -get_sampled_data(t, buffer) = get_sampled_data(t, buffer.data, buffer.ref, buffer.circular_buffer) -Symbolics.@register_symbolic get_sampled_data(t, buffer) -Symbolics.@register_symbolic get_sampled_data(t, buffer, dt, circular_buffer) false - -function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{2, Any}, ::Val{1}) - t = @inbounds args[1] - buffer = @inbounds args[2] - first_order_backwards_difference(t, buffer) -end -function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer) - first_order_backwards_difference(t, buffer) * ẋ -end - -function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{4, Any}, ::Val{1}) - t = @inbounds args[1] - buffer = @inbounds args[2] - sample_time = @inbounds args[3] - circular_buffer = @inbounds args[4] - first_order_backwards_difference(t, buffer, sample_time, circular_buffer) -end -function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer, sample_time, circular_buffer) - first_order_backwards_difference(t, buffer, sample_time, circular_buffer) * ẋ -end - - - -# SampledData component ---------------- - -module SampledDataType -@enum Option vector_based struct_based -end - -""" - SampledData(; name, buffer, sample_time, circular_buffer=true) - -data input component. - -# Parameters: - - `buffer::Vector{Real}`: holds the data sampled at `sample_time` - - `sample_time::Real` - - `circular_buffer::Bool = true`: how to handle `t > length(buffer)*sample_time`. If true data is considered circular, otherwise last data point is held. - -# Connectors: - - `output` -""" -@component function SampledData(::Val{SampledDataType.vector_based}; name, buffer, sample_time, circular_buffer=true) - pars = @parameters begin - buffer = buffer #::Vector{Real} - sample_time = sample_time #::Real - circular_buffer = circular_buffer #::Bool - end - vars = [] - systems = @named begin - output = RealOutput() - end - eqs = [ - output.u ~ get_sampled_data(t, buffer, sample_time, circular_buffer), - ] - return ODESystem(eqs, t, vars, pars; name, systems, - defaults = [output.u => get_sampled_data(0.0, buffer, sample_time, circular_buffer)]) -end - -""" - SampledData(; name, buffer) - -data input component. - -# Parameters: - - `buffer`: a `Parameter` type which holds the data and sample time - -# Connectors: - - `output` -""" -@component function SampledData(::Val{SampledDataType.struct_based}; name, buffer, unit = nothing) - pars = @parameters begin - buffer = buffer #::Parameter - end - vars = [] - systems = @named begin - output = RealOutput(; unit) - end - eqs = [ - output.u ~ get_sampled_data(t, buffer), - ] - return ODESystem(eqs, t, vars, pars; name, systems, - defaults = [output.u => get_sampled_data(0.0, buffer)]) -end - -SampledData(x::SampledDataType.Option; kwargs...) = SampledData(Val(x); kwargs...) - -# struct_based -SampledData(T::Type, circular_buffer = true; name) = SampledData(SampledDataType.struct_based; name, buffer = Parameter(T[], zero(T), circular_buffer)) - -# vector_based -SampledData(sample_time::T, circular_buffer = true; name) where {T <: Real} = SampledData(SampledDataType.vector_based; name, buffer=T[], sample_time, circular_buffer) -SampledData(buffer::Vector{<:Real}, sample_time::Real, circular_buffer = true; name) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) -SampledData(; name, buffer, sample_time, circular_buffer) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) - - - - - - - - - - +using DiffEqBase +import ChainRulesCore + +# Define and register smooth functions +# These are "smooth" aka differentiable and avoid Gibbs effect +# These follow: `offset` + `smooth_wave` * `smooth_step` with zero output for `t < start_time` +function smooth_cos(x, δ, f, amplitude, ϕ, offset, start_time) + offset + + amplitude * cos(2 * π * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_damped_sin(x, δ, f, amplitude, damping, ϕ, offset, start_time) + offset + + exp((start_time - x) * damping) * amplitude * sin(2 * π * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_ramp(x, δ, height, duration, offset, start_time) + offset + + height / (duration) * + (smooth_xH(x, δ, start_time) - smooth_xH(x, δ, start_time + duration)) +end + +function smooth_sin(x, δ, f, amplitude, ϕ, offset, start_time) + offset + + amplitude * sin(2 * pi * f * (x - start_time) + ϕ) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_square(x, δ, f, amplitude, offset, start_time) + offset + + amplitude * 2atan(sin(2π * (x - start_time) * f) / δ) / π * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_step(x, δ, height, offset, start_time) + offset + height * (atan((x - start_time) / δ) / π + 0.5) +end + +function smooth_triangular(x, δ, f, amplitude, offset, start_time) + offset + + amplitude * (1 - 2acos((1 - δ)sin(2π * (x - start_time) * f)) / π) * + smooth_step(x, δ, one(x), zero(x), start_time) +end + +function smooth_xH(x, δ, tₒ) + 0.5 * (x - tₒ) * (1 + ((x - tₒ) / sqrt((x - tₒ)^2 + δ^2))) +end + +function square(x, f, amplitude, offset, start_time) + offset + + (x > start_time) * (amplitude * + (4 * floor(f * (x - start_time)) - 2 * floor(2 * (x - start_time) * f) + 1)) +end + +function triangular(x, f, amplitude, offset, start_time) + p = 1 / f # period + offset + + (x > start_time) * + (4 * amplitude * f * abs(abs((x - p / 4 - start_time) % p) - p / 2) - amplitude) +end + +""" + Constant(; name, k = 0.0) + +Generate constant signal. + +# Parameters: + + - `k`: Constant output value + +# Connectors: + + - `output` +""" +@mtkmodel Constant begin + @components begin + output = RealOutput(; unit) + end + @parameters begin + k = 0.0, [description = "Constant output value of block"] + end + @equations begin + output.u ~ k + end +end + +""" + TimeVaryingFunction(f; name) + +Outputs ``f(t)``. + +The input variable `t` can be changed by passing a different variable as the keyword argument `t`. + +# Connectors: +- `output` +""" +@mtkmodel TimeVaryingFunction begin + @structural_parameters begin + f + end + @components begin + output = RealOutput(; unit) + end + @equations begin + output.u ~ f(t) + end +end +TimeVaryingFunction.f(f; name) = TimeVaryingFunction(; f, name) + +""" + Sine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, + smooth = false) + +Generate sine signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Sine(; name, + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase + equation = if smooth == false + offset + ifelse(t < start_time, 0, + amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_sin(t, smooth, frequency, amplitude, phase, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Cosine(; name, frequency, amplitude = 1, phase = 0, offset = 0, start_time = 0, + smooth = false) + +Cosine signal. + +# Parameters: +- `frequency`: [Hz] Frequency of sine wave +- `amplitude`: Amplitude of sine wave +- `phase`: [rad] Phase of sine wave +- `offset`: Offset of output signal +- `start_time`: [s] Output `y = offset` for `t < start_time` +- `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: +- `output` +""" + +@component function Cosine(; name, + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase + equation = if smooth == false + offset + ifelse(t < start_time, zero(t), + amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_cos(t, smooth, frequency, amplitude, phase, offset, start_time) + end + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + ContinuousClock(; name, offset = 0, start_time = 0) + +Generate current time signal. + +# Parameters: + + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + +# Connectors: + + - `output` +""" +@component function ContinuousClock(; name, offset = 0, start_time = 0, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time + eqs = [ + output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" +Ramp(; name, height = 1, duration = 1, offset = 0, start_time = 0, smooth = false) + +Generate ramp signal. + +# Parameters: + + - `height`: Height of ramp + - `duration`: [s] Duration of ramp (= 0.0 gives a Step) + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Ramp(; name, + height = 1, + duration = 1, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time height=height duration=duration + equation = if smooth == false + offset + ifelse(t < start_time, 0, + ifelse(t < (start_time + duration), (t - start_time) * height / duration, + height)) + else + smooth === true && (smooth = 1e-5) + smooth_ramp(t, smooth, height, duration, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Square(; name, frequency = 1.0, amplitude = 1.0, offset = 0.0, start_time = 0.0, + smooth = false) +Generate smooth square signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Square(; name, frequency = 1.0, amplitude = 1.0, + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters begin + frequency = frequency + amplitude = amplitude + offset = offset + start_time = start_time + end + + equation = if smooth == false + square(t, frequency, amplitude, offset, start_time) + else + smooth === true && (smooth = 1e-5) + smooth_square(t, smooth, frequency, amplitude, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Step(;name, height=1, offset=0, start_time=0, duration=Inf, smooth=true) + +Generate step signal. + +# Parameters: + + - `height`: Height of step + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. + - `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. + - `smooth`: If `true`, returns a smooth wave. Defaults to `true` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, + smooth = 1e-5, output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + duration_numeric = duration + pars = @parameters offset=offset start_time=start_time height=height duration=duration + equation = if smooth == false # use comparison in case smooth is a float + offset + ifelse((start_time < t) & (t < start_time + duration), height, 0) + else + smooth === true && (smooth = 1e-5) + if duration_numeric == Inf + smooth_step(t, smooth, height, offset, start_time) + else + smooth_step(t, smooth, height, offset, start_time) - + smooth_step(t, smooth, height, 0, start_time + duration) + end + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + ExpSine(; name, frequency, amplitude = 1, damping = 0.1, phase = 0, offset = 0, start_time = 0, smooth = false) + +Exponentially damped sine signal. + +# Parameters: + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `damping`: [1/s] Damping coefficient of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function ExpSine(; name, + frequency, + amplitude = 1, + damping = 0.1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping + + equation = if smooth == false + offset + ifelse(t < start_time, 0, + amplitude * exp(-damping * (t - start_time)) * + sin(2 * pi * frequency * (t - start_time) + phase)) + else + smooth === true && (smooth = 1e-5) + smooth_damped_sin(t, smooth, frequency, amplitude, damping, phase, offset, + start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +""" + Triangular(; name, amplitude = 1.0, frequency = 1.0, offset = 0.0, + start_time = 0.0, smooth = false) + +Generate smooth triangular signal for frequencies less than or equal to 25 Hz + +# Parameters: + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal. + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + +# Connectors: + + - `output` +""" +@component function Triangular(; name, amplitude = 1.0, frequency = 1.0, + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) + @named output = RealOutput(; unit = output__unit) + pars = @parameters begin + amplitude = amplitude + frequency = frequency + offset = offset + start_time = start_time + end + + equation = if smooth == false + triangular(t, frequency, amplitude, offset, start_time) + else + smooth === true && (smooth = 1e-5) + smooth_triangular(t, smooth, frequency, amplitude, offset, start_time) + end + + eqs = [ + output.u ~ equation, + ] + + compose(ODESystem(eqs, t, [], pars; name = name), [output]) +end + +# TODO: +# - Exponentials Generate a rising and falling exponential signal +# - Pulse Generate pulse signal of type Real +# - SawTooth Generate saw tooth signal +# - Trapezoid Generate trapezoidal signal of type Real + + + +# SampledData Parameter struct ---------------- + +struct Parameter{T <: Real} + data::Vector{T} + ref::T + circular_buffer::Bool +end + +Parameter(data::Vector{T}, ref::T) where {T <: Real} = Parameter(data, ref, true) +Parameter(x::Parameter) = x +function Parameter(x::T; tofloat = true) where {T <: Real} + if tofloat + x = float(x) + P = typeof(x) + else + P = T + end + + return Parameter(P[], x) +end + +function Base.isequal(x::Parameter, y::Parameter) + b0 = length(x.data) == length(y.data) + if b0 + b1 = all(x.data .== y.data) + b2 = x.ref == y.ref + return b1 & b2 + else + return false + end +end + +Base.:*(x::Number, y::Parameter) = x * y.ref +Base.:*(y::Parameter, x::Number) = Base.:*(x, y) +Base.:*(x::Parameter, y::Parameter) = x.ref * y.ref + +Base.:/(x::Number, y::Parameter) = x / y.ref +Base.:/(y::Parameter, x::Number) = y.ref / x +Base.:/(x::Parameter, y::Parameter) = x.ref / y.ref + +Base.:+(x::Number, y::Parameter) = x + y.ref +Base.:+(y::Parameter, x::Number) = Base.:+(x, y) +Base.:+(x::Parameter, y::Parameter) = x.ref + y.ref + +Base.:-(y::Parameter) = -y.ref +Base.:-(x::Number, y::Parameter) = x - y.ref +Base.:-(y::Parameter, x::Number) = y.ref - x +Base.:-(x::Parameter, y::Parameter) = x.ref - y.ref + +Base.:^(x::Number, y::Parameter) = Base.:^(x, y.ref) +Base.:^(y::Parameter, x::Number) = Base.:^(y.ref, x) +Base.:^(x::Parameter, y::Parameter) = Base.:^(x.ref, y.ref) + +Base.isless(x::Parameter, y::Number) = Base.isless(x.ref, y) +Base.isless(y::Number, x::Parameter) = Base.isless(y, x.ref) + +Base.copy(x::Parameter{T}) where {T} = Parameter{T}(copy(x.data), x.ref) + +ifelse(c::Bool, x::Parameter, y::Parameter) = ifelse(c, x.ref, y.ref) +ifelse(c::Bool, x::Parameter, y::Number) = ifelse(c, x.ref, y) +ifelse(c::Bool, x::Number, y::Parameter) = ifelse(c, x, y.ref) + +Base.max(x::Number, y::Parameter) = max(x, y.ref) +Base.max(x::Parameter, y::Number) = max(x.ref, y) +Base.max(x::Parameter, y::Parameter) = max(x.ref, y.ref) + +Base.min(x::Number, y::Parameter) = min(x, y.ref) +Base.min(x::Parameter, y::Number) = min(x.ref, y) +Base.min(x::Parameter, y::Parameter) = min(x.ref, y.ref) + +function Base.show(io::IO, m::MIME"text/plain", p::Parameter) + if !isempty(p.data) + print(io, p.data) + else + print(io, p.ref) + end +end + +get_sample_time(memory::Parameter) = memory.ref +Symbolics.@register_symbolic get_sample_time(memory) + +Base.convert(::Type{T}, x::Parameter{T}) where {T <: Real} = x.ref +function Base.convert(::Type{<:Parameter{T}}, x::Number) where {T <: Real} + Parameter{T}(T[], x, true) +end + + + +# SampledData utilities ---------------- + +function linear_interpolation(x1::Real, x2::Real, t1::Real, t2::Real, t) + if t1 != t2 + slope = (x2 - x1) / (t2 - t1) + intercept = x1 - slope * t1 + + return slope * t + intercept + else + @assert x1==x2 "x1 ($x1) and x2 ($x2) should be equal if t1 == t2" + + return x2 + end +end + +function first_order_backwards_difference(t, memory) + Δt = get_sample_time(memory) + x1 = get_sampled_data(t, memory) + x0 = get_sampled_data(t - Δt, memory) + + return (x1 - x0) / Δt +end + +function first_order_backwards_difference(t, buffer, Δt, circular_buffer) + x1 = get_sampled_data(t , buffer, Δt, circular_buffer) + x0 = get_sampled_data(t - Δt, buffer, Δt, circular_buffer) + + return (x1 - x0) / Δt +end + + + +function get_sampled_data(t, buffer::Vector{<:Real}, dt::Real, circular_buffer=true) + if t < 0 + t = zero(t) + end + + if isempty(buffer) + if T <: AbstractFloat + return T(NaN) + else + return zero(T) + end + end + + i1 = floor(Int, t / dt) + 1 #expensive + i2 = i1 + 1 + + t1 = (i1 - 1) * dt + x1 = @inbounds buffer[i1] + + if t == t1 + return x1 + else + n = length(buffer) + + if circular_buffer + i1 = (i1 - 1) % n + 1 + i2 = (i2 - 1) % n + 1 + else + if i2 > n + i2 = n + i1 = i2 - 1 + end + end + + t2 = (i2 - 1) * dt + x2 = @inbounds buffer[i2] + return linear_interpolation(x1, x2, t1, t2, t) + end +end +get_sampled_data(t, buffer) = get_sampled_data(t, buffer.data, buffer.ref, buffer.circular_buffer) +Symbolics.@register_symbolic get_sampled_data(t, buffer) +Symbolics.@register_symbolic get_sampled_data(t, buffer, dt, circular_buffer) false + +function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{2, Any}, ::Val{1}) + t = @inbounds args[1] + buffer = @inbounds args[2] + first_order_backwards_difference(t, buffer) +end +function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer) + first_order_backwards_difference(t, buffer) * ẋ +end + +function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{4, Any}, ::Val{1}) + t = @inbounds args[1] + buffer = @inbounds args[2] + sample_time = @inbounds args[3] + circular_buffer = @inbounds args[4] + first_order_backwards_difference(t, buffer, sample_time, circular_buffer) +end +function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer, sample_time, circular_buffer) + first_order_backwards_difference(t, buffer, sample_time, circular_buffer) * ẋ +end + + + +# SampledData component ---------------- + +module SampledDataType +@enum Option vector_based struct_based +end + +""" + SampledData(; name, buffer, sample_time, circular_buffer=true) + +data input component. + +# Parameters: + - `buffer::Vector{Real}`: holds the data sampled at `sample_time` + - `sample_time::Real` + - `circular_buffer::Bool = true`: how to handle `t > length(buffer)*sample_time`. If true data is considered circular, otherwise last data point is held. + +# Connectors: + - `output` +""" +@component function SampledData(::Val{SampledDataType.vector_based}; name, buffer, sample_time, circular_buffer=true) + pars = @parameters begin + buffer = buffer #::Vector{Real} + sample_time = sample_time #::Real + circular_buffer = circular_buffer #::Bool + end + vars = [] + systems = @named begin + output = RealOutput() + end + eqs = [ + output.u ~ get_sampled_data(t, buffer, sample_time, circular_buffer), + ] + return ODESystem(eqs, t, vars, pars; name, systems, + defaults = [output.u => get_sampled_data(0.0, buffer, sample_time, circular_buffer)]) +end + +""" + SampledData(; name, buffer) + +data input component. + +# Parameters: + - `buffer`: a `Parameter` type which holds the data and sample time + +# Connectors: + - `output` +""" +@component function SampledData(::Val{SampledDataType.struct_based}; name, buffer, unit = nothing) + pars = @parameters begin + buffer = buffer #::Parameter + end + vars = [] + systems = @named begin + output = RealOutput(; unit) + end + eqs = [ + output.u ~ get_sampled_data(t, buffer), + ] + return ODESystem(eqs, t, vars, pars; name, systems, + defaults = [output.u => get_sampled_data(0.0, buffer)]) +end + +SampledData(x::SampledDataType.Option; kwargs...) = SampledData(Val(x); kwargs...) + +# struct_based +SampledData(T::Type, circular_buffer = true; name) = SampledData(SampledDataType.struct_based; name, buffer = Parameter(T[], zero(T), circular_buffer)) + +# vector_based +SampledData(sample_time::T, circular_buffer = true; name) where {T <: Real} = SampledData(SampledDataType.vector_based; name, buffer=T[], sample_time, circular_buffer) +SampledData(buffer::Vector{<:Real}, sample_time::Real, circular_buffer = true; name) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) +SampledData(; name, buffer, sample_time, circular_buffer) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) + + + + + + + + + + diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index ecc54345f..30491a705 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -1,784 +1,784 @@ - -""" - Cap(; p_int, name) - -Caps a hydraulic port to prevent mass flow in or out. - -# Parameters: -- `p_int`: [Pa] initial pressure (set by `p_int` argument) - -# Connectors: -- `port`: hydraulic port -""" -@component function Cap(; p_int, name) - pars = @parameters p_int = p_int - - vars = @variables p(t) = p_int - - systems = @named begin - port = HydraulicPort(; p_int = p_int) - end - - eqs = [port.p ~ p - port.dm ~ 0] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -@component function Open(; p_int, name) - pars = @parameters p_int = p_int - - vars = @variables begin - p(t) = p_int - dm(t) = 0 - end - - systems = @named begin - port = HydraulicPort(; p_int = p_int) - end - - eqs = [port.p ~ p - port.dm ~ dm] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - TubeBase(add_inertia = true; p_int, area, length_int, head_factor = 1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) - -Variable length internal flow model of the fully developed incompressible flow friction. Includes optional inertia term when `add_inertia = true` to model wave propagation. Hydraulic ports have equal flow but variable pressure. Density is averaged over the pressures, used to calculated average flow velocity and flow friction. - -# States: -- `x`: [m] length of the pipe -- `ddm`: [kg/s^2] Rate of change of mass flow rate in control volume. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `area`: [m^2] tube cross sectional area -- `length_int`: [m] initial tube length -- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -""" -@component function TubeBase(add_inertia = true, variable_length = true; p_int, area, - length_int, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) - pars = @parameters begin - p_int = p_int - area = area - length_int = length_int - perimeter = perimeter - shape_factor = shape_factor - head_factor = head_factor - end - - @variables begin - x(t) = length_int - ddm(t) = 0 - end - - vars = [] - if variable_length - push!(vars, x) - c = x - else - c = length_int - end - add_inertia && push!(vars, ddm) - - systems = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - end - - # let ---------------------- - Δp = port_a.p - port_b.p - dm = port_a.dm - - d_h = 4 * area / perimeter - - # Opting for a more numerically stable constant density (use head factor to compensate if needed) - ρ = density_ref(port_a) # (full_density(port_a) + full_density(port_b)) / 2 - μ = viscosity(port_a) - - f = friction_factor(dm, area, d_h, μ, shape_factor) - u = dm / (ρ * area) - - shear = (1 / 2) * ρ * regPow(u, 2) * f * head_factor * (c / d_h) - inertia = if add_inertia - (c / area) * ddm - else - 0 - end - - eqs = [0 ~ port_a.dm + port_b.dm] - - if variable_length - push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) - else - push!(eqs, Δp ~ shear + inertia) - end - - if add_inertia - push!(eqs, D(dm) ~ ddm) - end - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - Tube(N, add_inertia=true; p_int, area, length, head_factor=1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) - -Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `TubeBase`:`N-1`) which models the fully developed flow friction, compressibility (when `N>1`), and inertia effects when `add_inertia = true`. See `TubeBase` and `FixedVolume` for more information. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `area`: [m^2] tube cross sectional area -- `length`: [m] real length of the tube -- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -""" -@component function Tube(N, add_inertia = true; p_int, area, length, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) - @assert(N>0, - "the Tube component must be defined with at least 1 segment (i.e. N>0), found N=$N") - - if N == 1 - return TubeBase(add_inertia, - false; - shape_factor, - p_int, - area, - length_int = length, - head_factor, - perimeter, - name) - end - - #TODO: How to set an assert effective_length >= length ?? - pars = @parameters begin - p_int = p_int - area = area - length = length - head_factor = head_factor - perimeter = perimeter - shape_factor = shape_factor - end - - vars = [] - - ports = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - end - - pipe_bases = [] - for i in 1:(N - 1) - x = TubeBase(add_inertia; name = Symbol("p$i"), - shape_factor = ParentScope(shape_factor), - p_int = ParentScope(p_int), area = ParentScope(area), - length_int = ParentScope(length) / (N - 1), - head_factor = ParentScope(head_factor), - perimeter = ParentScope(perimeter)) - push!(pipe_bases, x) - end - - volumes = [] - for i in 1:N - x = FixedVolume(; name = Symbol("v$i"), - vol = ParentScope(area) * ParentScope(length) / N, - p_int = ParentScope(p_int)) - push!(volumes, x) - end - - eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a) - connect(volumes[end].port, pipe_bases[end].port_b, port_b)] - - for i in 2:(N - 1) - push!(eqs, - connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) - end - - for i in 1:(N - 1) - push!(eqs, pipe_bases[i].x ~ length / (N - 1)) - end - - return ODESystem(eqs, t, vars, pars; name, systems = [ports; pipe_bases; volumes]) -end -@deprecate Pipe Tube - -""" - FlowDivider(;p_int, n, name) - -Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel tubes efficiently by placing a `FlowDivider` on each end of a tube. - -# Parameters: -- `p_int`: [Pa] initial pressure -- `n`: divide flow from `port_a` to `port_b` by `n` - -# Connectors: -- `port_a`: full flow hydraulic port -- `port_b`: part flow hydraulic port -""" -@component function FlowDivider(; p_int, n, name) - - #TODO: assert n >= 1 - - pars = @parameters begin - n = n - p_int = p_int - end - - vars = @variables begin - dm_a(t) = 0 - dm_b(t) = 0 - end - - systems = @named begin - port_a = HydraulicPort(; p_int) - port_b = HydraulicPort(; p_int) - open = Open(; p_int) - end - - eqs = [connect(port_a, port_b, open.port) - dm_a ~ port_a.dm - dm_b ~ dm_a / n - open.dm ~ dm_a - dm_b # extra flow dumps into an open port - # port_b.dm ~ dm_b # divided flow goes to port_b - ] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -@component function ValveBase(reversible = false; p_a_int, p_b_int, minimum_area = 0, - area_int, Cd, Cd_reverse = Cd, name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_int = area_int - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - end - - vars = @variables begin - area(t) = area_int - y(t) = area_int - end - - # let - # Opting for a more numerically stable constant density (use head factor to compensate if needed) - ρ = density_ref(port_a) #(full_density(port_a) + full_density(port_b)) / 2 - - x = if reversible - area - else - ifelse(area > minimum_area, area, minimum_area) - end - - # let ------ - Δp = port_a.p - port_b.p - dm = port_a.dm - c = if reversible - Cd - else - ifelse(Δp > 0, Cd, Cd_reverse) - end - - eqs = [0 ~ port_a.dm + port_b.dm - dm ~ regRoot(2 * Δp * ρ / c) * x - y ~ x] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - Valve(reversible = false; p_a_int, p_b_int, area_int, Cd, Cd_reverse = Cd, minimum_area = 0, name) - -Valve with `area` input and discharge coefficient `Cd` defined by https://en.wikipedia.org/wiki/Discharge_coefficient. The `Cd_reverse` parameter allows for directional flow restriction, making it possible to define a check valve. - -# Parameters: -- `p_a_int`: [Pa] initial pressure for `port_a` -- `p_b_int`: [Pa] initial pressure for `port_b` -- `area_int`: [m^2] initial valve opening -- `Cd`: discharge coefficient flowing from `a → b` -- `Cd_reverse`: discharge coefficient flowing from `b → a` -- `minimum_area`: when `reversible = false` applies a forced minimum area - -# Connectors: -- `port_a`: hydraulic port -- `port_b`: hydraulic port -- `area`: real input setting the valve `area`. When `reversible = true`, negative input reverses flow direction, otherwise a floor of `minimum_area` is enforced. -""" -@component function Valve(reversible = false; p_a_int, p_b_int, - area_int, Cd, Cd_reverse = Cd, - minimum_area = 0, - name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_int = area_int - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - area = RealInput() - base = ValveBase(reversible; p_a_int, p_b_int, area_int, Cd, Cd_reverse, - minimum_area) - end - - vars = [] - - eqs = [connect(base.port_a, port_a) - connect(base.port_b, port_b) - base.area ~ area.u] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [area.u => area_int]) -end - -@component function VolumeBase(; p_int, x_int = 0, area, dead_volume = 0, Χ1 = 1, Χ2 = 1, - name) - pars = @parameters begin - p_int = p_int - x_int = x_int - area = area - dead_volume = dead_volume - end - - systems = @named begin - port = HydraulicPort(; p_int) - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - rho(t) = liquid_density(port) - drho(t) = 0 - vol(t) = dead_volume + area * x_int - end - - # let - dm = port.dm - p = port.p - - eqs = [vol ~ dead_volume + area * x - D(x) ~ dx - D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" - FixedVolume(; vol, p_int, name) - -Fixed fluid volume. - -# Parameters: -- `vol`: [m^3] fixed volume -- `p_int`: [Pa] initial pressure - -# Connectors: -- `port`: hydraulic port -""" -@component function FixedVolume(; vol, p_int, name) - pars = @parameters begin - p_int = p_int - vol = vol - end - - systems = @named begin - port = HydraulicPort(; p_int) - end - - vars = @variables begin - rho(t) = liquid_density(port) - drho(t) = 0 - end - - # let - dm = port.dm - p = port.p - - eqs = [D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol] - - ODESystem(eqs, t, vars, pars; name, systems) -end - -""" -DynamicVolume(N, add_inertia=true; p_int, area, x_int = 0, x_max, x_min = 0, x_damp = x_min, direction = +1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, head_factor = 1, Cd = 1e2, Cd_reverse = Cd, name) - -Volume with moving wall with `flange` connector for converting hydraulic energy to 1D mechanical. The `direction` argument aligns the mechanical port with the hydraulic port, useful when connecting two dynamic volumes together in oppsing directions to create an actuator. - -``` - ┌─────────────────┐ ─── - │ │ ▲ - │ │ -dm ────► │ │ area - │ │ - │ │ ▼ - └─────────────────┤ ─── - │ - └─► x (= ∫ flange.v * direction) -``` - -# Features: -- volume discretization with flow resistance and inertia: use `N` to control number of volume and resistance elements. Set `N=0` to turn off volume discretization. See `TubeBase` for more information about flow resistance. -- minimum volume flow shutoff with damping and directional resistance. Use `reversible=false` when problem defines volume position `x` and solves for `dm` to prevent numerical instability. - -# Parameters: -## volume -- `p_int`: [Pa] initial pressure -- `area`: [m^2] moving wall area -- `x_int`: [m] initial wall position -- `x_max`: [m] max wall position, needed for volume discretization to apply the correct volume sizing as a function of `x` -- `x_min`: [m] wall position that shuts off flow and prevents negative volume. -- `x_damp`: [m] wall position that initiates a linear damping region before reaching full flow shut off. Helps provide a smooth end stop. - -- `direction`: [+/-1] applies the direction conversion from the `flange` to `x` - -## flow resistance -- `perimeter`: [m] perimeter of the cross section (needed only for non-circular volumes) -- `shape_factor`: shape factor, see `friction_factor` function -- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. - -## flow shut off and damping -- `Cd`: discharge coefficient for flow out of the volume. *Note: area is 1m² when valve is fully open. Ensure this does not induce unwanted flow resistance.* -- `Cd_reverse`: discharge coefficient for flow into the volume. Use a lower value to allow easy wall release, in some cases the wall can "stick". - - -# Connectors: -- `port`: hydraulic port -- `flange`: mechanical translational port -""" -@component function DynamicVolume(N, add_inertia = true, reversible = false; - p_int, - area, - x_int = 0, - x_max, - x_min = 0, - x_damp = x_min, - direction = +1, - - # Tube - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, - head_factor = 1, - - # Valve - Cd = 1e2, - Cd_reverse = Cd, - minimum_area = 0, - name) - @assert(N>=0, - "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") - @assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction" - - #TODO: How to set an assert effective_length >= length ?? - pars = @parameters begin - p_int = p_int - area = area - - x_int = x_int - x_max = x_max - x_min = x_min - x_damp = x_damp - - direction = direction - - perimeter = perimeter - shape_factor = shape_factor - head_factor = head_factor - - Cd = Cd - Cd_reverse = Cd_reverse - minimum_area = minimum_area - end - - vars = @variables x(t)=x_int vol(t)=x_int * area - - ports = @named begin - port = HydraulicPort(; p_int) - flange = MechanicalPort(; f = -direction * p_int * area) - damper = ValveBase(reversible; - p_a_int = p_int, - p_b_int = p_int, - area_int = 1, - Cd, - Cd_reverse, - minimum_area) - end - - pipe_bases = [] - for i in 1:N - comp = TubeBase(add_inertia; name = Symbol("p$i"), - shape_factor = ParentScope(shape_factor), - p_int = ParentScope(p_int), area = ParentScope(area), - length_int = 0, #set in equations - head_factor = ParentScope(head_factor), - perimeter = ParentScope(perimeter)) - push!(pipe_bases, comp) - end - - #TODO: How to handle x_int? - #TODO: Handle direction - @named moving_volume = VolumeBase(; - p_int, - x_int = 0, - area, - dead_volume = N == 0 ? area * x_int : 0, - Χ1 = N == 0 ? 1 : 0, - Χ2 = 1) - - ratio = (x - x_min) / (x_damp - x_min) - - damper_area = if reversible - one(x) - else - ifelse(x >= x_damp, one(x), ifelse((x < x_damp) & (x > x_min), ratio, zero(x))) - end - - eqs = [vol ~ x * area - D(x) ~ flange.v * direction - damper.area ~ damper_area - connect(port, damper.port_b)] - - volumes = [] - if N > 0 - Δx = ParentScope(x_max) / N - x₀ = ParentScope(x_int) - - for i in 1:N - length = ifelse(x₀ > Δx * i, - Δx, - ifelse(x₀ - Δx * (i - 1) > 0, - x₀ - Δx * (i - 1), - zero(Δx))) - - comp = VolumeBase(; name = Symbol("v$i"), p_int = ParentScope(p_int), - x_int = 0, - area = ParentScope(area), - dead_volume = ParentScope(area) * length, Χ1 = 1, Χ2 = 0) - - push!(volumes, comp) - end - - push!(eqs, connect(moving_volume.port, volumes[1].port, pipe_bases[1].port_a)) - push!(eqs, connect(pipe_bases[end].port_b, damper.port_a)) - for i in 2:N - push!(eqs, - connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) - end - - for i in 1:N - push!(eqs, - volumes[i].dx ~ ifelse((vol >= (i - 1) * (x_max / N) * area) & - (vol < (i) * (x_max / N) * area), - flange.v * direction, 0)) - push!(eqs, pipe_bases[i].x ~ volumes[i].vol / volumes[i].area) - end - else - push!(eqs, connect(moving_volume.port, damper.port_a)) - end - - push!(eqs, moving_volume.dx ~ flange.v * direction) - push!(eqs, -moving_volume.port.p * area * direction ~ flange.f) - - ODESystem(eqs, t, vars, pars; name, - systems = [ports; pipe_bases; volumes; moving_volume], - defaults = [flange.v => 0]) -end - -@component function SpoolValve(reversible = false; p_a_int, p_b_int, x_int, Cd, d, name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - d = d - x_int = x_int - Cd = Cd - end - - systems = @named begin - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - flange = MechanicalPort() - valve = ValveBase(reversible; p_a_int, p_b_int, - area_int = ParentScope(x_int) * 2π * ParentScope(d), Cd) - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - end - - eqs = [D(x) ~ dx - flange.v ~ dx - flange.f ~ 0 #TODO: model flow force - connect(valve.port_a, port_a) - connect(valve.port_b, port_b) - valve.area ~ x * 2π * d] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end - -@component function SpoolValve2Way(reversible = false; p_s_int, p_a_int, p_b_int, p_r_int, - m, g, x_int, Cd, d, name) - pars = @parameters begin - p_s_int = p_s_int - p_a_int = p_a_int - p_b_int = p_b_int - p_r_int = p_r_int - - m = m - g = g - - x_int = x_int - - d = d - - Cd = Cd - end - - vars = [] - - systems = @named begin - vSA = SpoolValve(reversible; p_a_int = p_s_int, p_b_int = p_a_int, x_int, Cd, d) - vBR = SpoolValve(reversible; p_a_int = p_b_int, p_b_int = p_r_int, x_int, Cd, d) - - port_s = HydraulicPort(; p_int = p_s_int) - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - port_r = HydraulicPort(; p_int = p_r_int) - - mass = Mass(; m = m, g = g) - - flange = MechanicalPort() - end - - eqs = [connect(vSA.port_a, port_s) - connect(vSA.port_b, port_a) - connect(vBR.port_a, port_b) - connect(vBR.port_b, port_r) - connect(vSA.flange, vBR.flange, mass.flange, flange)] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end - -@component function Actuator(N, add_inertia = true, reversible = false; - p_a_int, - p_b_int, - area_a, - area_b, - perimeter_a = 2 * sqrt(area_a * pi), - perimeter_b = 2 * sqrt(area_b * pi), - length_a_int, - length_b_int, - shape_factor_a = 64, - shape_factor_b = 64, - head_factor_a = 1, - head_factor_b = 1, - m, - g, - x_int = 0, - minimum_volume_a = 0, - minimum_volume_b = 0, - damping_volume_a = minimum_volume_a, - damping_volume_b = minimum_volume_b, - Cd = 1e4, - Cd_reverse = Cd, - name) - pars = @parameters begin - p_a_int = p_a_int - p_b_int = p_b_int - area_a = area_a - area_b = area_b - perimeter_a = perimeter_a - perimeter_b = perimeter_b - shape_factor_a = shape_factor_a - shape_factor_b = shape_factor_b - head_factor_a = head_factor_a - head_factor_b = head_factor_b - x_int = x_int - length_a_int = length_a_int - length_b_int = length_b_int - minimum_volume_a = minimum_volume_a - minimum_volume_b = minimum_volume_b - damping_volume_a = damping_volume_a - damping_volume_b = damping_volume_b - Cd = Cd - Cd_reverse = Cd_reverse - m = m - g = g - end - - vars = @variables begin - x(t) = x_int - dx(t) = 0 - end - - total_length = length_a_int + length_b_int - - #TODO: include effective_length - systems = @named begin - vol_a = DynamicVolume(N, add_inertia, reversible; direction = +1, - p_int = p_a_int, - area = area_a, - x_int = length_a_int, - x_max = total_length, - x_min = minimum_volume_a / area_a, - x_damp = damping_volume_a / area_a, - perimeter = perimeter_a, - shape_factor = shape_factor_a, - head_factor = head_factor_a, - Cd, - Cd_reverse) - - vol_b = DynamicVolume(N, add_inertia, reversible; direction = -1, - p_int = p_b_int, - area = area_b, - x_int = length_b_int, - x_max = total_length, - x_min = minimum_volume_b / area_b, - x_damp = damping_volume_b / area_b, - perimeter = perimeter_b, - shape_factor = shape_factor_b, - head_factor = head_factor_b, - Cd, - Cd_reverse) - mass = Mass(; m, g) - port_a = HydraulicPort(; p_int = p_a_int) - port_b = HydraulicPort(; p_int = p_b_int) - flange = MechanicalPort() - end - - eqs = [connect(vol_a.port, port_a) - connect(vol_b.port, port_b) - connect(vol_a.flange, vol_b.flange, mass.flange, flange) - D(x) ~ dx - dx ~ vol_a.flange.v] - - ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) -end + +""" + Cap(; p_int, name) + +Caps a hydraulic port to prevent mass flow in or out. + +# Parameters: +- `p_int`: [Pa] initial pressure (set by `p_int` argument) + +# Connectors: +- `port`: hydraulic port +""" +@component function Cap(; p_int, name) + pars = @parameters p_int = p_int + + vars = @variables p(t) = p_int + + systems = @named begin + port = HydraulicPort(; p_int = p_int) + end + + eqs = [port.p ~ p + port.dm ~ 0] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +@component function Open(; p_int, name) + pars = @parameters p_int = p_int + + vars = @variables begin + p(t) = p_int + dm(t) = 0 + end + + systems = @named begin + port = HydraulicPort(; p_int = p_int) + end + + eqs = [port.p ~ p + port.dm ~ dm] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + TubeBase(add_inertia = true; p_int, area, length_int, head_factor = 1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) + +Variable length internal flow model of the fully developed incompressible flow friction. Includes optional inertia term when `add_inertia = true` to model wave propagation. Hydraulic ports have equal flow but variable pressure. Density is averaged over the pressures, used to calculated average flow velocity and flow friction. + +# States: +- `x`: [m] length of the pipe +- `ddm`: [kg/s^2] Rate of change of mass flow rate in control volume. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `area`: [m^2] tube cross sectional area +- `length_int`: [m] initial tube length +- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +""" +@component function TubeBase(add_inertia = true, variable_length = true; p_int, area, + length_int, head_factor = 1, + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) + pars = @parameters begin + p_int = p_int + area = area + length_int = length_int + perimeter = perimeter + shape_factor = shape_factor + head_factor = head_factor + end + + @variables begin + x(t) = length_int + ddm(t) = 0 + end + + vars = [] + if variable_length + push!(vars, x) + c = x + else + c = length_int + end + add_inertia && push!(vars, ddm) + + systems = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + end + + # let ---------------------- + Δp = port_a.p - port_b.p + dm = port_a.dm + + d_h = 4 * area / perimeter + + # Opting for a more numerically stable constant density (use head factor to compensate if needed) + ρ = density_ref(port_a) # (full_density(port_a) + full_density(port_b)) / 2 + μ = viscosity(port_a) + + f = friction_factor(dm, area, d_h, μ, shape_factor) + u = dm / (ρ * area) + + shear = (1 / 2) * ρ * regPow(u, 2) * f * head_factor * (c / d_h) + inertia = if add_inertia + (c / area) * ddm + else + 0 + end + + eqs = [0 ~ port_a.dm + port_b.dm] + + if variable_length + push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) + else + push!(eqs, Δp ~ shear + inertia) + end + + if add_inertia + push!(eqs, D(dm) ~ ddm) + end + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + Tube(N, add_inertia=true; p_int, area, length, head_factor=1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name) + +Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `TubeBase`:`N-1`) which models the fully developed flow friction, compressibility (when `N>1`), and inertia effects when `add_inertia = true`. See `TubeBase` and `FixedVolume` for more information. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `area`: [m^2] tube cross sectional area +- `length`: [m] real length of the tube +- `perimeter`: [m] perimeter of the pipe cross section (needed only for non-circular pipes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +""" +@component function Tube(N, add_inertia = true; p_int, area, length, head_factor = 1, + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) + @assert(N>0, + "the Tube component must be defined with at least 1 segment (i.e. N>0), found N=$N") + + if N == 1 + return TubeBase(add_inertia, + false; + shape_factor, + p_int, + area, + length_int = length, + head_factor, + perimeter, + name) + end + + #TODO: How to set an assert effective_length >= length ?? + pars = @parameters begin + p_int = p_int + area = area + length = length + head_factor = head_factor + perimeter = perimeter + shape_factor = shape_factor + end + + vars = [] + + ports = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + end + + pipe_bases = [] + for i in 1:(N - 1) + x = TubeBase(add_inertia; name = Symbol("p$i"), + shape_factor = ParentScope(shape_factor), + p_int = ParentScope(p_int), area = ParentScope(area), + length_int = ParentScope(length) / (N - 1), + head_factor = ParentScope(head_factor), + perimeter = ParentScope(perimeter)) + push!(pipe_bases, x) + end + + volumes = [] + for i in 1:N + x = FixedVolume(; name = Symbol("v$i"), + vol = ParentScope(area) * ParentScope(length) / N, + p_int = ParentScope(p_int)) + push!(volumes, x) + end + + eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a) + connect(volumes[end].port, pipe_bases[end].port_b, port_b)] + + for i in 2:(N - 1) + push!(eqs, + connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) + end + + for i in 1:(N - 1) + push!(eqs, pipe_bases[i].x ~ length / (N - 1)) + end + + return ODESystem(eqs, t, vars, pars; name, systems = [ports; pipe_bases; volumes]) +end +@deprecate Pipe Tube + +""" + FlowDivider(;p_int, n, name) + +Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel tubes efficiently by placing a `FlowDivider` on each end of a tube. + +# Parameters: +- `p_int`: [Pa] initial pressure +- `n`: divide flow from `port_a` to `port_b` by `n` + +# Connectors: +- `port_a`: full flow hydraulic port +- `port_b`: part flow hydraulic port +""" +@component function FlowDivider(; p_int, n, name) + + #TODO: assert n >= 1 + + pars = @parameters begin + n = n + p_int = p_int + end + + vars = @variables begin + dm_a(t) = 0 + dm_b(t) = 0 + end + + systems = @named begin + port_a = HydraulicPort(; p_int) + port_b = HydraulicPort(; p_int) + open = Open(; p_int) + end + + eqs = [connect(port_a, port_b, open.port) + dm_a ~ port_a.dm + dm_b ~ dm_a / n + open.dm ~ dm_a - dm_b # extra flow dumps into an open port + # port_b.dm ~ dm_b # divided flow goes to port_b + ] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +@component function ValveBase(reversible = false; p_a_int, p_b_int, minimum_area = 0, + area_int, Cd, Cd_reverse = Cd, name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_int = area_int + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + end + + vars = @variables begin + area(t) = area_int + y(t) = area_int + end + + # let + # Opting for a more numerically stable constant density (use head factor to compensate if needed) + ρ = density_ref(port_a) #(full_density(port_a) + full_density(port_b)) / 2 + + x = if reversible + area + else + ifelse(area > minimum_area, area, minimum_area) + end + + # let ------ + Δp = port_a.p - port_b.p + dm = port_a.dm + c = if reversible + Cd + else + ifelse(Δp > 0, Cd, Cd_reverse) + end + + eqs = [0 ~ port_a.dm + port_b.dm + dm ~ regRoot(2 * Δp * ρ / c) * x + y ~ x] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + Valve(reversible = false; p_a_int, p_b_int, area_int, Cd, Cd_reverse = Cd, minimum_area = 0, name) + +Valve with `area` input and discharge coefficient `Cd` defined by https://en.wikipedia.org/wiki/Discharge_coefficient. The `Cd_reverse` parameter allows for directional flow restriction, making it possible to define a check valve. + +# Parameters: +- `p_a_int`: [Pa] initial pressure for `port_a` +- `p_b_int`: [Pa] initial pressure for `port_b` +- `area_int`: [m^2] initial valve opening +- `Cd`: discharge coefficient flowing from `a → b` +- `Cd_reverse`: discharge coefficient flowing from `b → a` +- `minimum_area`: when `reversible = false` applies a forced minimum area + +# Connectors: +- `port_a`: hydraulic port +- `port_b`: hydraulic port +- `area`: real input setting the valve `area`. When `reversible = true`, negative input reverses flow direction, otherwise a floor of `minimum_area` is enforced. +""" +@component function Valve(reversible = false; p_a_int, p_b_int, + area_int, Cd, Cd_reverse = Cd, + minimum_area = 0, + name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_int = area_int + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + area = RealInput() + base = ValveBase(reversible; p_a_int, p_b_int, area_int, Cd, Cd_reverse, + minimum_area) + end + + vars = [] + + eqs = [connect(base.port_a, port_a) + connect(base.port_b, port_b) + base.area ~ area.u] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [area.u => area_int]) +end + +@component function VolumeBase(; p_int, x_int = 0, area, dead_volume = 0, Χ1 = 1, Χ2 = 1, + name) + pars = @parameters begin + p_int = p_int + x_int = x_int + area = area + dead_volume = dead_volume + end + + systems = @named begin + port = HydraulicPort(; p_int) + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + rho(t) = liquid_density(port) + drho(t) = 0 + vol(t) = dead_volume + area * x_int + end + + # let + dm = port.dm + p = port.p + + eqs = [vol ~ dead_volume + area * x + D(x) ~ dx + D(rho) ~ drho + rho ~ full_density(port, p) + dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" + FixedVolume(; vol, p_int, name) + +Fixed fluid volume. + +# Parameters: +- `vol`: [m^3] fixed volume +- `p_int`: [Pa] initial pressure + +# Connectors: +- `port`: hydraulic port +""" +@component function FixedVolume(; vol, p_int, name) + pars = @parameters begin + p_int = p_int + vol = vol + end + + systems = @named begin + port = HydraulicPort(; p_int) + end + + vars = @variables begin + rho(t) = liquid_density(port) + drho(t) = 0 + end + + # let + dm = port.dm + p = port.p + + eqs = [D(rho) ~ drho + rho ~ full_density(port, p) + dm ~ drho * vol] + + ODESystem(eqs, t, vars, pars; name, systems) +end + +""" +DynamicVolume(N, add_inertia=true; p_int, area, x_int = 0, x_max, x_min = 0, x_damp = x_min, direction = +1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, head_factor = 1, Cd = 1e2, Cd_reverse = Cd, name) + +Volume with moving wall with `flange` connector for converting hydraulic energy to 1D mechanical. The `direction` argument aligns the mechanical port with the hydraulic port, useful when connecting two dynamic volumes together in oppsing directions to create an actuator. + +``` + ┌─────────────────┐ ─── + │ │ ▲ + │ │ +dm ────► │ │ area + │ │ + │ │ ▼ + └─────────────────┤ ─── + │ + └─► x (= ∫ flange.v * direction) +``` + +# Features: +- volume discretization with flow resistance and inertia: use `N` to control number of volume and resistance elements. Set `N=0` to turn off volume discretization. See `TubeBase` for more information about flow resistance. +- minimum volume flow shutoff with damping and directional resistance. Use `reversible=false` when problem defines volume position `x` and solves for `dm` to prevent numerical instability. + +# Parameters: +## volume +- `p_int`: [Pa] initial pressure +- `area`: [m^2] moving wall area +- `x_int`: [m] initial wall position +- `x_max`: [m] max wall position, needed for volume discretization to apply the correct volume sizing as a function of `x` +- `x_min`: [m] wall position that shuts off flow and prevents negative volume. +- `x_damp`: [m] wall position that initiates a linear damping region before reaching full flow shut off. Helps provide a smooth end stop. + +- `direction`: [+/-1] applies the direction conversion from the `flange` to `x` + +## flow resistance +- `perimeter`: [m] perimeter of the cross section (needed only for non-circular volumes) +- `shape_factor`: shape factor, see `friction_factor` function +- `head_factor`: effective length multiplier, used to account for addition friction from flow development and additional friction such as pipe bends, entrance/exit lossses, etc. + +## flow shut off and damping +- `Cd`: discharge coefficient for flow out of the volume. *Note: area is 1m² when valve is fully open. Ensure this does not induce unwanted flow resistance.* +- `Cd_reverse`: discharge coefficient for flow into the volume. Use a lower value to allow easy wall release, in some cases the wall can "stick". + + +# Connectors: +- `port`: hydraulic port +- `flange`: mechanical translational port +""" +@component function DynamicVolume(N, add_inertia = true, reversible = false; + p_int, + area, + x_int = 0, + x_max, + x_min = 0, + x_damp = x_min, + direction = +1, + + # Tube + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, + head_factor = 1, + + # Valve + Cd = 1e2, + Cd_reverse = Cd, + minimum_area = 0, + name) + @assert(N>=0, + "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") + @assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction" + + #TODO: How to set an assert effective_length >= length ?? + pars = @parameters begin + p_int = p_int + area = area + + x_int = x_int + x_max = x_max + x_min = x_min + x_damp = x_damp + + direction = direction + + perimeter = perimeter + shape_factor = shape_factor + head_factor = head_factor + + Cd = Cd + Cd_reverse = Cd_reverse + minimum_area = minimum_area + end + + vars = @variables x(t)=x_int vol(t)=x_int * area + + ports = @named begin + port = HydraulicPort(; p_int) + flange = MechanicalPort(; f = -direction * p_int * area) + damper = ValveBase(reversible; + p_a_int = p_int, + p_b_int = p_int, + area_int = 1, + Cd, + Cd_reverse, + minimum_area) + end + + pipe_bases = [] + for i in 1:N + comp = TubeBase(add_inertia; name = Symbol("p$i"), + shape_factor = ParentScope(shape_factor), + p_int = ParentScope(p_int), area = ParentScope(area), + length_int = 0, #set in equations + head_factor = ParentScope(head_factor), + perimeter = ParentScope(perimeter)) + push!(pipe_bases, comp) + end + + #TODO: How to handle x_int? + #TODO: Handle direction + @named moving_volume = VolumeBase(; + p_int, + x_int = 0, + area, + dead_volume = N == 0 ? area * x_int : 0, + Χ1 = N == 0 ? 1 : 0, + Χ2 = 1) + + ratio = (x - x_min) / (x_damp - x_min) + + damper_area = if reversible + one(x) + else + ifelse(x >= x_damp, one(x), ifelse((x < x_damp) & (x > x_min), ratio, zero(x))) + end + + eqs = [vol ~ x * area + D(x) ~ flange.v * direction + damper.area ~ damper_area + connect(port, damper.port_b)] + + volumes = [] + if N > 0 + Δx = ParentScope(x_max) / N + x₀ = ParentScope(x_int) + + for i in 1:N + length = ifelse(x₀ > Δx * i, + Δx, + ifelse(x₀ - Δx * (i - 1) > 0, + x₀ - Δx * (i - 1), + zero(Δx))) + + comp = VolumeBase(; name = Symbol("v$i"), p_int = ParentScope(p_int), + x_int = 0, + area = ParentScope(area), + dead_volume = ParentScope(area) * length, Χ1 = 1, Χ2 = 0) + + push!(volumes, comp) + end + + push!(eqs, connect(moving_volume.port, volumes[1].port, pipe_bases[1].port_a)) + push!(eqs, connect(pipe_bases[end].port_b, damper.port_a)) + for i in 2:N + push!(eqs, + connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)) + end + + for i in 1:N + push!(eqs, + volumes[i].dx ~ ifelse((vol >= (i - 1) * (x_max / N) * area) & + (vol < (i) * (x_max / N) * area), + flange.v * direction, 0)) + push!(eqs, pipe_bases[i].x ~ volumes[i].vol / volumes[i].area) + end + else + push!(eqs, connect(moving_volume.port, damper.port_a)) + end + + push!(eqs, moving_volume.dx ~ flange.v * direction) + push!(eqs, -moving_volume.port.p * area * direction ~ flange.f) + + ODESystem(eqs, t, vars, pars; name, + systems = [ports; pipe_bases; volumes; moving_volume], + defaults = [flange.v => 0]) +end + +@component function SpoolValve(reversible = false; p_a_int, p_b_int, x_int, Cd, d, name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + d = d + x_int = x_int + Cd = Cd + end + + systems = @named begin + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + flange = MechanicalPort() + valve = ValveBase(reversible; p_a_int, p_b_int, + area_int = ParentScope(x_int) * 2π * ParentScope(d), Cd) + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + end + + eqs = [D(x) ~ dx + flange.v ~ dx + flange.f ~ 0 #TODO: model flow force + connect(valve.port_a, port_a) + connect(valve.port_b, port_b) + valve.area ~ x * 2π * d] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end + +@component function SpoolValve2Way(reversible = false; p_s_int, p_a_int, p_b_int, p_r_int, + m, g, x_int, Cd, d, name) + pars = @parameters begin + p_s_int = p_s_int + p_a_int = p_a_int + p_b_int = p_b_int + p_r_int = p_r_int + + m = m + g = g + + x_int = x_int + + d = d + + Cd = Cd + end + + vars = [] + + systems = @named begin + vSA = SpoolValve(reversible; p_a_int = p_s_int, p_b_int = p_a_int, x_int, Cd, d) + vBR = SpoolValve(reversible; p_a_int = p_b_int, p_b_int = p_r_int, x_int, Cd, d) + + port_s = HydraulicPort(; p_int = p_s_int) + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + port_r = HydraulicPort(; p_int = p_r_int) + + mass = Mass(; m = m, g = g) + + flange = MechanicalPort() + end + + eqs = [connect(vSA.port_a, port_s) + connect(vSA.port_b, port_a) + connect(vBR.port_a, port_b) + connect(vBR.port_b, port_r) + connect(vSA.flange, vBR.flange, mass.flange, flange)] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end + +@component function Actuator(N, add_inertia = true, reversible = false; + p_a_int, + p_b_int, + area_a, + area_b, + perimeter_a = 2 * sqrt(area_a * pi), + perimeter_b = 2 * sqrt(area_b * pi), + length_a_int, + length_b_int, + shape_factor_a = 64, + shape_factor_b = 64, + head_factor_a = 1, + head_factor_b = 1, + m, + g, + x_int = 0, + minimum_volume_a = 0, + minimum_volume_b = 0, + damping_volume_a = minimum_volume_a, + damping_volume_b = minimum_volume_b, + Cd = 1e4, + Cd_reverse = Cd, + name) + pars = @parameters begin + p_a_int = p_a_int + p_b_int = p_b_int + area_a = area_a + area_b = area_b + perimeter_a = perimeter_a + perimeter_b = perimeter_b + shape_factor_a = shape_factor_a + shape_factor_b = shape_factor_b + head_factor_a = head_factor_a + head_factor_b = head_factor_b + x_int = x_int + length_a_int = length_a_int + length_b_int = length_b_int + minimum_volume_a = minimum_volume_a + minimum_volume_b = minimum_volume_b + damping_volume_a = damping_volume_a + damping_volume_b = damping_volume_b + Cd = Cd + Cd_reverse = Cd_reverse + m = m + g = g + end + + vars = @variables begin + x(t) = x_int + dx(t) = 0 + end + + total_length = length_a_int + length_b_int + + #TODO: include effective_length + systems = @named begin + vol_a = DynamicVolume(N, add_inertia, reversible; direction = +1, + p_int = p_a_int, + area = area_a, + x_int = length_a_int, + x_max = total_length, + x_min = minimum_volume_a / area_a, + x_damp = damping_volume_a / area_a, + perimeter = perimeter_a, + shape_factor = shape_factor_a, + head_factor = head_factor_a, + Cd, + Cd_reverse) + + vol_b = DynamicVolume(N, add_inertia, reversible; direction = -1, + p_int = p_b_int, + area = area_b, + x_int = length_b_int, + x_max = total_length, + x_min = minimum_volume_b / area_b, + x_damp = damping_volume_b / area_b, + perimeter = perimeter_b, + shape_factor = shape_factor_b, + head_factor = head_factor_b, + Cd, + Cd_reverse) + mass = Mass(; m, g) + port_a = HydraulicPort(; p_int = p_a_int) + port_b = HydraulicPort(; p_int = p_b_int) + flange = MechanicalPort() + end + + eqs = [connect(vol_a.port, port_a) + connect(vol_b.port, port_b) + connect(vol_a.flange, vol_b.flange, mass.flange, flange) + D(x) ~ dx + dx ~ vol_a.flange.v] + + ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) +end diff --git a/test/Blocks/sources.jl b/test/Blocks/sources.jl index 90e8e58aa..6c1ec6ca9 100644 --- a/test/Blocks/sources.jl +++ b/test/Blocks/sources.jl @@ -1,470 +1,477 @@ -using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq -using ModelingToolkitStandardLibrary.Blocks -using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, - smooth_square, smooth_step, smooth_ramp, - smooth_triangular, triangular, square -using OrdinaryDiffEq: ReturnCode.Success -using DynamicQuantities: @u_str - -@parameters t [unit = u"s"] -D = Differential(t) - -@testset "Constant" begin - @named src = Constant(k = 2) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u][end]≈2 atol=1e-3 -end - -@testset "TimeVaryingFunction" begin - f(t) = t^2 + 1 - vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 - @named src = TimeVaryingFunction(f) - @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈f.(sol.t) atol=1e-3 - @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10 atol=1e-3 # closed-form solution to integral -end - -@testset "Sine" begin - function sine(t, frequency, amplitude, phase, offset, start_time) - offset + ifelse(t < start_time, 0, - amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency = 1 - amplitude = 2 - phase = 0 - offset = 1 - start_time = 2 - δ = 1e-5 - @named int = Integrator() - - @named src = Sine(frequency = frequency, amplitude = amplitude, phase = phase, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈sine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 - - @named smooth_src = Sine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_sin.(smooth_sol.t, δ, frequency, amplitude, - phase, offset, start_time) atol=1e-3 -end - -@testset "Cosine" begin - function cosine(t, frequency, amplitude, phase, offset, start_time) - offset + ifelse(t < start_time, 0, - amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency = 1 - amplitude = 2 - phase = 0 - offset = 1 - start_time = 2 - δ = 1e-5 - @named int = Integrator() - - @named src = Cosine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈cosine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 - - @named smooth_src = Cosine(frequency = frequency, - amplitude = amplitude, - phase = phase, - offset = offset, - start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_cos.(smooth_sol.t, δ, frequency, amplitude, - phase, offset, start_time) atol=1e-3 -end - -@testset "ContinuousClock" begin - cont_clock(t, offset, start_time) = offset + ifelse(t < start_time, 0, t - start_time) - - offset, start_time = 1, 0 - - @named src = ContinuousClock(offset = offset, start_time = start_time) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈cont_clock.(sol.t, offset, start_time) atol=1e-3 -end - -@testset "Ramp" begin - function ramp(t, offset, height, duration, start_time) - offset + ifelse(t < start_time, 0, - ifelse(t < (start_time + duration), (t - start_time) * height / duration, - height)) - end - - offset, height, duration, start_time, δ = 1, 2, 2, 0, 1e-5 - @named int = Integrator() - - @named src = Ramp(offset = offset, height = height, duration = duration, - start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈ramp.(sol.t, offset, height, duration, start_time) atol=1e-3 - - start_time = 2 - @named smooth_src = Ramp(offset = offset, height = height, duration = duration, - start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_ramp.(smooth_sol.t, δ, height, duration, - offset, start_time) atol=1e-3 -end - -@testset "Step" begin - step(t, offset, height, start_time) = offset + ifelse(t < start_time, 0, height) - - offset, height, start_time, δ = 1, 2, 5, 1e-5 - @named int = Integrator() - - @named src = Step(offset = offset, height = height, start_time = start_time, - smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - - @test sol.retcode == Success - @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) atol=1e-2 - - # test with duration - duration = 1.2 - @named src = Step(offset = offset, height = height, start_time = start_time, - duration = duration, smooth = false) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance - - @test sol.retcode == Success - @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) - - step.(sol.t, 0, height, start_time + duration) atol=1e-2 - - @named smooth_src = Step(offset = offset, height = height, start_time = start_time, - smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u] ≈ - smooth_step.(smooth_sol.t, δ, height, offset, start_time) - - # with duration - @named smooth_src = Step(offset = offset, height = height, start_time = start_time, - smooth = true, duration = duration) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u] ≈ - smooth_step.(smooth_sol.t, δ, height, offset, start_time) - - smooth_step.(smooth_sol.t, δ, height, 0, start_time + duration) -end - -@testset "Square" begin - frequency = 1 - amplitude = 2 - offset = 1 - start_time = 2.5 - δ = 1e-5 - @named int = Integrator() - - @named src = Square(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - - @test sol.retcode == Success - @test sol[src.output.u]≈square.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 - - @named smooth_src = Square(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_square.(smooth_sol.t, δ, frequency, - amplitude, offset, start_time) atol=1e-3 -end - -@testset "Triangular" begin - frequency = 5 - amplitude = 1 - offset = 2 - start_time = 1 - δ = 1e-5 - @named int = Integrator() - - @named src = Triangular(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time) - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 4.0)) - sol = solve(prob, Rodas4(), saveat = 0.01) - - @test sol.retcode == Success - @test sol[src.output.u]≈triangular.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 - - @named smooth_src = Triangular(frequency = frequency, amplitude = amplitude, - offset = offset, start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 4.0)) - smooth_sol = solve(smooth_prob, Rodas4(), saveat = 0.01) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_triangular.(smooth_sol.t, δ, frequency, - amplitude, offset, start_time) atol=1e-3 -end - -@testset "ExpSine" begin - function exp_sine(t, amplitude, frequency, damping, phase, start_time) - offset + ifelse(t < start_time, 0, - amplitude * exp(-damping * (t - start_time)) * - sin(2 * pi * frequency * (t - start_time) + phase)) - end - - frequency, amplitude, damping = 3, 2, 0.10 - phase, offset, start_time, δ = 0, 0, 0, 1e-5 - @named src = ExpSine(frequency = frequency, amplitude = amplitude, damping = damping, - phase = phase, offset = offset, start_time = start_time) - @named int = Integrator() - @named iosys = ODESystem([ - connect(src.output, int.input), - ], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) - sol = solve(prob, Rodas4()) - @test sol.retcode == Success - @test sol[src.output.u]≈exp_sine.(sol.t, amplitude, frequency, damping, phase, - start_time) atol=1e-3 - - offset, start_time = 1, 2 - @named smooth_src = ExpSine(frequency = frequency, amplitude = amplitude, - damping = damping, phase = phase, offset = offset, - start_time = start_time, smooth = true) - @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), - ], - t, - systems = [int, smooth_src]) - smooth_sys = structural_simplify(smooth_iosys) - smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) - smooth_sol = solve(smooth_prob, Rodas4()) - - @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_damped_sin.(smooth_sol.t, δ, frequency, - amplitude, damping, phase, - offset, start_time) atol=1e-3 -end - -@testset "SampledData" begin - using DataInterpolations - - dt = 4e-4 - t_end = 10.0 - time_span = 0:dt:t_end - x = @. time_span^2 + 1.0 - - @testset "using Parameter type" begin - vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 - @named src = SampledData(Float64) - @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - s = complete(iosys) - prob = ODEProblem(sys, [], (0.0, t_end), [s.src.buffer => Parameter(x, dt)]; tofloat=false) - # prob = remake(prob; p = Parameter.(prob.p)) #<-- no longer needed with ModelingToolkit.jl PR #2231 - - sol = solve(prob, Rodas4(); initializealg = NoInit()) - @test sol.retcode == Success - @test sol[src.output.u][1] == 1.0 #check correct initial condition - - @test sol(time)[src.output.u]≈x atol=1e-3 - @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral - @test sol[dy][end]≈2 * time[end] atol=1e-3 - @test sol[ddy][end]≈2 atol=1e-3 - end - - @testset "using Vector Based" begin - vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 - @named src = SampledData(dt) - @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], - t, - systems = [int, src]) - sys = structural_simplify(iosys) - s = complete(iosys) - prob = ODEProblem(sys, [], (0.0, t_end), [s.src.buffer => x, s.src.sample_time => dt]; tofloat=false) - - sol = solve(prob, Rodas4(); initializealg = NoInit()) - @test sol.retcode == Success - @test sol[src.output.u][1] == 1.0 #check correct initial condition - - @test sol(time)[src.output.u]≈x atol=1e-3 - @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral - @test sol[dy][end]≈2 * time[end] atol=1e-3 - @test sol[ddy][end]≈2 atol=1e-3 - end -end - +using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq +using ModelingToolkitStandardLibrary.Blocks +using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, + smooth_square, smooth_step, smooth_ramp, + smooth_triangular, triangular, square +using OrdinaryDiffEq: ReturnCode.Success +using DynamicQuantities: @u_str + +@parameters t [unit = u"s"] +D = Differential(t) + +@testset "Constant" begin + @named src = Constant(k = 2) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u][end]≈2 atol=1e-3 +end + +@testset "TimeVaryingFunction" begin + f(t) = t^2 + 1 + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = TimeVaryingFunction(f) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈f.(sol.t) atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10 atol=1e-3 # closed-form solution to integral +end + +@testset "Sine" begin + function sine(t, frequency, amplitude, phase, offset, start_time) + offset + ifelse(t < start_time, 0, + amplitude * sin(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency = 1 + amplitude = 2 + phase = 0 + offset = 1 + start_time = 2 + δ = 1e-5 + @named int = Integrator() + + @named src = Sine(frequency = frequency, amplitude = amplitude, phase = phase, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈sine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 + + @named smooth_src = Sine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_sin.(smooth_sol.t, δ, frequency, amplitude, + phase, offset, start_time) atol=1e-3 +end + +@testset "Cosine" begin + function cosine(t, frequency, amplitude, phase, offset, start_time) + offset + ifelse(t < start_time, 0, + amplitude * cos(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency = 1 + amplitude = 2 + phase = 0 + offset = 1 + start_time = 2 + δ = 1e-5 + @named int = Integrator() + + @named src = Cosine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈cosine.(sol.t, frequency, amplitude, phase, offset, start_time) atol=1e-3 + + @named smooth_src = Cosine(frequency = frequency, + amplitude = amplitude, + phase = phase, + offset = offset, + start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_cos.(smooth_sol.t, δ, frequency, amplitude, + phase, offset, start_time) atol=1e-3 +end + +@testset "ContinuousClock" begin + cont_clock(t, offset, start_time) = offset + ifelse(t < start_time, 0, t - start_time) + + offset, start_time = 1, 0 + + @named src = ContinuousClock(offset = offset, start_time = start_time) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈cont_clock.(sol.t, offset, start_time) atol=1e-3 +end + +@testset "Ramp" begin + function ramp(t, offset, height, duration, start_time) + offset + ifelse(t < start_time, 0, + ifelse(t < (start_time + duration), (t - start_time) * height / duration, + height)) + end + + offset, height, duration, start_time, δ = 1, 2, 2, 0, 1e-5 + @named int = Integrator() + + @named src = Ramp(offset = offset, height = height, duration = duration, + start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈ramp.(sol.t, offset, height, duration, start_time) atol=1e-3 + + start_time = 2 + @named smooth_src = Ramp(offset = offset, height = height, duration = duration, + start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_ramp.(smooth_sol.t, δ, height, duration, + offset, start_time) atol=1e-3 +end + +@testset "Step" begin + step(t, offset, height, start_time) = offset + ifelse(t < start_time, 0, height) + + offset, height, start_time, δ = 1, 2, 5, 1e-5 + @named int = Integrator() + + @named src = Step(offset = offset, height = height, start_time = start_time, + smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + + @test sol.retcode == Success + @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) atol=1e-2 + + # test with duration + duration = 1.2 + @named src = Step(offset = offset, height = height, start_time = start_time, + duration = duration, smooth = false) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance + + @test sol.retcode == Success + @test sol[src.output.u]≈step.(sol.t, offset, height, start_time) - + step.(sol.t, 0, height, start_time + duration) atol=1e-2 + + @named smooth_src = Step(offset = offset, height = height, start_time = start_time, + smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4(), dtmax = 0.1) # set dtmax to prevent the solver from overstepping the entire step disturbance) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u] ≈ + smooth_step.(smooth_sol.t, δ, height, offset, start_time) + + # with duration + @named smooth_src = Step(offset = offset, height = height, start_time = start_time, + smooth = true, duration = duration) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u] ≈ + smooth_step.(smooth_sol.t, δ, height, offset, start_time) - + smooth_step.(smooth_sol.t, δ, height, 0, start_time + duration) +end + +@testset "Square" begin + frequency = 1 + amplitude = 2 + offset = 1 + start_time = 2.5 + δ = 1e-5 + @named int = Integrator() + + @named src = Square(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + + @test sol.retcode == Success + @test sol[src.output.u]≈square.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 + + @named smooth_src = Square(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_square.(smooth_sol.t, δ, frequency, + amplitude, offset, start_time) atol=1e-3 +end + +@testset "Triangular" begin + frequency = 5 + amplitude = 1 + offset = 2 + start_time = 1 + δ = 1e-5 + @named int = Integrator() + + @named src = Triangular(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time) + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 4.0)) + sol = solve(prob, Rodas4(), saveat = 0.01) + + @test sol.retcode == Success + @test sol[src.output.u]≈triangular.(sol.t, frequency, amplitude, offset, start_time) atol=1e-3 + + @named smooth_src = Triangular(frequency = frequency, amplitude = amplitude, + offset = offset, start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 4.0)) + smooth_sol = solve(smooth_prob, Rodas4(), saveat = 0.01) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_triangular.(smooth_sol.t, δ, frequency, + amplitude, offset, start_time) atol=1e-3 +end + +@testset "ExpSine" begin + function exp_sine(t, amplitude, frequency, damping, phase, start_time) + offset + ifelse(t < start_time, 0, + amplitude * exp(-damping * (t - start_time)) * + sin(2 * pi * frequency * (t - start_time) + phase)) + end + + frequency, amplitude, damping = 3, 2, 0.10 + phase, offset, start_time, δ = 0, 0, 0, 1e-5 + @named src = ExpSine(frequency = frequency, amplitude = amplitude, damping = damping, + phase = phase, offset = offset, start_time = start_time) + @named int = Integrator() + @named iosys = ODESystem([ + connect(src.output, int.input), + ], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 10.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[src.output.u]≈exp_sine.(sol.t, amplitude, frequency, damping, phase, + start_time) atol=1e-3 + + offset, start_time = 1, 2 + @named smooth_src = ExpSine(frequency = frequency, amplitude = amplitude, + damping = damping, phase = phase, offset = offset, + start_time = start_time, smooth = true) + @named smooth_iosys = ODESystem([ + connect(smooth_src.output, int.input), + ], + t, + systems = [int, smooth_src]) + smooth_sys = structural_simplify(smooth_iosys) + smooth_prob = ODEProblem(smooth_sys, Pair[int.x => 0.0], (0.0, 10.0)) + smooth_sol = solve(smooth_prob, Rodas4()) + + @test smooth_sol.retcode == Success + @test smooth_sol[smooth_src.output.u]≈smooth_damped_sin.(smooth_sol.t, δ, frequency, + amplitude, damping, phase, + offset, start_time) atol=1e-3 +end + +@testset "SampledData" begin + using DataInterpolations + + dt = 4e-4 + t_end = 10.0 + time_span = 0:dt:t_end + x = @. time_span^2 + 1.0 + + @testset "using Parameter type" begin + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = SampledData(Float64) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + s = complete(iosys) + prob = ODEProblem(sys, + [], + (0.0, t_end), + [s.src.buffer => Parameter(x, dt)]; + tofloat = false) + # prob = remake(prob; p = Parameter.(prob.p)) #<-- no longer needed with ModelingToolkit.jl PR #2231 + + sol = solve(prob, Rodas4(); initializealg = NoInit()) + @test sol.retcode == Success + @test sol[src.output.u][1] == 1.0 #check correct initial condition + + @test sol(time)[src.output.u]≈x atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral + @test sol[dy][end]≈2 * time[end] atol=1e-3 + @test sol[ddy][end]≈2 atol=1e-3 + end + + @testset "using Vector Based" begin + vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 + @named src = SampledData(dt) + @named int = Integrator() + @named iosys = ODESystem([y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], + t, + systems = [int, src]) + sys = structural_simplify(iosys) + s = complete(iosys) + prob = ODEProblem(sys, + [], + (0.0, t_end), + [s.src.buffer => x, s.src.sample_time => dt]; + tofloat = false) + + sol = solve(prob, Rodas4(); initializealg = NoInit()) + @test sol.retcode == Success + @test sol[src.output.u][1] == 1.0 #check correct initial condition + + @test sol(time)[src.output.u]≈x atol=1e-3 + @test sol[int.output.u][end]≈1 / 3 * 10^3 + 10.0 atol=1e-3 # closed-form solution to integral + @test sol[dy][end]≈2 * time[end] atol=1e-3 + @test sol[ddy][end]≈2 atol=1e-3 + end +end From 9556b0aa805dd046d991b071bfc96709ca7de988 Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 13 Sep 2023 20:06:28 -0400 Subject: [PATCH 34/80] bug fix --- src/Blocks/sources.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index 188097a0f..e69a3bf9c 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -568,9 +568,7 @@ function first_order_backwards_difference(t, buffer, Δt, circular_buffer) return (x1 - x0) / Δt end - - -function get_sampled_data(t, buffer::Vector{<:Real}, dt::Real, circular_buffer=true) +function get_sampled_data(t, buffer::Vector{T}, dt::T, circular_buffer = true) where {T<:Real} if t < 0 t = zero(t) end From 0ad16df3b0720205a4c5cf83de172cef044c0c1e Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Thu, 14 Sep 2023 17:45:41 -0400 Subject: [PATCH 35/80] Format --- src/Blocks/sources.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index e69a3bf9c..f021c4606 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -568,7 +568,10 @@ function first_order_backwards_difference(t, buffer, Δt, circular_buffer) return (x1 - x0) / Δt end -function get_sampled_data(t, buffer::Vector{T}, dt::T, circular_buffer = true) where {T<:Real} +function get_sampled_data(t, + buffer::Vector{T}, + dt::T, + circular_buffer = true) where {T <: Real} if t < 0 t = zero(t) end From 6d797b69a2bac129a0ad3f61cd217b8e6f658a5c Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Sat, 16 Sep 2023 16:25:59 -0400 Subject: [PATCH 36/80] Update to the latest MTK --- src/Hydraulic/IsothermalCompressible/components.jl | 4 +++- test/Hydraulic/isothermal_compressible.jl | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index 30491a705..0ceab5f58 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -116,7 +116,8 @@ Variable length internal flow model of the fully developed incompressible flow f 0 end - eqs = [0 ~ port_a.dm + port_b.dm] + eqs = [0 ~ port_a.dm + port_b.dm + domain_connect(port_a, port_b)] if variable_length push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) @@ -302,6 +303,7 @@ end end eqs = [0 ~ port_a.dm + port_b.dm + domain_connect(port_a, port_b) dm ~ regRoot(2 * Δp * ρ / c) * x y ~ x] diff --git a/test/Hydraulic/isothermal_compressible.jl b/test/Hydraulic/isothermal_compressible.jl index 8ccda0303..2e2e0c836 100644 --- a/test/Hydraulic/isothermal_compressible.jl +++ b/test/Hydraulic/isothermal_compressible.jl @@ -281,6 +281,12 @@ end sys = structural_simplify(system) defs = ModelingToolkit.defaults(sys) s = complete(system) + dt = 1e-4 + time = 0:dt:0.1 + x = @. 0.9 * (time > 0.015) * (time - 0.015)^2 - 25 * (time > 0.02) * (time - 0.02)^3 + defs[s.input.buffer] = Parameter(x, dt) + #prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.1); + # tofloat = false)#, jac = true) prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.1); tofloat = false, jac = true) @@ -292,15 +298,9 @@ end @test Symbol(defs[s.valve.port_r.ρ]) == Symbol(s.fluid.ρ) @test Symbol(defs[s.snk.port.ρ]) == Symbol(s.fluid.ρ) - dt = 1e-4 - time = 0:dt:0.1 - x = @. 0.9 * (time > 0.015) * (time - 0.015)^2 - 25 * (time > 0.02) * (time - 0.02)^3 - - defs[s.input.buffer] = Parameter(x, dt) # defs[s.piston.Cd_reverse] = 0.1 - p = Parameter.(ModelingToolkit.varmap_to_vars(defs, parameters(sys); tofloat = false)) - prob = remake(prob; p, tspan = (0, time[end])) + prob = remake(prob; tspan = (0, time[end])) @time sol = solve(prob, ImplicitEuler(nlsolve = NEWTON); adaptive = false, dt, initializealg = NoInit()) From a961765228f29c2ee623951f38f6abed5e2f7994 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Sat, 16 Sep 2023 18:31:31 -0400 Subject: [PATCH 37/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1c21a81ae..e4f737e98 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.2.1" +version = "2.3.1" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From 614f046a9b5ce34a916c6676a7028f849bff035b Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Tue, 26 Sep 2023 00:52:41 +0200 Subject: [PATCH 38/80] Documenter 1.0 upgrade --- .github/workflows/CI.yml | 4 ++++ .github/workflows/FormatCheck.yml | 2 +- docs/Project.toml | 3 +-- docs/make.jl | 10 ++------- docs/src/index.md | 35 ++++++++++--------------------- 5 files changed, 19 insertions(+), 35 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 023acbf5e..48a0f9232 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -3,9 +3,13 @@ on: pull_request: branches: - main + paths-ignore: + - 'docs/**' push: branches: - main + paths-ignore: + - 'docs/**' jobs: test: runs-on: ubuntu-latest diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index dd551501c..45bd09c47 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -3,7 +3,7 @@ name: format-check on: push: branches: - - 'master' + - 'main' - 'release-' tags: '*' pull_request: diff --git a/docs/Project.toml b/docs/Project.toml index e38c82671..034ba981d 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -11,9 +11,8 @@ Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" [compat] ControlSystemsBase = "1.1" DifferentialEquations = "7.6" -Documenter = "0.27" +Documenter = "1" IfElse = "0.1" ModelingToolkit = "8.67" -ModelingToolkitStandardLibrary = "2" OrdinaryDiffEq = "6.31" Plots = "1.36" diff --git a/docs/make.jl b/docs/make.jl index ed1072c80..3a6e05ec8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,14 +19,6 @@ include("pages.jl") makedocs(sitename = "ModelingToolkitStandardLibrary.jl", authors = "Julia Computing", - clean = true, - doctest = false, - linkcheck = true, - strict = [ - :linkcheck, - :doctest, - :example_block, - ], modules = [ModelingToolkit, ModelingToolkitStandardLibrary, ModelingToolkitStandardLibrary.Blocks, @@ -38,6 +30,8 @@ makedocs(sitename = "ModelingToolkitStandardLibrary.jl", ModelingToolkitStandardLibrary.Thermal, ModelingToolkitStandardLibrary.Hydraulic, ModelingToolkitStandardLibrary.Hydraulic.IsothermalCompressible], + clean = true, doctest = false, linkcheck = true, + warnonly = [:docs_block, :missing_docs, :cross_references], format = Documenter.HTML(assets = ["assets/favicon.ico"], canonical = "https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/"), pages = pages) diff --git a/docs/src/index.md b/docs/src/index.md index 90aed9b00..e6e05c0c4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -87,32 +87,19 @@ Pkg.status(; mode = PKGMODE_MANIFEST) # hide ``` -```@raw html -You can also download the -manifest file and the -project file. +link_manifest = "https://github.com/SciML/" * name * ".jl/tree/gh-pages/v" * version * + "/assets/Manifest.toml" +link_project = "https://github.com/SciML/" * name * ".jl/tree/gh-pages/v" * version * + "/assets/Project.toml" +Markdown.parse("""You can also download the +[manifest]($link_manifest) +file and the +[project]($link_project) +file. +""") ``` From 3e2a548aaf31066ad227d0fed50de8c16ff8de79 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Mon, 25 Sep 2023 18:14:41 +0200 Subject: [PATCH 39/80] avoid using array variable --- src/Blocks/math.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index cae7338d2..9fb8ea3af 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -24,11 +24,11 @@ end Gain.f(k; name) = Gain.f(; k, name) """ - MatrixGain(K::AbstractArray; name) + MatrixGain(; K::AbstractArray, name) Output the product of a gain matrix with the input signal vector. -# Parameters: +# Structural parameters: - `K`: Matrix gain @@ -50,7 +50,7 @@ Output the product of a gain matrix with the input signal vector. output = RealOutput(; nout = nout) end @equations begin - [output.u[i] ~ sum(getdefault(K)[i, j] * input.u[j] for j in 1:nin) + [output.u[i] ~ sum(K[i, j] * input.u[j] for j in 1:nin) for i in 1:nout]... end end From 5335e35cb022726b9007dd05eb94e3d9cb045fe9 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 26 Sep 2023 08:17:19 +0200 Subject: [PATCH 40/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e4f737e98..c779dad39 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.3.1" +version = "2.3.2" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From ed33f5d3cac66bcd10fb250f8ce575ceb522770f Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 26 Sep 2023 10:04:59 -0400 Subject: [PATCH 41/80] Fix heterogeneous parameters handling for `linearize_function` Co-authored-by: Fredrik Bagge Carlson --- src/Blocks/analysis_points.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index a6b90866d..9b4fec8b2 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -450,10 +450,17 @@ end # Methods above are implemented in terms of linearization_function, the method below creates wrappers for linearize for f in [:get_sensitivity, :get_comp_sensitivity, :get_looptransfer] - @eval function $f(sys, ap, args...; loop_openings = nothing, kwargs...) - lin_fun, ssys = $(Symbol(string(f) * "_function"))(sys, ap, args...; loop_openings, + @eval function $f(sys, + ap, + args...; + loop_openings = nothing, + op = Dict(), + p = DiffEqBase.NullParameters(), + kwargs...) + lin_fun, ssys = $(Symbol(string(f) * "_function"))(sys, ap, args...; op, p, + loop_openings, kwargs...) - ModelingToolkit.linearize(ssys, lin_fun; kwargs...), ssys + ModelingToolkit.linearize(ssys, lin_fun; op, p, kwargs...), ssys end end From e0f851facda8f5838043267732fb6fd44d2f3886 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 26 Sep 2023 11:28:21 -0400 Subject: [PATCH 42/80] Relax tests --- test/Thermal/thermal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Thermal/thermal.jl b/test/Thermal/thermal.jl index 1c705272d..0e9711a65 100644 --- a/test/Thermal/thermal.jl +++ b/test/Thermal/thermal.jl @@ -241,7 +241,7 @@ end @test sol.retcode == Success @test sol[T_winding.T] == sol[winding.T] @test sol[T_core.T] == sol[core.T] - @test sol[-core.port.Q_flow] == + @test sol[-core.port.Q_flow] ≈ sol[coreLosses.port.Q_flow + convection.solid.Q_flow + winding2core.port_b.Q_flow] @test sol[T_winding.T][end] >= 500 # not good but better than nothing @test sol[T_core.T] <= sol[T_winding.T] From 48671832405ffb3e10a82674900f517512a1318f Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Tue, 26 Sep 2023 13:20:29 -0400 Subject: [PATCH 43/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c779dad39..3dcd582b4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.3.2" +version = "2.3.3" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From ace8565ec15b2bddb880db71275f2abebf393608 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 29 Sep 2023 08:27:28 +0200 Subject: [PATCH 44/80] add info annotation with common mistake --- src/Blocks/analysis_points.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index 9b4fec8b2..e38402b38 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -520,6 +520,11 @@ get_comp_sensitivity Compute the (linearized) loop-transfer function in analysis point `ap`, from `ap.out` to `ap.in`. +!!! info "Negative feedback" + + Feedback loops often use negative feedback, and the computed loop-transfer function will in this case have the negative feedback included. Standard analysis tools often assume a loop-transfer function without the negative gain built in, and the result of this function may thus need negation before use. + + !!! danger "Experimental" The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. From 437487d8b0b4c969ca7d59c484da991ac8ce75f4 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 3 Oct 2023 15:19:02 +0200 Subject: [PATCH 45/80] improve docstring and interface to PI controller --- src/Blocks/continuous.jl | 20 ++++++++++++++++---- test/Blocks/continuous.jl | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 852120189..3e1405538 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -172,14 +172,19 @@ Initial value of the state `x` can be set with `x`, and of derivative state `xd` end """ - PI(;name, gainPI.k = 1.0, T, int.x = 0.0) + PI(;name, k = 1.0, T = 1.0, int.x = 0.0) Textbook version of a PI-controller without actuator saturation and anti-windup measure. -The proportional gain can be set with `gainPI.k` +The proportional gain can be set with `k` Initial value of integrator state `x` can be set with `int.x` -# Parameters: +The PI controller is implemented on standard form: +```math +U(s) = k (1 + \\dfrac{1}{sT}) E(S) +``` +# Parameters: + - `k`: Proportional gain - `T`: [s] Integrator time constant (T>0 required) # Connectors: @@ -191,7 +196,8 @@ See also [`LimPI`](@ref) """ @mtkmodel PI begin @parameters begin - T, [description = "Integrator time constant"] + k=1.0, [description = "Proportional gain"] + T=1.0, [description = "Integrator time constant"] end begin @symcheck T > 0 || @@ -294,6 +300,12 @@ end Text-book version of a PI-controller with actuator saturation and anti-windup measure. +The PI controller is implemented on standard form +```math +u(t) = sat(k (e(t) + ∫\\dfrac{1}{T}e(t) dt) ) +``` +The simplified expression above is given without the anti-windup protection. + # Parameters: - `k`: Proportional gain diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index 8b778c518..6e96cd62c 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -148,7 +148,7 @@ end @testset "PI" begin re_val = 2 @named ref = Constant(; k = re_val) - @named pi_controller = PI(int.k = 1, T = 1) + @named pi_controller = PI(k = 1, T = 1) @named plant = Plant() @named fb = Feedback() @named model = ODESystem([ From f612f7af3adf8f438f5c99a8801fde4b0b033b84 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 3 Oct 2023 15:27:55 +0200 Subject: [PATCH 46/80] format --- src/Blocks/continuous.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 3e1405538..1efa54d5b 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -196,8 +196,8 @@ See also [`LimPI`](@ref) """ @mtkmodel PI begin @parameters begin - k=1.0, [description = "Proportional gain"] - T=1.0, [description = "Integrator time constant"] + k = 1.0, [description = "Proportional gain"] + T = 1.0, [description = "Integrator time constant"] end begin @symcheck T > 0 || From c420a515e3fea0a9b118220fe04fc02e540558cd Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 3 Oct 2023 18:23:18 +0200 Subject: [PATCH 47/80] Update continuous.jl --- src/Blocks/continuous.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 1efa54d5b..f3100eaff 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -206,7 +206,7 @@ See also [`LimPI`](@ref) @components begin err_input = RealInput() # control error ctr_output = RealOutput() # control signal - gainPI = Gain(; k = 1.0) + gainPI = Gain(; k) addPI = Add() int = Integrator(k = 1 / T, x = 0.0) end From 9027bba5ac94ac6f5e8e32a33228ef1ceb9883b1 Mon Sep 17 00:00:00 2001 From: Frames White Date: Tue, 3 Oct 2023 20:40:51 +0800 Subject: [PATCH 48/80] Fix frule --- src/Hydraulic/IsothermalCompressible/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hydraulic/IsothermalCompressible/utils.jl b/src/Hydraulic/IsothermalCompressible/utils.jl index d3fce9840..cc937bbf2 100644 --- a/src/Hydraulic/IsothermalCompressible/utils.jl +++ b/src/Hydraulic/IsothermalCompressible/utils.jl @@ -119,7 +119,7 @@ end Symbolics.derivative(::typeof(friction_factor), args, ::Val{1}) = 0 Symbolics.derivative(::typeof(friction_factor), args, ::Val{4}) = 0 function ChainRulesCore.frule(_, ::typeof(friction_factor), args...) - (friction_factor(args...), ChainRulesCore.ZeroTangent) + (friction_factor(args...), ChainRulesCore.ZeroTangent()) end function transition(x1, x2, y1, y2, x) From da7e3f2a25da75ec54aeddfe5ba97c9e78d859e5 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 4 Oct 2023 00:54:20 -0400 Subject: [PATCH 49/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3dcd582b4..2e2261ddb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.3.3" +version = "2.3.4" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From c246fb826d893e8d2abaebb91a819c51a29e5e34 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 3 Oct 2023 14:54:14 +0200 Subject: [PATCH 50/80] initialize all relevant variables for SISO --- src/Blocks/nonlinear.jl | 3 ++- src/Blocks/utils.jl | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Blocks/nonlinear.jl b/src/Blocks/nonlinear.jl index 416166cc4..847c11190 100644 --- a/src/Blocks/nonlinear.jl +++ b/src/Blocks/nonlinear.jl @@ -18,7 +18,8 @@ Limit the range of a signal. """ @component function Limiter(; name, y_max, y_min = y_max > 0 ? -y_max : -Inf) @symcheck y_max ≥ y_min || throw(ArgumentError("`y_min` must be smaller than `y_max`")) - @named siso = SISO() + m = (y_max + y_min) / 2 + @named siso = SISO(u_start = m, y_start = m) # Default signals to center of saturation to minimize risk of saturation while linearizing etc. @unpack u, y = siso pars = @parameters y_max=y_max [description = "Maximum allowed output of Limiter $name"] y_min=y_min [ description = "Minimum allowed output of Limiter $name", diff --git a/src/Blocks/utils.jl b/src/Blocks/utils.jl index 9254db35a..1246fbffd 100644 --- a/src/Blocks/utils.jl +++ b/src/Blocks/utils.jl @@ -106,8 +106,8 @@ Single input single output (SISO) continuous system block. y(t) = y_start, [description = "Output of SISO system"] end @components begin - input = RealInput(u_start = 0.0) - output = RealOutput(u_start = 0.0) + input = RealInput(u_start = u_start) + output = RealOutput(u_start = y_start) end @equations begin u ~ input.u From 64245174125808005f59c56c8c82e984b9d23435 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Tue, 17 Oct 2023 15:46:24 +0200 Subject: [PATCH 51/80] nicify some docstrings and descriptions --- src/Blocks/continuous.jl | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index f3100eaff..acef58336 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -1,8 +1,8 @@ """ Integrator(;name, k = 1, x = 0.0) -Outputs `y = ∫k*u dt`, corresponding to the transfer function `1/s`. -Initial value of integrator state `x` can be set with `x` +Outputs `y = ∫k*u dt`, corresponding to the transfer function ``1/s``. +Initial value of integrator state ``x`` can be set with `x` # Connectors: @@ -19,7 +19,7 @@ Initial value of integrator state `x` can be set with `x` x(t) = 0.0, [description = "State of Integrator"] end @parameters begin - k = 1, [description = "Gain of Integrator"] + k = 1, [description = "Gain"] end @equations begin D(x) ~ k * u @@ -30,7 +30,7 @@ end Derivative(; name, k = 1, T, x = 0.0) Outputs an approximate derivative of the input. The transfer function of this block is -Initial value of the state `x` can be set with `x` +Initial value of the state ``x`` can be set with `x` ``` k k @@ -57,11 +57,11 @@ A smaller `T` leads to a more ideal approximation of the derivative. @mtkmodel Derivative begin @extend u, y = siso = SISO() @variables begin - x(t) = 0.0, [description = "State of Derivative"] + x(t) = 0.0, [description = "Derivative-filter state"] end @parameters begin - T = T, [description = "Time constant of Derivative"] - k = 1, [description = "Gain of Derivative"] + T = T, [description = "Time constant"] + k = 1, [description = "Gain"] end begin @symcheck T > 0 || @@ -77,8 +77,8 @@ end FirstOrder(; name, k = 1.0, T, x = 0.0, lowpass = true) A first-order filter with a single real pole in `s = -T` and gain `k`. If `lowpass=true` (default), the transfer function -is given by `Y(s)/U(s) = ` -Initial value of the state `x` can be set with `x` +is given by ``Y(s)/U(s) = `` + ``` k @@ -94,10 +94,12 @@ sT + 1 - k sT + 1 ``` +Initial value of the state `x` can be set with `x` + # Parameters: - `k`: Gain - - `T`: [s] Time constants (T>0 required) + - `T`: [s] Time constant (T>0 required) # Connectors: @@ -108,13 +110,15 @@ See also [`SecondOrder`](@ref) """ @mtkmodel FirstOrder begin @extend u, y = siso = SISO() + @structural_parameters begin + lowpass = true + end @variables begin x(t) = 0.0, [description = "State of FirstOrder filter"] end @parameters begin - lowpass = true - T = T, [description = "Time constant of FirstOrder filter"] - k = 1.0, [description = "Gain of FirstOrder"] + T = T, [description = "Time constant"] + k = 1.0, [description = "Gain"] end begin @symcheck T > 0 || @@ -122,12 +126,12 @@ See also [`SecondOrder`](@ref) end @equations begin D(x) ~ (k * u - x) / T - getdefault(lowpass) ? y ~ x : y ~ k * u - x + lowpass ? y ~ x : y ~ k * u - x end end """ - SecondOrder(; name, k = 1.0, w, d, x = 0.0, xd = 0.0) + SecondOrder(; name, k = 1.0, w = 1.0, d = 1.0, x = 0.0, xd = 0.0) A second-order filter with gain `k`, a bandwidth of `w` rad/s and relative damping `d`. The transfer function is given by `Y(s)/U(s) = ` @@ -160,9 +164,9 @@ Initial value of the state `x` can be set with `x`, and of derivative state `xd` xd(t) = 0.0, [description = "Derivative state of SecondOrder filter"] end @parameters begin - k = 1.0, [description = "Gain of SecondOrder"] - w, [description = "Bandwidth of SecondOrder"] - d, [description = "Relative damping of SecondOrder"] + k = 1.0, [description = "Gain"] + w = 1.0, [description = "Bandwidth (angular frequency)"] + d = 1.0, [description = "Relative damping"] end @equations begin D(x) ~ xd From 1d668cc8024e4a27d1feafd7b1854be6d0c62b1f Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 25 Oct 2023 06:00:54 -0400 Subject: [PATCH 52/80] fixes free test --- test/Mechanical/translational.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Mechanical/translational.jl b/test/Mechanical/translational.jl index 1587a43a6..58b825c49 100644 --- a/test/Mechanical/translational.jl +++ b/test/Mechanical/translational.jl @@ -12,7 +12,7 @@ D = Differential(t) @testset "Free" begin function System(; name) systems = @named begin - acc = TV.Acceleration() + acc = TV.Acceleration(false) a = Constant(; k = -10, output.unit = u"m/s^2") mass = TV.Mass(; m = 100) free = TV.Free() From 647b8119dbc97da36f9a315efa44d9457ab7546a Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Thu, 26 Oct 2023 08:56:49 +0200 Subject: [PATCH 53/80] add generic transfer function (#233) * add generic transfer function * formatting * add test --- src/Blocks/Blocks.jl | 2 +- src/Blocks/continuous.jl | 82 +++++++++++++++++++++++++++++++++++++++ test/Blocks/continuous.jl | 68 ++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index c391e3743..85040d582 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -26,7 +26,7 @@ include("sources.jl") export Limiter, DeadZone, SlewRateLimiter include("nonlinear.jl") -export Integrator, Derivative, FirstOrder, SecondOrder, StateSpace +export Integrator, Derivative, FirstOrder, SecondOrder, StateSpace, TransferFunction export PI, LimPI, PID, LimPID include("continuous.jl") diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index acef58336..eb7a331da 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -553,3 +553,85 @@ linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u end StateSpace(A, B, C, D = nothing; kwargs...) = StateSpace(; A, B, C, D, kwargs...) + +symbolic_eps(t) = eps(t) +@register_symbolic symbolic_eps(t) + +""" + TransferFunction(; b, a, name) + +A single input, single output, linear time-invariant system provided as a transfer-function. +``` +Y(s) = b(s) / a(s) U(s) +``` +where `b` and `a` are vectors of coefficients of the numerator and denominator polynomials, respectively, ordered such that the coefficient of the highest power of `s` is first. + +The internal state realization is on controller canonical form, with state variable `x`, output variable `y` and input variable `u`. For numerical robustness, the realization used by the integrator is scaled by the last entry of the `a` parameter. The internally scaled state variable is available as `x_scaled`. + +To set the initial state, it's recommended to set the initial condition for `x`, and let that of `x_scaled` be computed automatically. + +# Parameters: +- `b`: Numerator polynomial coefficients, e.g., `2s + 3` is specified as `[2, 3]` +- `a`: Denomenator polynomial coefficients, e.g., `s² + 2ωs + ω^2` is specified as `[1, 2ω, ω^2]` + +# Connectors: + - `input` + - `output` + +See also [`StateSpace`](@ref) which handles MIMO systems, as well as [ControlSystemsMTK.jl](https://juliacontrol.github.io/ControlSystemsMTK.jl/stable/) for an interface between [ControlSystems.jl](https://juliacontrol.github.io/ControlSystems.jl/stable/) and ModelingToolkit.jl for advanced manipulation of transfer functions and linear statespace systems. For linearization, see [`linearize`](@ref) and [Linear Analysis](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/linear_analysis/). +""" +@component function TransferFunction(; b = [1], a = [1, 1], name) + nb = length(b) + na = length(a) + nb <= na || + error("Transfer function is not proper, the numerator must not be longer than the denominator") + nx = na - 1 + nbb = max(0, na - nb) + + @named begin + input = RealInput() + output = RealOutput() + end + + @parameters begin + b[1:nb] = b, + [ + description = "Numerator coefficients of transfer function (e.g., 2s + 3 is specified as [2,3])", + ] + a[1:na] = a, + [ + description = "Denominator coefficients of transfer function (e.g., `s² + 2ωs + ω^2` is specified as [1, 2ω, ω^2])", + ] + bb[1:(nbb + nb)] = [zeros(nbb); b] + d = bb[1] / a[1], [description = "Direct feedthrough gain"] + end + + a = collect(a) + @parameters a_end = ifelse(a[end] > 100 * symbolic_eps(sqrt(a' * a)), a[end], 1.0) + + pars = [collect(b); a; collect(bb); d; a_end] + @variables begin + x(t)[1:nx] = zeros(nx), + [description = "State of transfer function on controller canonical form"] + x_scaled(t)[1:nx] = collect(x) * a_end, [description = "Scaled vector x"] + u(t), [description = "Input of transfer function"] + y(t), [description = "Output of transfer function"] + end + + x = collect(x) + x_scaled = collect(x_scaled) + + sts = [x; x_scaled; y; u] + + if nx == 0 + eqs = [y ~ d * u] + else + eqs = [D(x_scaled[1]) ~ (-a[2:na]'x_scaled + a_end * u) / a[1] + D.(x_scaled[2:nx]) .~ x_scaled[1:(nx - 1)] + y ~ ((bb[2:na] - d * a[2:na])'x_scaled) / a_end + d * u + x .~ x_scaled ./ a_end] + end + push!(eqs, input.u ~ u) + push!(eqs, output.u ~ y) + compose(ODESystem(eqs, t, sts, pars; name = name), input, output) +end diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index 6e96cd62c..5506bb6b7 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -2,6 +2,7 @@ using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str +using Test @parameters t [unit = u"s"] @@ -409,4 +410,71 @@ end @test sol[plant.output.u][end]≈re_val atol=1e-3 # zero control error after 100s @test all(-1.5 .<= sol[pid_controller.ctr_output.u] .<= 1.5) # test limit end + + @testset "TransferFunction" begin + pt1_func(t, k, T) = k * (1 - exp(-t / T)) # Known solution to first-order system + + @named c = Constant(; k = 1) + @named pt1 = TransferFunction(b = [1.2], a = [3.14, 1]) + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[pt1.output.u]≈pt1_func.(sol.t, 1.2, 3.14) atol=1e-3 + + # Test logic for a_end by constructing an integrator + @named c = Constant(; k = 1) + @named pt1 = TransferFunction(b = [1.2], a = [3.14, 0]) + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[pt1.output.u] ≈ sol.t .* (1.2 / 3.14) + @test sol[pt1.x[1]] ≈ sol.t .* (1 / 3.14) # Test that scaling of state works properly + + # Test higher order + + function pt2_func(t, k, w, d) + y = if d == 0 + -k * (-1 + cos(t * w)) + else + d = complex(d) + real(k * (1 + + (-cosh(sqrt(-1 + d^2) * t * w) - + (d * sinh(sqrt(-1 + d^2) * t * w)) / sqrt(-1 + d^2)) / + exp(d * t * w))) + end + end + + k, w, d = 1.0, 1.0, 0.5 + @named pt1 = TransferFunction(b = [w^2], a = [1, 2d * w, w^2]) + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[pt1.output.u]≈pt2_func.(sol.t, k, w, d) atol=1e-3 + + # test zeros (high-pass version of first test) + @named c = Constant(; k = 1) + @named pt1 = TransferFunction(b = [1, 0], a = [1, 1]) + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test sol[pt1.output.u]≈1 .- pt1_func.(sol.t, 1, 1) atol=1e-3 + @test sol[pt1.x[1]]≈pt1_func.(sol.t, 1, 1) atol=1e-3 # Test that scaling of state works properly + + # Test with no state + @named pt1 = TransferFunction(b = [2.7], a = [pi]) + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) + sys = structural_simplify(iosys) + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) + sol = solve(prob, Rodas4()) + @test sol.retcode == Success + @test all(==(2.7 / pi), sol[pt1.output.u]) + end end From 6548133938c3f2642bf9e5b2c83b8b9df2062cf6 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Sat, 28 Oct 2023 18:00:25 +0200 Subject: [PATCH 54/80] add TransferFunction to API page (#238) --- docs/src/API/blocks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/API/blocks.md b/docs/src/API/blocks.md index 0c113fbdc..11f241fd7 100644 --- a/docs/src/API/blocks.md +++ b/docs/src/API/blocks.md @@ -80,6 +80,7 @@ Derivative FirstOrder SecondOrder StateSpace +TransferFunction PI LimPI PID From 9f8dcc7261d79db71a2d8c3e8575e1d202cc5a78 Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Wed, 13 Dec 2023 19:15:23 +0100 Subject: [PATCH 55/80] typos ci --- .github/dependabot.yml | 3 +++ .github/workflows/SpellCheck.yml | 13 +++++++++++++ .typos.toml | 4 ++++ README.md | 2 +- docs/src/index.md | 2 +- docs/src/tutorials/input_component.md | 6 +++--- src/Blocks/analysis_points.jl | 2 +- src/Blocks/continuous.jl | 2 +- src/Electrical/Digital/logic.jl | 4 ++-- src/Hydraulic/IsothermalCompressible/components.jl | 2 +- src/ModelingToolkitStandardLibrary.jl | 2 +- test/Blocks/continuous.jl | 2 +- test/Blocks/test_analysis_points.jl | 2 +- test/Electrical/digital.jl | 2 +- 14 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/SpellCheck.yml create mode 100644 .typos.toml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 700707ced..1e8a051e2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,6 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + ignore: + - dependency-name: "crate-ci/typos" + update-types: ["version-update:semver-patch"] diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml new file mode 100644 index 000000000..599253c8c --- /dev/null +++ b/.github/workflows/SpellCheck.yml @@ -0,0 +1,13 @@ +name: Spell Check + +on: [pull_request] + +jobs: + typos-check: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v3 + - name: Check spelling + uses: crate-ci/typos@v1.16.23 \ No newline at end of file diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 000000000..9b079dcfc --- /dev/null +++ b/.typos.toml @@ -0,0 +1,4 @@ +[default.extend-words] +Nd = "Nd" +nin = "nin" +coul = "coul" \ No newline at end of file diff --git a/README.md b/README.md index 52e6f84e9..973f252a4 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ the documentation, which contains the unreleased features. ## Libraries -The following are the constituant libraries of the ModelingToolkit Standard Library. +The following are the constituent libraries of the ModelingToolkit Standard Library. - [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) - [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) diff --git a/docs/src/index.md b/docs/src/index.md index e6e05c0c4..b0319ca60 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -21,7 +21,7 @@ Pkg.add("ModelingToolkitStandardLibrary") ## Libraries -The following are the constituant libraries of the ModelingToolkit Standard Library. +The following are the constituent libraries of the ModelingToolkit Standard Library. - [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) - [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) diff --git a/docs/src/tutorials/input_component.md b/docs/src/tutorials/input_component.md index cb4f50041..7347c1213 100644 --- a/docs/src/tutorials/input_component.md +++ b/docs/src/tutorials/input_component.md @@ -47,7 +47,7 @@ prob = ODEProblem(sys, [], (0, time[end])) sol = solve(prob, ImplicitEuler()) ``` -If we want to run a new data set, this requires building a new `LinearInterpolation` and `ODESystem` followed by running `structural_simplify`, all of which takes time. Therefore, to run serveral pieces of data it's better to re-use an `ODESystem`. The next couple methods will demonstrate how to do this. +If we want to run a new data set, this requires building a new `LinearInterpolation` and `ODESystem` followed by running `structural_simplify`, all of which takes time. Therefore, to run several pieces of data it's better to re-use an `ODESystem`. The next couple methods will demonstrate how to do this. ## Custom Component with External Data @@ -105,7 +105,7 @@ Additional code could be added to resolve this issue, for example by using a `Re ## `SampledData` Component -To resolve the issues presented above, the `ModelingToolkitStandardLibrary.Blocks.SampledData` component can be used which allows for a resusable `ODESystem` and self contained data which ensures a solution which remains valid for it's lifetime. Now it's possible to also parallize the call to `solve()`. +To resolve the issues presented above, the `ModelingToolkitStandardLibrary.Blocks.SampledData` component can be used which allows for a resusable `ODESystem` and self contained data which ensures a solution which remains valid for it's lifetime. Now it's possible to also parallelize the call to `solve()`. ```julia function System(; name) @@ -146,4 +146,4 @@ sol2 = Ref{ODESolution}() end ``` -Note, in the above example, we can build the system with an empty `SampledData` component, only setting the expected data type: `@named src = SampledData(Float64)`. It's also possible to initialize the component with real sampled data: `@named src = SampledData(data, dt)`. Additionally note that before running an `ODEProblem` using the `SampledData` component, one must be careful about the parameter vector Type. The `SampledData` component contains a `buffer` parameter of type `Parameter`, therefore we must generate the problem using `tofloat=false`. This will initially give a parameter vector of type `Vector{Any}` with a mix of numbers and `Parameter` type. We can convert the vector to a uniform `Parameter` type by running `p = Parameter.(p)`. This will wrap all the single values in a `Parameter` which will be mathmatically equivalent to a `Number`. +Note, in the above example, we can build the system with an empty `SampledData` component, only setting the expected data type: `@named src = SampledData(Float64)`. It's also possible to initialize the component with real sampled data: `@named src = SampledData(data, dt)`. Additionally note that before running an `ODEProblem` using the `SampledData` component, one must be careful about the parameter vector Type. The `SampledData` component contains a `buffer` parameter of type `Parameter`, therefore we must generate the problem using `tofloat=false`. This will initially give a parameter vector of type `Vector{Any}` with a mix of numbers and `Parameter` type. We can convert the vector to a uniform `Parameter` type by running `p = Parameter.(p)`. This will wrap all the single values in a `Parameter` which will be mathematically equivalent to a `Number`. diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index e38402b38..b7690692d 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -52,7 +52,7 @@ eqs = [connect(P.output, C.input) connect(C.output, :plant_input, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :feedback_system) -matrices_S, _ = get_sensitivity(sys, :plant_input) # Compute the matrices of a state-space representation of the (input) sensitivity funciton. +matrices_S, _ = get_sensitivity(sys, :plant_input) # Compute the matrices of a state-space representation of the (input) sensitivity function. matrices_T, _ = get_comp_sensitivity(sys, :plant_input) ``` diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index eb7a331da..2661c7124 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -572,7 +572,7 @@ To set the initial state, it's recommended to set the initial condition for `x`, # Parameters: - `b`: Numerator polynomial coefficients, e.g., `2s + 3` is specified as `[2, 3]` -- `a`: Denomenator polynomial coefficients, e.g., `s² + 2ωs + ω^2` is specified as `[1, 2ω, ω^2]` +- `a`: Denominator polynomial coefficients, e.g., `s² + 2ωs + ω^2` is specified as `[1, 2ω, ω^2]` # Connectors: - `input` diff --git a/src/Electrical/Digital/logic.jl b/src/Electrical/Digital/logic.jl index 675a0d7ce..ed6f52ffa 100644 --- a/src/Electrical/Digital/logic.jl +++ b/src/Electrical/Digital/logic.jl @@ -1,10 +1,10 @@ -@enum Logic Uninitialized=1 ForcingUnknown ForcingZero ForcingOne HighImpedence WeakUnknown WeakZero WeakOne DontCare +@enum Logic Uninitialized=1 ForcingUnknown ForcingZero ForcingOne HighImpedance WeakUnknown WeakZero WeakOne DontCare const U = Uninitialized const X = ForcingUnknown const F0 = ForcingZero const F1 = ForcingOne -const Z = HighImpedence +const Z = HighImpedance const W = WeakUnknown const L = WeakZero const H = WeakOne diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index 0ceab5f58..91f6bd54c 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -497,7 +497,7 @@ dm ────► │ │ area name) @assert(N>=0, "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") - @assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction" + @assert (direction == +1)||(direction == -1) "direction argument must be +/-1, found $direction" #TODO: How to set an assert effective_length >= length ?? pars = @parameters begin diff --git a/src/ModelingToolkitStandardLibrary.jl b/src/ModelingToolkitStandardLibrary.jl index bff0ab19b..9b0b6259a 100644 --- a/src/ModelingToolkitStandardLibrary.jl +++ b/src/ModelingToolkitStandardLibrary.jl @@ -9,7 +9,7 @@ Omits the check expression if the argument `J` is symbolic. """ macro symcheck(ex) ex.args[1].head === :call || - error("Expected an expresion on the form sym > val || error()") + error("Expected an expression on the form sym > val || error()") sym = ex.args[1].args[2] quote _issymbolic(x) = !(unwrap(x) isa Real) diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index 5506bb6b7..bd9a4a7ae 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -8,7 +8,7 @@ using Test #= Testing strategy: -The general strategy is to test systems using simple intputs where the solution +The general strategy is to test systems using simple inputs where the solution is known on closed form. For algebraic systems (without differential variables), an integrator with a constant input is often used together with the system under test. =# diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index 9a9331b52..dcb15d6e5 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -144,7 +144,7 @@ lsys = sminreal(ss(matrices...)) matrices_So, _ = get_sensitivity(sys_outer, :inner_plant_output) lsyso = sminreal(ss(matrices_So...)) -@test lsys == lsyso || lsys == -1 * lsyso * (-1) # Output and input sensitivites are equal for SISO systems +@test lsys == lsyso || lsys == -1 * lsyso * (-1) # Output and input sensitivities are equal for SISO systems ## A more complicated test case using ModelingToolkit, OrdinaryDiffEq, LinearAlgebra diff --git a/test/Electrical/digital.jl b/test/Electrical/digital.jl index 7b5740f4c..b4264031c 100644 --- a/test/Electrical/digital.jl +++ b/test/Electrical/digital.jl @@ -32,7 +32,7 @@ using OrdinaryDiffEq: ReturnCode.Success @test typeof(logic.logic) == Vector{Logic} @test get_logic_level(logic) == [1, 6, 2, 4] - # Predefiend logic vectors + # Predefined logic vectors @test std_ulogic.logic == [U, X, F0, F1, Z, W, L, H, DC] @test UX01.logic == [U, X, F0, F1] @test UX01Z.logic == [U, X, F0, F1, Z] From baa5458d8a62b675d4295f7ec0ef15bf8136908b Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Wed, 13 Dec 2023 19:36:54 +0100 Subject: [PATCH 56/80] Aqua CI --- Project.toml | 10 +++++++++- test/qa.jl | 11 +++++++++++ test/runtests.jl | 4 ++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/qa.jl diff --git a/Project.toml b/Project.toml index 2e2261ddb..c4222f5af 100644 --- a/Project.toml +++ b/Project.toml @@ -12,14 +12,22 @@ ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" [compat] +Aqua = "0.8" ChainRulesCore = "1" +ControlSystemsBase = "1" +DataInterpolations = "4" DiffEqBase = "6" IfElse = "0.1" +LinearAlgebra = "1" ModelingToolkit = "8.67" +OrdinaryDiffEq = "6" +SafeTestsets = "0.1" Symbolics = "4.9, 5" +Test = "1" julia = "1.6" [extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e" DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -28,4 +36,4 @@ SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["LinearAlgebra", "OrdinaryDiffEq", "SafeTestsets", "Test", "ControlSystemsBase", "DataInterpolations"] +test = ["Aqua", "LinearAlgebra", "OrdinaryDiffEq", "SafeTestsets", "Test", "ControlSystemsBase", "DataInterpolations"] diff --git a/test/qa.jl b/test/qa.jl new file mode 100644 index 000000000..5d580adc1 --- /dev/null +++ b/test/qa.jl @@ -0,0 +1,11 @@ +using ModelingToolkitStandardLibrary, Aqua +@testset "Aqua" begin + Aqua.find_persistent_tasks_deps(ModelingToolkitStandardLibrary) + Aqua.test_ambiguities(ModelingToolkitStandardLibrary, recursive = false) + Aqua.test_deps_compat(ModelingToolkitStandardLibrary) + Aqua.test_piracies(ModelingToolkitStandardLibrary) + Aqua.test_project_extras(ModelingToolkitStandardLibrary) + Aqua.test_stale_deps(ModelingToolkitStandardLibrary) + Aqua.test_unbound_args(ModelingToolkitStandardLibrary) + Aqua.test_undefined_exports(ModelingToolkitStandardLibrary) +end diff --git a/test/runtests.jl b/test/runtests.jl index 178984894..282175ab7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,9 @@ using SafeTestsets +@safetestset "Quality Assurance" begin + include("qa.jl") +end + # Blocks @safetestset "Blocks: math" begin include("Blocks/math.jl") From a651e19f05db16153eeaf446e78df8246142ca42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 03:19:57 +0000 Subject: [PATCH 57/80] chore(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/SpellCheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index 599253c8c..74af4eff7 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -8,6 +8,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Actions Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check spelling uses: crate-ci/typos@v1.16.23 \ No newline at end of file From 4b885b967efa1fbfec8987c0bad7da36d1ca3ab2 Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Wed, 3 Jan 2024 04:26:16 +0100 Subject: [PATCH 58/80] Downgrade CI --- .github/workflows/CI.yml | 1 - .github/workflows/Downgrade.yml | 29 +++++++++++++++++++++++++++++ .github/workflows/Downstream.yml | 2 +- Project.toml | 12 ++++++------ 4 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/Downgrade.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 48a0f9232..ff4ecb4bc 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,7 +19,6 @@ jobs: - All version: - '1' - - '1.6' steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v1 diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml new file mode 100644 index 000000000..372644a06 --- /dev/null +++ b/.github/workflows/Downgrade.yml @@ -0,0 +1,29 @@ +name: Downgrade +on: + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + push: + branches: + - main + paths-ignore: + - 'docs/**' +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['1'] + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + - uses: cjdoris/julia-downgrade-compat-action@v1 +# if: ${{ matrix.version == '1.6' }} + with: + skip: Pkg,TOML + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 \ No newline at end of file diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index 932cfd3f5..48a176c5a 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - julia-version: [1,1.6] + julia-version: [1] os: [ubuntu-latest] package: - {user: SciML, repo: ModelingToolkit.jl} diff --git a/Project.toml b/Project.toml index c4222f5af..be766c4a7 100644 --- a/Project.toml +++ b/Project.toml @@ -16,15 +16,15 @@ Aqua = "0.8" ChainRulesCore = "1" ControlSystemsBase = "1" DataInterpolations = "4" -DiffEqBase = "6" +DiffEqBase = "6.103" IfElse = "0.1" -LinearAlgebra = "1" -ModelingToolkit = "8.67" -OrdinaryDiffEq = "6" +LinearAlgebra = "1.10" +ModelingToolkit = "8.69" +OrdinaryDiffEq = "6.33" SafeTestsets = "0.1" -Symbolics = "4.9, 5" +Symbolics = "5.2" Test = "1" -julia = "1.6" +julia = "1.10" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" From 2fc90bce24fcf22357c5f3c89c21940302f0d20e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 01:13:10 +0000 Subject: [PATCH 59/80] chore(deps): bump crate-ci/typos from 1.16.23 to 1.17.0 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.23 to 1.17.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.16.23...v1.17.0) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/SpellCheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index 74af4eff7..0decc88b4 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -10,4 +10,4 @@ jobs: - name: Checkout Actions Repository uses: actions/checkout@v4 - name: Check spelling - uses: crate-ci/typos@v1.16.23 \ No newline at end of file + uses: crate-ci/typos@v1.17.0 \ No newline at end of file From 9fc09c0d8f4fef2137aeb391c6bc162a7c93d30c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 01:39:15 +0000 Subject: [PATCH 60/80] chore(deps): bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ff4ecb4bc..9efbc7264 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -24,7 +24,7 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 env: cache-name: cache-artifacts with: From a55b6bf52789acd33a44c3d9f6c7bc838660f896 Mon Sep 17 00:00:00 2001 From: Geordie McBain Date: Wed, 24 Jan 2024 16:42:44 +1100 Subject: [PATCH 61/80] typo: misnamed C2.v --- docs/src/tutorials/custom_component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/custom_component.md b/docs/src/tutorials/custom_component.md index 53f51c73e..a0274179f 100644 --- a/docs/src/tutorials/custom_component.md +++ b/docs/src/tutorials/custom_component.md @@ -132,7 +132,7 @@ Plots.savefig("chua_phase_plane.png") nothing # hide Plots.plot(sol; idxs = [C1.v, C2.v, L.i], - labels = ["C1 Voltage in V" "C1 Voltage in V" "Inductor Current in A"]) + labels = ["C1 Voltage in V" "C2 Voltage in V" "Inductor Current in A"]) Plots.savefig("chua.png") nothing # hide ``` From 111dce7851c885afa8db1e9d66b78ab5b1c9a638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 01:36:10 +0000 Subject: [PATCH 62/80] chore(deps): bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/CI.yml | 2 +- .github/workflows/Documentation.yml | 2 +- .github/workflows/Downstream.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9efbc7264..9c7935186 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,6 +37,6 @@ jobs: - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: file: lcov.info diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 14bd20018..135b571b3 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -23,6 +23,6 @@ jobs: DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key run: julia --project=docs/ --code-coverage=user docs/make.jl - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: file: lcov.info diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index 48a176c5a..948ebc6dc 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -48,6 +48,6 @@ jobs: exit(0) # Exit immediately, as a success end - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: file: lcov.info From 937e139e60e91c52fcbb0d2db8e98adfa67acad3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 01:36:06 +0000 Subject: [PATCH 63/80] chore(deps): bump crate-ci/typos from 1.17.0 to 1.18.0 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.17.0...v1.18.0) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/SpellCheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index 0decc88b4..9246edd2a 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -10,4 +10,4 @@ jobs: - name: Checkout Actions Repository uses: actions/checkout@v4 - name: Check spelling - uses: crate-ci/typos@v1.17.0 \ No newline at end of file + uses: crate-ci/typos@v1.18.0 \ No newline at end of file From d094af53b4f142730322a56cd88f5f1d5e17a919 Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Tue, 6 Feb 2024 04:47:12 +0100 Subject: [PATCH 64/80] [skip ci] Update dependabot.yml --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1e8a051e2..ec3b005a0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,4 +7,4 @@ updates: interval: "weekly" ignore: - dependency-name: "crate-ci/typos" - update-types: ["version-update:semver-patch"] + update-types: ["version-update:semver-patch", "version-update:semver-minor"] From 439a6919644e90f525f848986c593caf5a73c2d4 Mon Sep 17 00:00:00 2001 From: Anant Thazhemadam <47104651+thazhemadam@users.noreply.github.com> Date: Fri, 9 Feb 2024 22:49:29 +0530 Subject: [PATCH 65/80] ci: explicitly specify token for codecov Explicitly specify the codecov token to be used (i.e., `CODECOV_TOKEN`), given that the latest v4 release of the codecov action requires it to be able to generate coverage reports. Additionally, fail CI if coverage reporting fails, since coverage is an important enough metric for us to ensure that it's tracked consistently. --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9c7935186..687e5f669 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -40,3 +40,5 @@ jobs: - uses: codecov/codecov-action@v4 with: file: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true From c16e05dd00a3c556daf370c16f94d9f2806d5e30 Mon Sep 17 00:00:00 2001 From: Anant Thazhemadam <47104651+thazhemadam@users.noreply.github.com> Date: Fri, 9 Feb 2024 22:56:48 +0530 Subject: [PATCH 66/80] ci: explicitly specify token for codecov Explicitly specify the codecov token to be used (i.e., `CODECOV_TOKEN`), given that the latest v4 release of the codecov action requires it to be able to generate coverage reports. Additionally, fail CI if coverage reporting fails, since coverage is an important enough metric for us to ensure that it's tracked consistently. --- .github/workflows/Downstream.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index 948ebc6dc..e0ddd30b5 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -51,3 +51,5 @@ jobs: - uses: codecov/codecov-action@v4 with: file: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true From 49dbdd828fe3b863956716a4cf335d9acd90d687 Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Sun, 18 Feb 2024 13:31:05 +0100 Subject: [PATCH 67/80] [skip ci] update downgrade CI repo --- .github/workflows/Downgrade.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 372644a06..93974846d 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -21,9 +21,9 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} - - uses: cjdoris/julia-downgrade-compat-action@v1 + - uses: julia-actions/julia-downgrade-compat@v1 # if: ${{ matrix.version == '1.6' }} with: skip: Pkg,TOML - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 \ No newline at end of file + - uses: julia-actions/julia-runtest@v1 From aa98f8607c6acefa197c05e70d213040767b6b2b Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 5 Feb 2024 17:20:38 -0500 Subject: [PATCH 68/80] Define `isconnection` on AnalysisPoint --- src/Blocks/analysis_points.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index b7690692d..ef95e6f42 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -5,6 +5,9 @@ Base.@kwdef mutable struct AnalysisPoint out = nothing name::Symbol = :nothing end +if Base.isdefined(ModelingToolkit, :isconnection) + ModelingToolkit.isconnection(::AnalysisPoint) = true +end Base.nameof(ap::AnalysisPoint) = ap.name From 875d870836ce5edb5647cc3de7ee5962a3af643d Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 5 Feb 2024 17:22:53 -0500 Subject: [PATCH 69/80] Format --- src/Blocks/analysis_points.jl | 28 ++--- src/Blocks/continuous.jl | 18 +-- src/Blocks/sources.jl | 114 ++++++++++-------- src/Blocks/utils.jl | 2 +- src/Electrical/Digital/logic_vectors.jl | 2 +- src/Electrical/Digital/tables.jl | 2 +- .../IsothermalCompressible/components.jl | 102 ++++++++-------- src/Hydraulic/IsothermalCompressible/utils.jl | 4 +- src/Mechanical/Translational/components.jl | 4 +- .../TranslationalPosition/components.jl | 6 +- 10 files changed, 147 insertions(+), 135 deletions(-) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index ef95e6f42..a20a1e728 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -258,7 +258,7 @@ end const SymOrVec = Union{Symbol, Vector{Symbol}} function get_sensitivity_function(sys, ap_name::SymOrVec; loop_openings = nothing, - kwargs...) + kwargs...) find = namespaced_ap_match(ap_name, loop_openings) t = get_iv(sys) aps = [] @@ -287,7 +287,7 @@ function get_sensitivity_function(sys, ap_name::SymOrVec; loop_openings = nothin end function get_comp_sensitivity_function(sys, ap_name::SymOrVec; loop_openings = nothing, - kwargs...) + kwargs...) find = namespaced_ap_match(ap_name, loop_openings) t = get_iv(sys) aps = [] @@ -316,7 +316,7 @@ function get_comp_sensitivity_function(sys, ap_name::SymOrVec; loop_openings = n end function get_looptransfer_function(sys, ap_name::SymOrVec; loop_openings = nothing, - kwargs...) + kwargs...) find = namespaced_ap_match(ap_name, loop_openings) t = get_iv(sys) aps = [] @@ -383,9 +383,9 @@ function open_loop(sys, ap_name::Symbol; ground_input = false, kwargs...) end function ModelingToolkit.linearization_function(sys::ModelingToolkit.AbstractSystem, - input_name::SymOrVec, output_name; - loop_openings = nothing, - kwargs...) + input_name::SymOrVec, output_name; + loop_openings = nothing, + kwargs...) t = get_iv(sys) @variables u(t)=0 [input = true] names = [input_name;] @@ -447,19 +447,19 @@ for f in [:get_sensitivity, :get_comp_sensitivity, :get_looptransfer, :open_loop end function ModelingToolkit.linearize(sys, input::AnalysisPoint, output::AnalysisPoint; - kwargs...) + kwargs...) ModelingToolkit.linearize(sys, nameof(input), nameof(output); kwargs...) end # Methods above are implemented in terms of linearization_function, the method below creates wrappers for linearize for f in [:get_sensitivity, :get_comp_sensitivity, :get_looptransfer] @eval function $f(sys, - ap, - args...; - loop_openings = nothing, - op = Dict(), - p = DiffEqBase.NullParameters(), - kwargs...) + ap, + args...; + loop_openings = nothing, + op = Dict(), + p = DiffEqBase.NullParameters(), + kwargs...) lin_fun, ssys = $(Symbol(string(f) * "_function"))(sys, ap, args...; op, p, loop_openings, kwargs...) @@ -475,7 +475,7 @@ Linearize a system between two analysis points. To get a loop-transfer function, The output is allowed to be either an analysis-point name, or a vector of symbolic variables like the standard interface to `linearize`. The input must be an analysis-point name. """ function ModelingToolkit.linearize(sys, input_name::SymOrVec, output_name; - loop_openings = nothing, kwargs...) + loop_openings = nothing, kwargs...) lin_fun, ssys = linearization_function(sys, input_name, output_name; loop_openings, kwargs...) ModelingToolkit.linearize(ssys, lin_fun; kwargs...), ssys diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 2661c7124..72bfcc7d2 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -245,7 +245,7 @@ Text-book version of a PID-controller without actuator saturation and anti-windu See also [`LimPID`](@ref) """ @component function PID(; name, k = 1, Ti = false, Td = false, Nd = 10, int__x = 0, - der__x = 0) + der__x = 0) with_I = !isequal(Ti, false) with_D = !isequal(Td, false) @named err_input = RealInput() # control error @@ -386,13 +386,13 @@ where the transfer function for the derivative includes additional filtering, se - `ctr_output` """ @component function LimPID(; name, k = 1, Ti = false, Td = false, wp = 1, wd = 1, - Ni = Ti == 0 ? Inf : √(max(Td / Ti, 1e-6)), - Nd = 10, - u_max = Inf, - u_min = u_max > 0 ? -u_max : -Inf, - gains = false, - int__x = 0.0, - der__x = 0.0) + Ni = Ti == 0 ? Inf : √(max(Td / Ti, 1e-6)), + Nd = 10, + u_max = Inf, + u_min = u_max > 0 ? -u_max : -Inf, + gains = false, + int__x = 0.0, + der__x = 0.0) with_I = !isequal(Ti, false) with_D = !isequal(Td, false) with_AWM = Ni != Inf @@ -522,7 +522,7 @@ y &= h(x, u) linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u₀), u₀`. """ @component function StateSpace(; A, B, C, D = nothing, x = zeros(size(A, 1)), name, - u0 = zeros(size(B, 2)), y0 = zeros(size(C, 1))) + u0 = zeros(size(B, 2)), y0 = zeros(size(C, 1))) nx, nu, ny = size(A, 1), size(B, 2), size(C, 1) size(A, 2) == nx || error("`A` has to be a square matrix.") size(B, 1) == nx || error("`B` has to be of dimension ($nx x $nu).") diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index f021c4606..d7c9fc7db 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -130,13 +130,13 @@ Generate sine signal. - `output` """ @component function Sine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase equation = if smooth == false @@ -174,13 +174,13 @@ Cosine signal. """ @component function Cosine(; name, - frequency, - amplitude = 1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) + frequency, + amplitude = 1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase equation = if smooth == false @@ -240,12 +240,12 @@ Generate ramp signal. - `output` """ @component function Ramp(; name, - height = 1, - duration = 1, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) + height = 1, + duration = 1, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time height=height duration=duration equation = if smooth == false @@ -283,8 +283,8 @@ Generate smooth square signal. - `output` """ @component function Square(; name, frequency = 1.0, amplitude = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters begin frequency = frequency @@ -326,7 +326,7 @@ Generate step signal. - `output` """ @component function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, - smooth = 1e-5, output__unit = nothing) + smooth = 1e-5, output__unit = nothing) @named output = RealOutput(; unit = output__unit) duration_numeric = duration pars = @parameters offset=offset start_time=start_time height=height duration=duration @@ -370,14 +370,14 @@ Exponentially damped sine signal. - `output` """ @component function ExpSine(; name, - frequency, - amplitude = 1, - damping = 0.1, - phase = 0, - offset = 0, - start_time = 0, - smooth = false, - output__unit = nothing) + frequency, + amplitude = 1, + damping = 0.1, + phase = 0, + offset = 0, + start_time = 0, + smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping @@ -418,8 +418,8 @@ Generate smooth triangular signal for frequencies less than or equal to 25 Hz - `output` """ @component function Triangular(; name, amplitude = 1.0, frequency = 1.0, - offset = 0.0, start_time = 0.0, smooth = false, - output__unit = nothing) + offset = 0.0, start_time = 0.0, smooth = false, + output__unit = nothing) @named output = RealOutput(; unit = output__unit) pars = @parameters begin amplitude = amplitude @@ -569,9 +569,9 @@ function first_order_backwards_difference(t, buffer, Δt, circular_buffer) end function get_sampled_data(t, - buffer::Vector{T}, - dt::T, - circular_buffer = true) where {T <: Real} + buffer::Vector{T}, + dt::T, + circular_buffer = true) where {T <: Real} if t < 0 t = zero(t) end @@ -630,7 +630,12 @@ function Symbolics.derivative(::typeof(get_sampled_data), args::NTuple{4, Any}, circular_buffer = @inbounds args[4] first_order_backwards_difference(t, buffer, sample_time, circular_buffer) end -function ChainRulesCore.frule((_, ẋ, _), ::typeof(get_sampled_data), t, buffer, sample_time, circular_buffer) +function ChainRulesCore.frule((_, ẋ, _), + ::typeof(get_sampled_data), + t, + buffer, + sample_time, + circular_buffer) first_order_backwards_difference(t, buffer, sample_time, circular_buffer) * ẋ end @@ -655,7 +660,11 @@ data input component. # Connectors: - `output` """ -@component function SampledData(::Val{SampledDataType.vector_based}; name, buffer, sample_time, circular_buffer=true) +@component function SampledData(::Val{SampledDataType.vector_based}; + name, + buffer, + sample_time, + circular_buffer = true) pars = @parameters begin buffer = buffer #::Vector{Real} sample_time = sample_time #::Real @@ -704,16 +713,19 @@ SampledData(x::SampledDataType.Option; kwargs...) = SampledData(Val(x); kwargs.. SampledData(T::Type, circular_buffer = true; name) = SampledData(SampledDataType.struct_based; name, buffer = Parameter(T[], zero(T), circular_buffer)) # vector_based -SampledData(sample_time::T, circular_buffer = true; name) where {T <: Real} = SampledData(SampledDataType.vector_based; name, buffer=T[], sample_time, circular_buffer) -SampledData(buffer::Vector{<:Real}, sample_time::Real, circular_buffer = true; name) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) -SampledData(; name, buffer, sample_time, circular_buffer) = SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) - - - - - - - - - - +function SampledData(sample_time::T, circular_buffer = true; name) where {T <: Real} + SampledData(SampledDataType.vector_based; + name, + buffer = T[], + sample_time, + circular_buffer) +end +function SampledData(buffer::Vector{<:Real}, + sample_time::Real, + circular_buffer = true; + name) + SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) +end +function SampledData(; name, buffer, sample_time, circular_buffer) + SampledData(SampledDataType.vector_based; name, buffer, sample_time, circular_buffer) +end diff --git a/src/Blocks/utils.jl b/src/Blocks/utils.jl index 1246fbffd..6dfc36155 100644 --- a/src/Blocks/utils.jl +++ b/src/Blocks/utils.jl @@ -128,7 +128,7 @@ Base class for a multiple input multiple output (MIMO) continuous system block. - `y_start`: Initial value for the output """ @component function MIMO(; name, nin = 1, nout = 1, u_start = zeros(nin), - y_start = zeros(nout)) + y_start = zeros(nout)) @named input = RealInput(nin = nin, u_start = u_start) @named output = RealOutput(nout = nout, u_start = y_start) @variables(u(t)[1:nin]=u_start, [description = "Input of MIMO system $name"], diff --git a/src/Electrical/Digital/logic_vectors.jl b/src/Electrical/Digital/logic_vectors.jl index 8f048249a..590621cd1 100644 --- a/src/Electrical/Digital/logic_vectors.jl +++ b/src/Electrical/Digital/logic_vectors.jl @@ -24,7 +24,7 @@ axes(l::LogicVector) = axes(l.logic) getindex(s::LogicVector, i::Int) = getindex(s.logic, i) function Base.getindex(s::LogicVector, i1::Int, i2::Int, - I::Int...) + I::Int...) getindex(s.logic, i1, i2, I...) end diff --git a/src/Electrical/Digital/tables.jl b/src/Electrical/Digital/tables.jl index b2d7b8983..599ada98e 100644 --- a/src/Electrical/Digital/tables.jl +++ b/src/Electrical/Digital/tables.jl @@ -17,7 +17,7 @@ function Base.getindex(s::LogicTable, i1::Logic, i2::Logic) getindex(s.logic, get_logic_level(i1), get_logic_level(i2)) end function Base.getindex(s::LogicTable, i1::Logic, i2::Logic, - I::Logic...) + I::Logic...) getindex(s.logic, get_logic_level(i1), get_logic_level(i2), get_logic_level(I...)...) end diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index 91f6bd54c..b7422f35c 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -65,9 +65,9 @@ Variable length internal flow model of the fully developed incompressible flow f - `port_b`: hydraulic port """ @component function TubeBase(add_inertia = true, variable_length = true; p_int, area, - length_int, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) + length_int, head_factor = 1, + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) pars = @parameters begin p_int = p_int area = area @@ -150,8 +150,8 @@ Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `Tub - `port_b`: hydraulic port """ @component function Tube(N, add_inertia = true; p_int, area, length, head_factor = 1, - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, name) + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, name) @assert(N>0, "the Tube component must be defined with at least 1 segment (i.e. N>0), found N=$N") @@ -263,7 +263,7 @@ Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel end @component function ValveBase(reversible = false; p_a_int, p_b_int, minimum_area = 0, - area_int, Cd, Cd_reverse = Cd, name) + area_int, Cd, Cd_reverse = Cd, name) pars = @parameters begin p_a_int = p_a_int p_b_int = p_b_int @@ -329,9 +329,9 @@ Valve with `area` input and discharge coefficient `Cd` defined by https://en.wik - `area`: real input setting the valve `area`. When `reversible = true`, negative input reverses flow direction, otherwise a floor of `minimum_area` is enforced. """ @component function Valve(reversible = false; p_a_int, p_b_int, - area_int, Cd, Cd_reverse = Cd, - minimum_area = 0, - name) + area_int, Cd, Cd_reverse = Cd, + minimum_area = 0, + name) pars = @parameters begin p_a_int = p_a_int p_b_int = p_b_int @@ -359,7 +359,7 @@ Valve with `area` input and discharge coefficient `Cd` defined by https://en.wik end @component function VolumeBase(; p_int, x_int = 0, area, dead_volume = 0, Χ1 = 1, Χ2 = 1, - name) + name) pars = @parameters begin p_int = p_int x_int = x_int @@ -477,24 +477,24 @@ dm ────► │ │ area - `flange`: mechanical translational port """ @component function DynamicVolume(N, add_inertia = true, reversible = false; - p_int, - area, - x_int = 0, - x_max, - x_min = 0, - x_damp = x_min, - direction = +1, - - # Tube - perimeter = 2 * sqrt(area * pi), - shape_factor = 64, - head_factor = 1, - - # Valve - Cd = 1e2, - Cd_reverse = Cd, - minimum_area = 0, - name) + p_int, + area, + x_int = 0, + x_max, + x_min = 0, + x_damp = x_min, + direction = +1, + + # Tube + perimeter = 2 * sqrt(area * pi), + shape_factor = 64, + head_factor = 1, + + # Valve + Cd = 1e2, + Cd_reverse = Cd, + minimum_area = 0, + name) @assert(N>=0, "the Tube component must be defined with 0 or more segments (i.e. N>=0), found N=$N") @assert (direction == +1)||(direction == -1) "direction argument must be +/-1, found $direction" @@ -647,7 +647,7 @@ end end @component function SpoolValve2Way(reversible = false; p_s_int, p_a_int, p_b_int, p_r_int, - m, g, x_int, Cd, d, name) + m, g, x_int, Cd, d, name) pars = @parameters begin p_s_int = p_s_int p_a_int = p_a_int @@ -690,28 +690,28 @@ end end @component function Actuator(N, add_inertia = true, reversible = false; - p_a_int, - p_b_int, - area_a, - area_b, - perimeter_a = 2 * sqrt(area_a * pi), - perimeter_b = 2 * sqrt(area_b * pi), - length_a_int, - length_b_int, - shape_factor_a = 64, - shape_factor_b = 64, - head_factor_a = 1, - head_factor_b = 1, - m, - g, - x_int = 0, - minimum_volume_a = 0, - minimum_volume_b = 0, - damping_volume_a = minimum_volume_a, - damping_volume_b = minimum_volume_b, - Cd = 1e4, - Cd_reverse = Cd, - name) + p_a_int, + p_b_int, + area_a, + area_b, + perimeter_a = 2 * sqrt(area_a * pi), + perimeter_b = 2 * sqrt(area_b * pi), + length_a_int, + length_b_int, + shape_factor_a = 64, + shape_factor_b = 64, + head_factor_a = 1, + head_factor_b = 1, + m, + g, + x_int = 0, + minimum_volume_a = 0, + minimum_volume_b = 0, + damping_volume_a = minimum_volume_a, + damping_volume_b = minimum_volume_b, + Cd = 1e4, + Cd_reverse = Cd, + name) pars = @parameters begin p_a_int = p_a_int p_b_int = p_b_int diff --git a/src/Hydraulic/IsothermalCompressible/utils.jl b/src/Hydraulic/IsothermalCompressible/utils.jl index cc937bbf2..d88f24f42 100644 --- a/src/Hydraulic/IsothermalCompressible/utils.jl +++ b/src/Hydraulic/IsothermalCompressible/utils.jl @@ -51,8 +51,8 @@ Fluid parameter setter for isothermal compressible fluid domain. Defaults given - `p_gas`: [Pa] reference pressure (set by `gas_pressure` argument) """ @connector function HydraulicFluid(; density = 997, bulk_modulus = 2.09e9, - viscosity = 0.0010016, gas_density = 0.0073955, - gas_pressure = -1000, n = 1, let_gas = 1, name) + viscosity = 0.0010016, gas_density = 0.0073955, + gas_pressure = -1000, n = 1, let_gas = 1, name) pars = @parameters begin ρ = density β = bulk_modulus diff --git a/src/Mechanical/Translational/components.jl b/src/Mechanical/Translational/components.jl index c93c28b81..c8d909944 100644 --- a/src/Mechanical/Translational/components.jl +++ b/src/Mechanical/Translational/components.jl @@ -116,7 +116,7 @@ Linear 1D translational spring end # default @component function Spring(::Val{:relative}; name, k, delta_s = 0.0, flange_a__v = 0.0, - flange_b__v = 0.0) + flange_b__v = 0.0) pars = @parameters begin k = k, [description = "Spring constant", unit = u"N/m"] end @@ -137,7 +137,7 @@ end const ABS = Val(:absolute) @component function Spring(::Val{:absolute}; name, k, sa = 0, sb = 0, flange_a__v = 0.0, - flange_b__v = 0.0, l = 0) + flange_b__v = 0.0, l = 0) pars = @parameters begin k = k, [description = "Spring constant", unit = u"N/m"] l = l diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index 8327187d2..e6faeb340 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -64,7 +64,7 @@ end const REL = Val(:relative) @component function Spring(::Val{:relative}; name, k, va = 0.0, vb = 0.0, - delta_s = 0, flange_a__s = 0, flange_b__s = 0) + delta_s = 0, flange_a__s = 0, flange_b__s = 0) pars = @parameters begin k = k end @@ -118,8 +118,8 @@ function Spring(; name, k, flange_a__s = 0, flange_b__s = 0, l = 0) end #default function @component function Spring(::Val{:absolute}; - name, k, flange_a__s = 0, - flange_b__s = 0, l = 0) + name, k, flange_a__s = 0, + flange_b__s = 0, l = 0) pars = @parameters begin k = k, [description = "Spring constant", unit = u"N/m"] l = l, [description = "Unstretched spring length", unit = u"m"] From f07aa73e859a9ea6ff2d29f9edcad388c9465228 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 14:28:24 -0500 Subject: [PATCH 70/80] Format and fix AnalysisPoint --- README.md | 6 +- docs/pages.jl | 6 +- docs/src/API/linear_analysis.md | 2 +- docs/src/connectors/connections.md | 8 +- docs/src/tutorials/custom_component.md | 18 ++-- docs/src/tutorials/dc_motor_pi.md | 26 ++--- docs/src/tutorials/input_component.md | 18 ++-- docs/src/tutorials/rc_circuit.md | 6 +- docs/src/tutorials/thermal_model.md | 2 +- src/Blocks/Blocks.jl | 4 +- src/Blocks/analysis_points.jl | 4 +- src/Blocks/continuous.jl | 20 ++-- src/Blocks/nonlinear.jl | 4 +- src/Blocks/sources.jl | 24 +++-- src/Blocks/utils.jl | 2 +- src/Electrical/Digital/components.jl | 12 +-- src/Electrical/Digital/gates.jl | 14 +-- src/Electrical/Digital/sources.jl | 8 +- src/Electrical/Digital/tables.jl | 60 +++++------ src/Electrical/Electrical.jl | 10 +- src/Electrical/utils.jl | 2 +- .../IsothermalCompressible/components.jl | 77 ++++++------- .../IsothermalCompressible/sources.jl | 6 +- src/Hydraulic/IsothermalCompressible/utils.jl | 2 +- src/Magnetic/FluxTubes/FluxTubes.jl | 2 +- src/Mechanical/Rotational/sources.jl | 10 +- src/Mechanical/Rotational/utils.jl | 4 +- src/Mechanical/Translational/components.jl | 16 +-- src/Mechanical/Translational/sources.jl | 2 +- src/Mechanical/TranslationalModelica/utils.jl | 3 +- .../TranslationalPosition/components.jl | 31 +++--- src/Mechanical/TranslationalPosition/utils.jl | 4 +- src/Thermal/HeatTransfer/ideal_components.jl | 2 +- src/Thermal/Thermal.jl | 4 +- test/Blocks/continuous.jl | 76 +++++++------ test/Blocks/math.jl | 71 +++++++----- test/Blocks/nonlinear.jl | 22 ++-- test/Blocks/sources.jl | 73 +++++++------ test/Blocks/test_analysis_points.jl | 59 +++++----- test/Electrical/analog.jl | 102 +++++++++--------- test/Electrical/digital.jl | 4 +- test/Hydraulic/isothermal_compressible.jl | 50 ++++----- test/Magnetic/magnetic.jl | 22 ++-- test/Mechanical/multibody.jl | 6 +- test/Mechanical/rotational.jl | 76 ++++++------- test/Mechanical/translational.jl | 20 ++-- test/Mechanical/translational_modelica.jl | 8 +- test/Thermal/demo.jl | 2 +- test/Thermal/thermal.jl | 20 ++-- test/chua_circuit.jl | 16 +-- test/multi_domain.jl | 48 ++++----- 51 files changed, 574 insertions(+), 520 deletions(-) diff --git a/README.md b/README.md index 973f252a4..94dd89676 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,9 @@ V = 1.0 @named ground = Ground() rc_eqs = [connect(constant.output, source.V) - connect(source.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, source.n, ground.g)] + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g)] @named rc_model = ODESystem(rc_eqs, t, systems = [resistor, capacitor, constant, source, ground]) diff --git a/docs/pages.jl b/docs/pages.jl index af2de24f3..35cf400cb 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -5,7 +5,7 @@ pages = [ "Custom Components" => "tutorials/custom_component.md", "Thermal Conduction Model" => "tutorials/thermal_model.md", "DC Motor with Speed Controller" => "tutorials/dc_motor_pi.md", - "SampledData Component" => "tutorials/input_component.md", + "SampledData Component" => "tutorials/input_component.md" ], "About Acausal Connections" => "connectors/connections.md", "API" => [ @@ -15,6 +15,6 @@ pages = [ "Mechanical Components" => "API/mechanical.md", "Thermal Components" => "API/thermal.md", "Hydraulic Components" => "API/hydraulic.md", - "Linear Analysis" => "API/linear_analysis.md", - ], + "Linear Analysis" => "API/linear_analysis.md" + ] ] diff --git a/docs/src/API/linear_analysis.md b/docs/src/API/linear_analysis.md index 0028defb8..98d265371 100644 --- a/docs/src/API/linear_analysis.md +++ b/docs/src/API/linear_analysis.md @@ -60,7 +60,7 @@ using ModelingToolkitStandardLibrary.Blocks, ModelingToolkit @named C = Gain(-1) # A P controller t = ModelingToolkit.get_iv(P) eqs = [connect(P.output, :plant_output, C.input) # Connect with an automatically created analysis point called :plant_output - connect(C.output, :plant_input, P.input)] + connect(C.output, :plant_input, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :feedback_system) matrices_S = get_sensitivity(sys, :plant_input)[1] # Compute the matrices of a state-space representation of the (input)sensitivity function. diff --git a/docs/src/connectors/connections.md b/docs/src/connectors/connections.md index 28ec45c92..83a43f2ca 100644 --- a/docs/src/connectors/connections.md +++ b/docs/src/connectors/connections.md @@ -99,7 +99,7 @@ using Plots @named ground = Ground() eqs = [connect(capacitor.p, resistor.p) - connect(resistor.n, ground.g, capacitor.n)] + connect(resistor.n, ground.g, capacitor.n)] @named model = ODESystem(eqs, t; systems = [resistor, capacitor, ground]) @@ -139,7 +139,7 @@ const TV = ModelingToolkitStandardLibrary.Mechanical.Translational @named ground = TV.Fixed() eqs = [connect(damping.flange_a, body.flange) - connect(ground.flange, damping.flange_b)] + connect(ground.flange, damping.flange_b)] @named model = ODESystem(eqs, t; systems = [damping, body, ground]) @@ -172,7 +172,7 @@ const TP = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition @named ground = TP.Fixed(s_0 = 0) eqs = [connect(damping.flange_a, body.flange) - connect(ground.flange, damping.flange_b)] + connect(ground.flange, damping.flange_b)] @named model = ODESystem(eqs, t; systems = [damping, body, ground]) @@ -267,7 +267,7 @@ Let's define a quick function to simplify and solve the 2 different systems. Not ```@example connections function simplify_and_solve(damping, spring, body, ground) eqs = [connect(spring.flange_a, body.flange, damping.flange_a) - connect(spring.flange_b, damping.flange_b, ground.flange)] + connect(spring.flange_b, damping.flange_b, ground.flange)] @named model = ODESystem(eqs, t; systems = [ground, body, spring, damping]) diff --git a/docs/src/tutorials/custom_component.md b/docs/src/tutorials/custom_component.md index a0274179f..f53f3f3fc 100644 --- a/docs/src/tutorials/custom_component.md +++ b/docs/src/tutorials/custom_component.md @@ -46,7 +46,7 @@ function NonlinearResistor(; name, Ga, Gb, Ve) Gb * (v + Ve) - Ga * Ve, ifelse(v > Ve, Gb * (v - Ve) + Ga * Ve, - Ga * v)), + Ga * v)) ] extend(ODESystem(eqs, t, [], pars; name = name), oneport) end @@ -102,14 +102,14 @@ The final model can now be created with the components from the library and the @named Gnd = Ground() connections = [connect(L.p, G.p) - connect(G.n, Nr.p) - connect(Nr.n, Gnd.g) - connect(C1.p, G.n) - connect(L.n, Ro.p) - connect(G.p, C2.p) - connect(C1.n, Gnd.g) - connect(C2.n, Gnd.g) - connect(Ro.n, Gnd.g)] + connect(G.n, Nr.p) + connect(Nr.n, Gnd.g) + connect(C1.p, G.n) + connect(L.n, Ro.p) + connect(G.p, C2.p) + connect(C1.n, Gnd.g) + connect(C2.n, Gnd.g) + connect(Ro.n, Gnd.g)] @named model = ODESystem(connections, t, systems = [L, Ro, G, C1, C2, Nr, Gnd]) nothing # hide diff --git a/docs/src/tutorials/dc_motor_pi.md b/docs/src/tutorials/dc_motor_pi.md index ae711ed0d..874dabb06 100644 --- a/docs/src/tutorials/dc_motor_pi.md +++ b/docs/src/tutorials/dc_motor_pi.md @@ -50,18 +50,18 @@ The actual model can now be composed. @named speed_sensor = SpeedSensor() connections = [connect(fixed.flange, emf.support, friction.flange_b) - connect(emf.flange, friction.flange_a, inertia.flange_a) - connect(inertia.flange_b, load.flange) - connect(inertia.flange_b, speed_sensor.flange) - connect(load_step.output, load.tau) - connect(ref.output, feedback.input1) - connect(speed_sensor.w, :y, feedback.input2) - connect(feedback.output, pi_controller.err_input) - connect(pi_controller.ctr_output, :u, source.V) - connect(source.p, R1.p) - connect(R1.n, L1.p) - connect(L1.n, emf.p) - connect(emf.n, source.n, ground.g)] + connect(emf.flange, friction.flange_a, inertia.flange_a) + connect(inertia.flange_b, load.flange) + connect(inertia.flange_b, speed_sensor.flange) + connect(load_step.output, load.tau) + connect(ref.output, feedback.input1) + connect(speed_sensor.w, :y, feedback.input2) + connect(feedback.output, pi_controller.err_input) + connect(pi_controller.ctr_output, :u, source.V) + connect(source.p, R1.p) + connect(R1.n, L1.p) + connect(L1.n, emf.p) + connect(emf.n, source.n, ground.g)] @named model = ODESystem(connections, t, systems = [ @@ -78,7 +78,7 @@ connections = [connect(fixed.flange, emf.support, friction.flange_b) load_step, inertia, friction, - speed_sensor, + speed_sensor ]) nothing # hide ``` diff --git a/docs/src/tutorials/input_component.md b/docs/src/tutorials/input_component.md index 7347c1213..293294334 100644 --- a/docs/src/tutorials/input_component.md +++ b/docs/src/tutorials/input_component.md @@ -28,9 +28,9 @@ function System(f; name) pars = @parameters m=10 k=1000 d=1 eqs = [f ~ src.output.u - ddx * 10 ~ k * x + d * dx + f - D(x) ~ dx - D(dx) ~ ddx] + ddx * 10 ~ k * x + d * dx + f + D(x) ~ dx + D(dx) ~ ddx] ODESystem(eqs, t, vars, pars; systems = [src], name) end @@ -74,9 +74,9 @@ function System(; name) pars = @parameters m=10 k=1000 d=1 eqs = [f ~ get_sampled_data(t) - ddx * 10 ~ k * x + d * dx + f - D(x) ~ dx - D(dx) ~ ddx] + ddx * 10 ~ k * x + d * dx + f + D(x) ~ dx + D(dx) ~ ddx] ODESystem(eqs, t, vars, pars; name) end @@ -115,9 +115,9 @@ function System(; name) pars = @parameters m=10 k=1000 d=1 eqs = [f ~ src.output.u - ddx * 10 ~ k * x + d * dx + f - D(x) ~ dx - D(dx) ~ ddx] + ddx * 10 ~ k * x + d * dx + f + D(x) ~ dx + D(dx) ~ ddx] ODESystem(eqs, t, vars, pars; systems = [src], name) end diff --git a/docs/src/tutorials/rc_circuit.md b/docs/src/tutorials/rc_circuit.md index a33a89ca1..4004d5abd 100644 --- a/docs/src/tutorials/rc_circuit.md +++ b/docs/src/tutorials/rc_circuit.md @@ -22,9 +22,9 @@ V = 1.0 @named ground = Ground() rc_eqs = [connect(constant.output, source.V) - connect(source.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, source.n, ground.g)] + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g)] @named rc_model = ODESystem(rc_eqs, t, systems = [resistor, capacitor, constant, source, ground]) diff --git a/docs/src/tutorials/thermal_model.md b/docs/src/tutorials/thermal_model.md index b83a064bb..233e2464d 100644 --- a/docs/src/tutorials/thermal_model.md +++ b/docs/src/tutorials/thermal_model.md @@ -23,7 +23,7 @@ connections = [ connect(mass1.port, conduction.port_a), connect(conduction.port_b, mass2.port), connect(mass1.port, Tsensor1.port), - connect(mass2.port, Tsensor2.port), + connect(mass2.port, Tsensor2.port) ] @named model = ODESystem(connections, t, diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index 85040d582..23a7e431d 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -20,7 +20,7 @@ export Log, Log10 include("math.jl") export Constant, TimeVaryingFunction, Sine, Cosine, ContinuousClock, Ramp, Step, ExpSine, - Square, Triangular, Parameter, SampledData + Square, Triangular, Parameter, SampledData include("sources.jl") export Limiter, DeadZone, SlewRateLimiter @@ -31,7 +31,7 @@ export PI, LimPI, PID, LimPID include("continuous.jl") export AnalysisPoint, get_sensitivity, get_comp_sensitivity, - get_looptransfer, open_loop + get_looptransfer, open_loop include("analysis_points.jl") end diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index a20a1e728..594031db2 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -1,6 +1,6 @@ using ModelingToolkit: get_eqs, vars, @set!, get_iv -Base.@kwdef mutable struct AnalysisPoint +Base.@kwdef mutable struct AnalysisPoint <: Real in = nothing out = nothing name::Symbol = :nothing @@ -420,7 +420,7 @@ function ModelingToolkit.linearization_function(sys::ModelingToolkit.AbstractSys push!(multiplicities_y, length(yi)) append!(y, yi) [ap_var(ap.in) .~ yi; - ap_var(ap.out) .~ ap_var(ap.in)], yi + ap_var(ap.out) .~ ap_var(ap.in)], yi else # loop opening [ap_var(ap.out) .~ 0;], [] end diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 72bfcc7d2..7b6fa47ed 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -282,7 +282,7 @@ See also [`LimPID`](@ref) eqs = [ connect(err_input, addPID.input1), connect(addPID.output, gainPID.input), - connect(gainPID.output, ctr_output), + connect(gainPID.output, ctr_output) ] if with_I push!(eqs, connect(err_input, int.input)) @@ -347,7 +347,7 @@ The simplified expression above is given without the anti-windup protection. connect(err_input, addTrack.input1), connect(gainTrack.output, addTrack.input2), connect(addTrack.output, int.input), - connect(int.output, addPI.input2), + connect(int.output, addPI.input2) ] ODESystem(eqs, t, [], []; name = name, systems = sys) end @@ -459,7 +459,7 @@ where the transfer function for the derivative includes additional filtering, se connect(addP.output, addPID.input1), connect(addPID.output, gainPID.input), connect(gainPID.output, limiter.input), - connect(limiter.output, ctr_output), + connect(limiter.output, ctr_output) ] if with_I push!(eqs, connect(reference, addI.input1)) @@ -538,7 +538,7 @@ linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u @named input = RealInput(nin = nu) @named output = RealOutput(nout = ny) @variables x(t)[1:nx]=x [ - description = "State variables of StateSpace system $name", + description = "State variables of StateSpace system $name" ] # pars = @parameters A=A B=B C=C D=D # This is buggy eqs = [ # FIXME: if array equations work @@ -547,7 +547,7 @@ linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u for i in 1:nx]..., # cannot use D here [output.u[j] ~ sum(C[j, i] * x[i] for i in 1:nx) + sum(D[j, k] * (input.u[k] - u0[k]) for k in 1:nu) + y0[j] - for j in 1:ny]..., + for j in 1:ny]... ] compose(ODESystem(eqs, t, vcat(x...), [], name = name), [input, output]) end @@ -596,11 +596,11 @@ See also [`StateSpace`](@ref) which handles MIMO systems, as well as [ControlSys @parameters begin b[1:nb] = b, [ - description = "Numerator coefficients of transfer function (e.g., 2s + 3 is specified as [2,3])", + description = "Numerator coefficients of transfer function (e.g., 2s + 3 is specified as [2,3])" ] a[1:na] = a, [ - description = "Denominator coefficients of transfer function (e.g., `s² + 2ωs + ω^2` is specified as [1, 2ω, ω^2])", + description = "Denominator coefficients of transfer function (e.g., `s² + 2ωs + ω^2` is specified as [1, 2ω, ω^2])" ] bb[1:(nbb + nb)] = [zeros(nbb); b] d = bb[1] / a[1], [description = "Direct feedthrough gain"] @@ -627,9 +627,9 @@ See also [`StateSpace`](@ref) which handles MIMO systems, as well as [ControlSys eqs = [y ~ d * u] else eqs = [D(x_scaled[1]) ~ (-a[2:na]'x_scaled + a_end * u) / a[1] - D.(x_scaled[2:nx]) .~ x_scaled[1:(nx - 1)] - y ~ ((bb[2:na] - d * a[2:na])'x_scaled) / a_end + d * u - x .~ x_scaled ./ a_end] + D.(x_scaled[2:nx]) .~ x_scaled[1:(nx - 1)] + y ~ ((bb[2:na] - d * a[2:na])'x_scaled) / a_end + d * u + x .~ x_scaled ./ a_end] end push!(eqs, input.u ~ u) push!(eqs, output.u ~ y) diff --git a/src/Blocks/nonlinear.jl b/src/Blocks/nonlinear.jl index 847c11190..8f2466f72 100644 --- a/src/Blocks/nonlinear.jl +++ b/src/Blocks/nonlinear.jl @@ -22,10 +22,10 @@ Limit the range of a signal. @named siso = SISO(u_start = m, y_start = m) # Default signals to center of saturation to minimize risk of saturation while linearizing etc. @unpack u, y = siso pars = @parameters y_max=y_max [description = "Maximum allowed output of Limiter $name"] y_min=y_min [ - description = "Minimum allowed output of Limiter $name", + description = "Minimum allowed output of Limiter $name" ] eqs = [ - y ~ _clamp(u, y_min, y_max), + y ~ _clamp(u, y_min, y_max) ] extend(ODESystem(eqs, t, [], pars; name = name), siso) end diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index d7c9fc7db..a1b5fda07 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -148,7 +148,7 @@ Generate sine signal. end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -191,7 +191,7 @@ Cosine signal. smooth_cos(t, smooth, frequency, amplitude, phase, offset, start_time) end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -215,7 +215,7 @@ Generate current time signal. @named output = RealOutput(; unit = output__unit) pars = @parameters offset=offset start_time=start_time eqs = [ - output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time), + output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time) ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -258,7 +258,7 @@ Generate ramp signal. end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -301,7 +301,7 @@ Generate smooth square signal. end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -343,7 +343,7 @@ Generate step signal. end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -392,7 +392,7 @@ Exponentially damped sine signal. end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -436,7 +436,7 @@ Generate smooth triangular signal for frequencies less than or equal to 25 Hz end eqs = [ - output.u ~ equation, + output.u ~ equation ] compose(ODESystem(eqs, t, [], pars; name = name), [output]) @@ -675,10 +675,12 @@ data input component. output = RealOutput() end eqs = [ - output.u ~ get_sampled_data(t, buffer, sample_time, circular_buffer), + output.u ~ get_sampled_data(t, buffer, sample_time, circular_buffer) ] return ODESystem(eqs, t, vars, pars; name, systems, - defaults = [output.u => get_sampled_data(0.0, buffer, sample_time, circular_buffer)]) + defaults = [ + output.u => get_sampled_data(0.0, buffer, sample_time, circular_buffer) + ]) end """ @@ -701,7 +703,7 @@ data input component. output = RealOutput(; unit) end eqs = [ - output.u ~ get_sampled_data(t, buffer), + output.u ~ get_sampled_data(t, buffer) ] return ODESystem(eqs, t, vars, pars; name, systems, defaults = [output.u => get_sampled_data(0.0, buffer)]) diff --git a/src/Blocks/utils.jl b/src/Blocks/utils.jl index 6dfc36155..dc044a4af 100644 --- a/src/Blocks/utils.jl +++ b/src/Blocks/utils.jl @@ -135,7 +135,7 @@ Base class for a multiple input multiple output (MIMO) continuous system block. y(t)[1:nout]=y_start, [description = "Output of MIMO system $name"],) eqs = [ [u[i] ~ input.u[i] for i in 1:nin]..., - [y[i] ~ output.u[i] for i in 1:nout]..., + [y[i] ~ output.u[i] for i in 1:nout]... ] return ODESystem(eqs, t, vcat(u..., y...), []; name = name, systems = [input, output]) end diff --git a/src/Electrical/Digital/components.jl b/src/Electrical/Digital/components.jl index ce9f31211..c3252f57d 100644 --- a/src/Electrical/Digital/components.jl +++ b/src/Electrical/Digital/components.jl @@ -30,9 +30,9 @@ Takes two bits as input, and outputs the sum and the carry @variables sum(t), carry(t) eqs = [y1.val ~ _xor(x1.val, x2.val) - y2.val ~ _and(x1.val, x2.val) - sum ~ y1.val - carry ~ y2.val] + y2.val ~ _and(x1.val, x2.val) + sum ~ y1.val + carry ~ y2.val] ODESystem(eqs, t, [sum, carry], [], systems = [x1, x2, y1, y2], name = name) end @@ -68,9 +68,9 @@ Takes three bits as input, and outputs the sum and the carry @variables sum(t), carry(t) eqs = [y1.val ~ _xor(x1.val, x2.val, x3.val) - y2.val ~ _or(_and(x3.val, _xor(x1.val, x2.val)), _and(x1.val, x2.val)) - sum ~ y1.val - carry ~ y2.val] + y2.val ~ _or(_and(x3.val, _xor(x1.val, x2.val)), _and(x1.val, x2.val)) + sum ~ y1.val + carry ~ y2.val] ODESystem(eqs, t, [sum, carry], [], systems = [x1, x2, x3, y1, y2], name = name) end diff --git a/src/Electrical/Digital/gates.jl b/src/Electrical/Digital/gates.jl index e7cdd73f5..0940e4cfc 100644 --- a/src/Electrical/Digital/gates.jl +++ b/src/Electrical/Digital/gates.jl @@ -17,7 +17,7 @@ function Not(; name) @named y = DigitalPin() eqs = [x.i ~ y.i - y.val ~ _not(x.val)] + y.val ~ _not(x.val)] ODESystem(eqs, t, [], [], systems = [x, y], name = name) end @@ -43,7 +43,7 @@ function And(; name, N = 2) vals = [k.val for k in x] eqs = [y.val ~ _and(vals...) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end @@ -69,7 +69,7 @@ function Nand(; name, N = 2) vlist = [k.val for k in x] eqs = [y.val ~ _not(_and(vlist...)) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end @@ -95,7 +95,7 @@ function Or(; name, N = 2) vals = [k.val for k in x] eqs = [y.val ~ _or(vals...) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end @@ -121,7 +121,7 @@ function Nor(; name, N = 2) vlist = [k.val for k in x] eqs = [y.val ~ _not(_or(vlist...)) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end @@ -147,7 +147,7 @@ function Xor(; name, N = 2) vals = [k.val for k in x] eqs = [y.val ~ _xor(vals...) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end @@ -173,6 +173,6 @@ function Xnor(; name, N = 2) vlist = [k.val for k in x] eqs = [y.val ~ _not(_xor(vlist...)) - y.i ~ sum(k -> k.i, x)] + y.i ~ sum(k -> k.i, x)] ODESystem(eqs, t, [], [], systems = [x..., y], name = name) end diff --git a/src/Electrical/Digital/sources.jl b/src/Electrical/Digital/sources.jl index 17537c088..b2a41a8e8 100644 --- a/src/Electrical/Digital/sources.jl +++ b/src/Electrical/Digital/sources.jl @@ -19,7 +19,7 @@ function PulseDiff(; name, Val = 1, dt = 0.1) D = ModelingToolkit.Difference(t; dt = dt) eqs = [D(val) ~ Val - val ~ d.val] + val ~ d.val] ODESystem(eqs, t, [val], [], systems = [d], defaults = Dict(Val => 0), name = name) end @@ -40,7 +40,7 @@ function Set(; name) @named d = DigitalPin() eqs = [ - d.val ~ 1, + d.val ~ 1 ] ODESystem(eqs, t, [], [], systems = [d], name = name) end @@ -61,7 +61,7 @@ function Reset(; name) @named d = DigitalPin() eqs = [ - d.val ~ 0, + d.val ~ 0 ] ODESystem(eqs, t, [], [], systems = [d], name = name) end @@ -82,7 +82,7 @@ function Pulse(; name, duty_cycle = 0.5, T = 1.0) @named d = DigitalPin() eqs = [ - d.val ~ IfElse.ifelse(t % T > duty_cycle * T, 1, 0), + d.val ~ IfElse.ifelse(t % T > duty_cycle * T, 1, 0) ] ODESystem(eqs, t, [], [], systems = [d], name = name) end diff --git a/src/Electrical/Digital/tables.jl b/src/Electrical/Digital/tables.jl index 599ada98e..beb171730 100644 --- a/src/Electrical/Digital/tables.jl +++ b/src/Electrical/Digital/tables.jl @@ -37,16 +37,16 @@ get_logic_level(l::LogicTable) = Int.(l.logic) # AND gate const AndTable = LogicTable([ -# U X F0 F1 Z W L H DC - U U F0 U U U F0 U U # U - U X F0 X X X F0 X X # X - F0 F0 F0 F0 F0 F0 F0 F0 F0 # F0 - U X F0 F1 X X F0 F1 X # F1 - U X F0 X X X F0 X X # Z - U X F0 X X X F0 X X # W - F0 F0 F0 F0 F0 F0 F0 F0 F0 # L - U X F0 F1 X X F0 F1 X # H - U X F0 X X X F0 X X]) # DC + # U X F0 F1 Z W L H DC + U U F0 U U U F0 U U # U + U X F0 X X X F0 X X # X + F0 F0 F0 F0 F0 F0 F0 F0 F0 # F0 + U X F0 F1 X X F0 F1 X # F1 + U X F0 X X X F0 X X # Z + U X F0 X X X F0 X X # W + F0 F0 F0 F0 F0 F0 F0 F0 F0 # L + U X F0 F1 X X F0 F1 X # H + U X F0 X X X F0 X X]) # DC function _and2(a::Logic, b::Logic) AndTable[a, b] @@ -75,16 +75,16 @@ _not(x::Number) = _not(convert(Logic, x)) # OR gate const OrTable = LogicTable([ -# U X F0 F1 Z W L H DC - U U U F1 U U U F1 U # U - U X X F1 X X X F1 X # X - U X F0 F1 X X F0 F1 X # F0 - F1 F1 F1 F1 F1 F1 F1 F1 F1 # F1 - U X X F1 X X X F1 X # Z - U X X F1 X X X F1 X # W - U X F0 F1 X X F0 F1 X # L - F1 F1 F1 F1 F1 F1 F1 F1 F1 # H - U X X F1 X X X F1 X]) # DC + # U X F0 F1 Z W L H DC + U U U F1 U U U F1 U # U + U X X F1 X X X F1 X # X + U X F0 F1 X X F0 F1 X # F0 + F1 F1 F1 F1 F1 F1 F1 F1 F1 # F1 + U X X F1 X X X F1 X # Z + U X X F1 X X X F1 X # W + U X F0 F1 X X F0 F1 X # L + F1 F1 F1 F1 F1 F1 F1 F1 F1 # H + U X X F1 X X X F1 X]) # DC function _or2(a::Logic, b::Logic) OrTable[a, b] @@ -105,16 +105,16 @@ end # XOR gate const XorTable = LogicTable([ -# U X F0 F1 Z W L H DC - U U U U U U U U U # U - U X X X X X X X X # X - U X F0 F1 X X F0 F1 X # F0 - U X F1 F0 X X F1 F0 X # F1 - U X X X X X X X X # Z - U X X X X X X X X # W - U X F0 F1 X X F0 F1 X # L - U X F1 F0 X X F1 F0 X # H - U X X X X X X X X]) # DC + # U X F0 F1 Z W L H DC + U U U U U U U U U # U + U X X X X X X X X # X + U X F0 F1 X X F0 F1 X # F0 + U X F1 F0 X X F1 F0 X # F1 + U X X X X X X X X # Z + U X X X X X X X X # W + U X F0 F1 X X F0 F1 X # L + U X F1 F0 X X F1 F0 X # H + U X X X X X X X X]) # DC function _xor2(a::Logic, b::Logic) XorTable[a, b] diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index 720febb03..46e04531b 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -21,8 +21,8 @@ export Pin, OnePort include("utils.jl") export Capacitor, - Ground, Inductor, Resistor, Conductor, Short, IdealOpAmp, EMF, - HeatingResistor + Ground, Inductor, Resistor, Conductor, Short, IdealOpAmp, EMF, + HeatingResistor include("Analog/ideal_components.jl") export CurrentSensor, PotentialSensor, VoltageSensor, PowerSensor, MultiSensor @@ -43,12 +43,12 @@ export Logic include("Digital/logic.jl") export StdLogicVector, StdULogicVector, - std_ulogic, UX01, UX01Z, X01, X01Z, - get_logic_level + std_ulogic, UX01, UX01Z, X01, X01Z, + get_logic_level include("Digital/logic_vectors.jl") export LogicTable, - AndTable, OrTable, NotTable, XorTable + AndTable, OrTable, NotTable, XorTable include("Digital/tables.jl") end diff --git a/src/Electrical/utils.jl b/src/Electrical/utils.jl index 32151812a..08cdc3f23 100644 --- a/src/Electrical/utils.jl +++ b/src/Electrical/utils.jl @@ -89,7 +89,7 @@ end @variables val(t) v(t) i(t) eqs = [ val ~ IfElse.ifelse((0.0 <= v) & (v <= 0.8) | (2.0 <= v) & (v <= 5.0), - IfElse.ifelse(v > 2.0, 1, 0), X), + IfElse.ifelse(v > 2.0, 1, 0), X) ] ODESystem(Equation[], t, [val, v, i], [], defaults = Dict(val => 0, i => 0), name = name) diff --git a/src/Hydraulic/IsothermalCompressible/components.jl b/src/Hydraulic/IsothermalCompressible/components.jl index b7422f35c..8e41b39fb 100644 --- a/src/Hydraulic/IsothermalCompressible/components.jl +++ b/src/Hydraulic/IsothermalCompressible/components.jl @@ -20,7 +20,7 @@ Caps a hydraulic port to prevent mass flow in or out. end eqs = [port.p ~ p - port.dm ~ 0] + port.dm ~ 0] ODESystem(eqs, t, vars, pars; name, systems) end @@ -38,7 +38,7 @@ end end eqs = [port.p ~ p - port.dm ~ dm] + port.dm ~ dm] ODESystem(eqs, t, vars, pars; name, systems) end @@ -117,7 +117,7 @@ Variable length internal flow model of the fully developed incompressible flow f end eqs = [0 ~ port_a.dm + port_b.dm - domain_connect(port_a, port_b)] + domain_connect(port_a, port_b)] if variable_length push!(eqs, Δp ~ ifelse(c > 0, shear + inertia, zero(c))) @@ -204,7 +204,7 @@ Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `Tub end eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a) - connect(volumes[end].port, pipe_bases[end].port_b, port_b)] + connect(volumes[end].port, pipe_bases[end].port_b, port_b)] for i in 2:(N - 1) push!(eqs, @@ -253,11 +253,11 @@ Reduces the flow from `port_a` to `port_b` by `n`. Useful for modeling parallel end eqs = [connect(port_a, port_b, open.port) - dm_a ~ port_a.dm - dm_b ~ dm_a / n - open.dm ~ dm_a - dm_b # extra flow dumps into an open port - # port_b.dm ~ dm_b # divided flow goes to port_b - ] + dm_a ~ port_a.dm + dm_b ~ dm_a / n + open.dm ~ dm_a - dm_b # extra flow dumps into an open port + # port_b.dm ~ dm_b # divided flow goes to port_b + ] ODESystem(eqs, t, vars, pars; name, systems) end @@ -303,9 +303,9 @@ end end eqs = [0 ~ port_a.dm + port_b.dm - domain_connect(port_a, port_b) - dm ~ regRoot(2 * Δp * ρ / c) * x - y ~ x] + domain_connect(port_a, port_b) + dm ~ regRoot(2 * Δp * ρ / c) * x + y ~ x] ODESystem(eqs, t, vars, pars; name, systems) end @@ -352,8 +352,8 @@ Valve with `area` input and discharge coefficient `Cd` defined by https://en.wik vars = [] eqs = [connect(base.port_a, port_a) - connect(base.port_b, port_b) - base.area ~ area.u] + connect(base.port_b, port_b) + base.area ~ area.u] ODESystem(eqs, t, vars, pars; name, systems, defaults = [area.u => area_int]) end @@ -384,10 +384,10 @@ end p = port.p eqs = [vol ~ dead_volume + area * x - D(x) ~ dx - D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] + D(x) ~ dx + D(rho) ~ drho + rho ~ full_density(port, p) + dm ~ drho * vol * Χ1 + rho * area * dx * Χ2] ODESystem(eqs, t, vars, pars; name, systems) end @@ -424,8 +424,8 @@ Fixed fluid volume. p = port.p eqs = [D(rho) ~ drho - rho ~ full_density(port, p) - dm ~ drho * vol] + rho ~ full_density(port, p) + dm ~ drho * vol] ODESystem(eqs, t, vars, pars; name, systems) end @@ -564,9 +564,9 @@ dm ────► │ │ area end eqs = [vol ~ x * area - D(x) ~ flange.v * direction - damper.area ~ damper_area - connect(port, damper.port_b)] + D(x) ~ flange.v * direction + damper.area ~ damper_area + connect(port, damper.port_b)] volumes = [] if N > 0 @@ -597,8 +597,9 @@ dm ────► │ │ area for i in 1:N push!(eqs, - volumes[i].dx ~ ifelse((vol >= (i - 1) * (x_max / N) * area) & - (vol < (i) * (x_max / N) * area), + volumes[i].dx ~ ifelse( + (vol >= (i - 1) * (x_max / N) * area) & + (vol < (i) * (x_max / N) * area), flange.v * direction, 0)) push!(eqs, pipe_bases[i].x ~ volumes[i].vol / volumes[i].area) end @@ -637,11 +638,11 @@ end end eqs = [D(x) ~ dx - flange.v ~ dx - flange.f ~ 0 #TODO: model flow force - connect(valve.port_a, port_a) - connect(valve.port_b, port_b) - valve.area ~ x * 2π * d] + flange.v ~ dx + flange.f ~ 0 #TODO: model flow force + connect(valve.port_a, port_a) + connect(valve.port_b, port_b) + valve.area ~ x * 2π * d] ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) end @@ -681,10 +682,10 @@ end end eqs = [connect(vSA.port_a, port_s) - connect(vSA.port_b, port_a) - connect(vBR.port_a, port_b) - connect(vBR.port_b, port_r) - connect(vSA.flange, vBR.flange, mass.flange, flange)] + connect(vSA.port_b, port_a) + connect(vBR.port_a, port_b) + connect(vBR.port_b, port_r) + connect(vSA.flange, vBR.flange, mass.flange, flange)] ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) end @@ -777,10 +778,10 @@ end end eqs = [connect(vol_a.port, port_a) - connect(vol_b.port, port_b) - connect(vol_a.flange, vol_b.flange, mass.flange, flange) - D(x) ~ dx - dx ~ vol_a.flange.v] + connect(vol_b.port, port_b) + connect(vol_a.flange, vol_b.flange, mass.flange, flange) + D(x) ~ dx + dx ~ vol_a.flange.v] ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0]) end diff --git a/src/Hydraulic/IsothermalCompressible/sources.jl b/src/Hydraulic/IsothermalCompressible/sources.jl index 237962489..4f7f53b68 100644 --- a/src/Hydraulic/IsothermalCompressible/sources.jl +++ b/src/Hydraulic/IsothermalCompressible/sources.jl @@ -18,7 +18,7 @@ Hydraulic mass flow input source vars = [] eqs = [ - port.dm ~ -dm.u, + port.dm ~ -dm.u ] ODESystem(eqs, t, vars, pars; name, systems) @@ -47,7 +47,7 @@ Fixed pressure source end eqs = [ - port.p ~ p, + port.p ~ p ] ODESystem(eqs, t, vars, pars; name, systems) @@ -79,7 +79,7 @@ input pressure source end eqs = [ - port.p ~ p.u, + port.p ~ p.u ] ODESystem(eqs, t, vars, pars; name, systems) diff --git a/src/Hydraulic/IsothermalCompressible/utils.jl b/src/Hydraulic/IsothermalCompressible/utils.jl index d88f24f42..bd09012a5 100644 --- a/src/Hydraulic/IsothermalCompressible/utils.jl +++ b/src/Hydraulic/IsothermalCompressible/utils.jl @@ -68,7 +68,7 @@ Fluid parameter setter for isothermal compressible fluid domain. Defaults given end eqs = [ - dm ~ 0, + dm ~ 0 ] ODESystem(eqs, t, vars, pars; name, defaults = [dm => 0]) diff --git a/src/Magnetic/FluxTubes/FluxTubes.jl b/src/Magnetic/FluxTubes/FluxTubes.jl index 4d39a3a9f..1c9b0c48e 100644 --- a/src/Magnetic/FluxTubes/FluxTubes.jl +++ b/src/Magnetic/FluxTubes/FluxTubes.jl @@ -11,7 +11,7 @@ export PositiveMagneticPort, NegativeMagneticPort, TwoPort include("utils.jl") export Ground, Idle, Short, Crossing, ConstantPermeance, ConstantReluctance, EddyCurrent, - ElectroMagneticConverter + ElectroMagneticConverter include("basic.jl") export ConstantMagneticPotentialDifference, ConstantMagneticFlux diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 979b71821..5282cfa71 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -3,8 +3,10 @@ use_support = false) @variables begin phi(t), - [description = "Angle of flange with respect to support", - unit = u"rad"] + [ + description = "Angle of flange with respect to support (= flange.phi - support.phi)", + unit = u"rad", + ] end @equations begin phi ~ flange.phi - phi_support @@ -63,7 +65,7 @@ Constant torque source tau_constant, [ description = "Constant torque (if negative, torque is acting as load in positive direction of rotation)", - unit = u"N*m"] + unit = u"N*m" ] end @extend flange, phi = partial_element = PartialTorque(; use_support = false) @@ -108,7 +110,7 @@ Forced movement of a flange according to a reference angular velocity signal @variables w(t) = 0.0 [description = "Angular velocity", unit = u"rad*s^-1"] @variables a(t) = 0.0 [description = "Angular acceleration", unit = u"rad*s^-2"] eqs = [phi ~ flange.phi - phi_support - D(phi) ~ w] + D(phi) ~ w] if exact pars = [] push!(eqs, w ~ w_ref.u) diff --git a/src/Mechanical/Rotational/utils.jl b/src/Mechanical/Rotational/utils.jl index 1d6b23fd0..b76b21b84 100644 --- a/src/Mechanical/Rotational/utils.jl +++ b/src/Mechanical/Rotational/utils.jl @@ -148,7 +148,7 @@ Partial model for a component with one rotational 1-dim. shaft flange and a supp if use_support @named support = Support() eqs = [support.phi ~ phi_support - support.tau ~ -flange.tau] + support.tau ~ -flange.tau] push!(sys, support) else eqs = [phi_support ~ 0] @@ -186,7 +186,7 @@ Partial model for a component with two rotational 1-dim. shaft flanges and a sup if use_support @named support = Support() eqs = [support.phi ~ phi_support - support.tau ~ -flange_a.tau - flange_b.tau] + support.tau ~ -flange_a.tau - flange_b.tau] push!(sys, support) else eqs = [phi_support ~ 0] diff --git a/src/Mechanical/Translational/components.jl b/src/Mechanical/Translational/components.jl index c8d909944..a3087d505 100644 --- a/src/Mechanical/Translational/components.jl +++ b/src/Mechanical/Translational/components.jl @@ -69,7 +69,7 @@ Sliding mass with inertia vars = [v, f] eqs = [flange.v ~ v - flange.f ~ f] + flange.f ~ f] # gravity option if g !== nothing @@ -127,9 +127,9 @@ end # default @named flange_b = MechanicalPort(; v = flange_b__v) eqs = [D(delta_s) ~ flange_a.v - flange_b.v - f ~ k * delta_s - flange_a.f ~ +f - flange_b.f ~ -f] + f ~ k * delta_s + flange_a.f ~ +f + flange_b.f ~ -f] return compose(ODESystem(eqs, t, [delta_s, f], pars; name = name), flange_a, flange_b) #flange_a.f => +k*delta_s, flange_b.f => -k*delta_s @@ -150,10 +150,10 @@ const ABS = Val(:absolute) @named flange_b = MechanicalPort(; v = flange_b__v) eqs = [D(sa) ~ flange_a.v - D(sb) ~ flange_b.v - f ~ k * (sa - sb - l) #delta_s - flange_a.f ~ +f - flange_b.f ~ -f] + D(sb) ~ flange_b.v + f ~ k * (sa - sb - l) #delta_s + flange_a.f ~ +f + flange_b.f ~ -f] return compose(ODESystem(eqs, t, [sa, sb, f], pars; name = name), flange_a, flange_b) #, flange_a.f => k * (flange_a__s - flange_b__s - l) diff --git a/src/Mechanical/Translational/sources.jl b/src/Mechanical/Translational/sources.jl index 07a4fd21c..508a7ccd7 100644 --- a/src/Mechanical/Translational/sources.jl +++ b/src/Mechanical/Translational/sources.jl @@ -62,7 +62,7 @@ Linear 1D position input source. Set `solves_force=false` to force input force end eqs = [ - v.u ~ flange.v, + v.u ~ flange.v ] !solves_force && push!(eqs, 0 ~ flange.f) diff --git a/src/Mechanical/TranslationalModelica/utils.jl b/src/Mechanical/TranslationalModelica/utils.jl index c445b8b81..273c7c02b 100644 --- a/src/Mechanical/TranslationalModelica/utils.jl +++ b/src/Mechanical/TranslationalModelica/utils.jl @@ -103,7 +103,6 @@ function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @variables s(t) [ description = "Distance between flange and support (= flange.s - support.s)", unit = u"m" - ] eqs = [s ~ flange.s - s_support] if use_support @@ -137,7 +136,7 @@ function PartialElementaryTwoFlangesAndSupport2(; name, use_support = false) @variables s_support(t) [description = "Absolute position of support flange", unit = u"m"] eqs = [s_a ~ flange_a.s - s_support - s_b ~ flange_b.s - s_support] + s_b ~ flange_b.s - s_support] if use_support @named support = Support() push!(eqs, support.f ~ -flange_a.f - flange_b.f) diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index e6faeb340..5bf0d8876 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -79,19 +79,22 @@ const REL = Val(:relative) @named flange_b = Flange() eqs = [D(flange_a.s) ~ va - D(flange_b.s) ~ vb - D(delta_s) ~ va - vb - f ~ k * delta_s - flange_a.f ~ +f - flange_b.f ~ -f] - - return compose(ODESystem(eqs, t, vars, pars; name = name, + D(flange_b.s) ~ vb + D(delta_s) ~ va - vb + f ~ k * delta_s + flange_a.f ~ +f + flange_b.f ~ -f] + + return compose( + ODESystem(eqs, t, vars, pars; name = name, defaults = [ flange_a.s => flange_a__s, flange_b.s => flange_b__s, flange_a.f => +delta_s * k, - flange_b.f => -delta_s * k, - ]), flange_a, flange_b) + flange_b.f => -delta_s * k + ]), + flange_a, + flange_b) end const ABS = Val(:absolute) @@ -130,11 +133,11 @@ end #default function @named flange_b = Flange(; s = flange_a__s, f = -k * (flange_a__s - flange_b__s - l)) eqs = [ - # delta_s ~ flange_a.s - flange_b.s - f ~ k * (flange_a.s - flange_b.s - l) #delta_s - flange_a.f ~ +f - flange_b.f ~ -f] - return compose(ODESystem(eqs, t, [f], pars; name = name), flange_a, flange_b) + # delta_s ~ flange_a.s - flange_b.s + f ~ k * (flange_a.s - flange_b.s - l) #delta_s + flange_a.f ~ +f + flange_b.f ~ -f] + return compose(ODESystem(eqs, t, vars, pars; name = name), flange_a, flange_b) end """ diff --git a/src/Mechanical/TranslationalPosition/utils.jl b/src/Mechanical/TranslationalPosition/utils.jl index bbd1a0249..e133c649e 100644 --- a/src/Mechanical/TranslationalPosition/utils.jl +++ b/src/Mechanical/TranslationalPosition/utils.jl @@ -103,7 +103,7 @@ Partial model for a component with one translational 1-dim. shaft flange and a s if use_support @named support = Support() eqs = [support.s ~ s_support - support.f ~ -flange.f] + support.f ~ -flange.f] push!(sys, support) else eqs = [s_support ~ 0] @@ -132,7 +132,7 @@ Partial model for a component with two translational 1-dim. flanges and a suppor if use_support @named support = Support() eqs = [support.s ~ s_support - support.f ~ -flange_a.f - flange_b.f] + support.f ~ -flange_a.f - flange_b.f] push!(sys, support) else eqs = [s_support ~ 0] diff --git a/src/Thermal/HeatTransfer/ideal_components.jl b/src/Thermal/HeatTransfer/ideal_components.jl index 027e1e045..dccb76321 100644 --- a/src/Thermal/HeatTransfer/ideal_components.jl +++ b/src/Thermal/HeatTransfer/ideal_components.jl @@ -205,7 +205,7 @@ This is a model to collect the heat flows from `m` heatports to one single heatp port_a = [HeatPort(name = Symbol(:port_a, i)) for i in 1:m] @named port_b = HeatPort() eqs = [port_b.Q_flow + sum(k -> k.Q_flow, port_a) ~ 0 - port_b.T ~ port_a[1].T] + port_b.T ~ port_a[1].T] for i in 1:(m - 1) push!(eqs, port_a[i].T ~ port_a[i + 1].T) end diff --git a/src/Thermal/Thermal.jl b/src/Thermal/Thermal.jl index ce0900ac5..f798d4367 100644 --- a/src/Thermal/Thermal.jl +++ b/src/Thermal/Thermal.jl @@ -13,8 +13,8 @@ export HeatPort, Element1D include("utils.jl") export BodyRadiation, ConvectiveConductor, ConvectiveResistor, HeatCapacitor, - ThermalConductor, - ThermalResistor, ThermalCollector + ThermalConductor, + ThermalResistor, ThermalCollector include("HeatTransfer/ideal_components.jl") export RelativeTemperatureSensor, HeatFlowSensor, TemperatureSensor diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index bd9a4a7ae..11adea1f8 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -29,9 +29,10 @@ end @named source = Sine(; frequency = 1) @named int = Integrator(; k = 1) @named der = Derivative(; k = 1, T = 0.001) - @named iosys = ODESystem([ + @named iosys = ODESystem( + [ connect(source.output, der.input), - connect(der.output, int.input), + connect(der.output, int.input) ], t, systems = [int, source, der]) @@ -97,7 +98,7 @@ end @named ss = StateSpace(; A, B, C, D, x = zeros(2)) @named c = Constant(; k = 1) @named model = ODESystem([ - connect(c.output, ss.input), + connect(c.output, ss.input) ], t, systems = [ss, c]) @@ -117,7 +118,7 @@ end y0 = [2] @named ss = StateSpace(; A, B, C, D, x = zeros(2), u0, y0) @named model = ODESystem([ - connect(c.output, ss.input), + connect(c.output, ss.input) ], t, systems = [ss, c]) @@ -141,8 +142,8 @@ Second order demo plant D = Differential(t) sts = @variables x1(t)=x[1] x2(t)=x[2] eqs = [D(x1) ~ x2 - D(x2) ~ -x1 - 0.5 * x2 + input.u - output.u ~ 0.9 * x1 + x2] + D(x2) ~ -x1 - 0.5 * x2 + input.u + output.u ~ 0.9 * x1 + x2] compose(ODESystem(eqs, t, sts, []; name), [input, output]) end @@ -152,11 +153,12 @@ end @named pi_controller = PI(k = 1, T = 1) @named plant = Plant() @named fb = Feedback() - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pi_controller.err_input), - connect(pi_controller.ctr_output, plant.input), + connect(pi_controller.ctr_output, plant.input) ], t, systems = [pi_controller, plant, ref, fb]) @@ -174,11 +176,12 @@ end @named pid_controller = PID(k = 3, Ti = 0.5, Td = 1 / 100) @named plant = Plant() @named fb = Feedback() - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pid_controller.err_input), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref, fb]) @@ -191,11 +194,12 @@ end @testset "PI" begin @named pid_controller = PID(k = 3, Ti = 0.5, Td = false) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pid_controller.err_input), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref, fb]) @@ -209,11 +213,12 @@ end @testset "PD" begin @named pid_controller = PID(k = 10, Ti = false, Td = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pid_controller.err_input), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref, fb]) @@ -241,12 +246,13 @@ end # without anti-windup measure sol = let - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pi_controller.err_input), connect(pi_controller.ctr_output, sat.input), - connect(sat.output, plant.input), + connect(sat.output, plant.input) ], t, systems = [pi_controller, plant, ref, fb, sat]) @@ -257,12 +263,13 @@ end # with anti-windup measure sol_lim = let - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, fb.input1), connect(plant.output, fb.input2), connect(fb.output, pi_controller_lim.err_input), connect(pi_controller_lim.ctr_output, sat.input), - connect(sat.output, plant.input), + connect(sat.output, plant.input) ], t, systems = [pi_controller_lim, plant, ref, fb, sat]) @@ -286,13 +293,15 @@ end @testset "LimPID" begin re_val = 1 @named ref = Constant(; k = re_val) - @named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5, u_min = -1.5, + @named pid_controller = LimPID( + k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5, u_min = -1.5, Ni = 0.1 / 0.5) @named plant = Plant() - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) @@ -309,10 +318,11 @@ end @testset "PI" begin @named pid_controller = LimPID(k = 3, Ti = 0.5, Td = false, u_max = 1.5, u_min = -1.5, Ni = 0.1 / 0.5) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) @@ -329,10 +339,11 @@ end @testset "PD" begin @named pid_controller = LimPID(k = 10, Ti = false, Td = 1, u_max = 1.5, u_min = -1.5) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) @@ -350,10 +361,11 @@ end @testset "wp" begin @named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5, u_min = -1.5, Ni = 0.1 / 0.5, wp = 0, wd = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) @@ -371,10 +383,11 @@ end @testset "wd" begin @named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5, u_min = -1.5, Ni = 0.1 / 0.5, wp = 1, wd = 0) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) @@ -393,10 +406,11 @@ end @testset "PI without AWM" begin @named pid_controller = LimPID(k = 3, Ti = 0.5, Td = false, u_max = 1.5, u_min = -1.5, Ni = Inf) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(ref.output, pid_controller.reference), connect(plant.output, pid_controller.measurement), - connect(pid_controller.ctr_output, plant.input), + connect(pid_controller.ctr_output, plant.input) ], t, systems = [pid_controller, plant, ref]) diff --git a/test/Blocks/math.jl b/test/Blocks/math.jl index e6945696e..185f60454 100644 --- a/test/Blocks/math.jl +++ b/test/Blocks/math.jl @@ -11,10 +11,12 @@ using DynamicQuantities: @u_str @named c = Constant(; k = 1) @named gain = Gain(; k = 1) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c.output, gain.input), - connect(gain.output, int.input), - ], t, systems = [int, gain, c]) + connect(gain.output, int.input) + ], + t, systems = [int, gain, c]) sys = structural_simplify(model) prob = ODEProblem(sys, Pair[int.x => 1.0], (0.0, 1.0)) @@ -31,11 +33,12 @@ end @named gain = Gain(; k = 1) @named int = Integrator(; k = 1) @named fb = Feedback(;) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c.output, fb.input1), connect(fb.input2, int.output), connect(fb.output, gain.input), - connect(gain.output, int.input), + connect(gain.output, int.input) ], t, systems = [int, gain, c, fb]) @@ -54,10 +57,11 @@ end @named c2 = Sine(; frequency = 1) @named add = Add(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, add.input1), connect(c2.output, add.input2), - connect(add.output, int.input), + connect(add.output, int.input) ], t, systems = [int, add, c1, c2]) @@ -72,10 +76,11 @@ end k1 = -1 k2 = 2 @named add = Add(; k1 = k1, k2 = k2) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, add.input1), connect(c2.output, add.input2), - connect(add.output, int.input), + connect(add.output, int.input) ], t, systems = [int, add, c1, c2]) @@ -94,11 +99,12 @@ end @named c3 = Sine(; frequency = 2) @named add = Add3(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, add.input1), connect(c2.output, add.input2), connect(c3.output, add.input3), - connect(add.output, int.input), + connect(add.output, int.input) ], t, systems = [int, add, c1, c2, c3]) @@ -114,11 +120,12 @@ end k2 = 2 k3 = -pi @named add = Add3(; k1 = k1, k2 = k2, k3 = k3) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, add.input1), connect(c2.output, add.input2), connect(c3.output, add.input3), - connect(add.output, int.input), + connect(add.output, int.input) ], t, systems = [int, add, c1, c2, c3]) @@ -137,10 +144,11 @@ end @named c2 = Sine(; frequency = 1) @named prod = Product(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, prod.input1), connect(c2.output, prod.input2), - connect(prod.output, int.input), + connect(prod.output, int.input) ], t, systems = [int, prod, c1, c2]) @@ -157,10 +165,11 @@ end @named c2 = Constant(; k = 2) @named div = Division(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, div.input1), connect(c2.output, div.input2), - connect(div.output, int.input), + connect(div.output, int.input) ], t, systems = [int, div, c1, c2]) @@ -176,9 +185,10 @@ end @named c = Sine(; frequency = 1) @named absb = Abs(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c.output, absb.input), - connect(absb.output, int.input), + connect(absb.output, int.input) ], t, systems = [int, absb, c]) @@ -216,16 +226,18 @@ end (Sinh, sinh), (Cosh, cosh), (Tanh, tanh), - (Exp, exp), + (Exp, exp) ] @info "testing $block" @named source = Sine(frequency = 1, amplitude = 0.5) @named b = block() @named int = Integrator() - @named model = ODESystem([ + @named model = ODESystem( + [ connect(source.output, b.input), - connect(b.output, int.input), - ], t, systems = [int, b, source]) + connect(b.output, int.input) + ], + t, systems = [int, b, source]) sys = structural_simplify(model) prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 1.0)) sol = solve(prob, Rodas4()) @@ -240,10 +252,12 @@ end @named source = Sine(; frequency = 1, offset = 2, amplitude = 0.5) @named b = block() @named int = Integrator() - @named model = ODESystem([ + @named model = ODESystem( + [ connect(source.output, b.input), - connect(b.output, int.input), - ], t, systems = [int, b, source]) + connect(b.output, int.input) + ], + t, systems = [int, b, source]) sys = structural_simplify(model) prob = ODEProblem(sys, Pair[int.x => 0.0, b.input.u => 2.0], (0.0, 1.0)) sol = solve(prob, Rodas4()) @@ -258,10 +272,11 @@ end @named c2 = Sine(; frequency = 1, offset = 1) @named b = Atan2(;) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c1.output, b.input1), connect(c2.output, b.input2), - connect(b.output, int.input), + connect(b.output, int.input) ], t, systems = [int, b, c1, c2]) diff --git a/test/Blocks/nonlinear.jl b/test/Blocks/nonlinear.jl index ca14763ab..a902c4a2d 100644 --- a/test/Blocks/nonlinear.jl +++ b/test/Blocks/nonlinear.jl @@ -11,9 +11,10 @@ using DynamicQuantities: @u_str @named c = Constant(; k = 1) @named int = Integrator(; k = 1) @named sat = Limiter(; y_min = -0.6, y_max = 0.8) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c.output, int.input), - connect(int.output, sat.input), + connect(int.output, sat.input) ], t, systems = [int, c, sat]) @@ -31,9 +32,10 @@ using DynamicQuantities: @u_str @named source = Sine(; frequency = 1 / 2) @named lim = Limiter(; y_max = y_max, y_min = y_min) @named int = Integrator(; k = 1) - @named iosys = ODESystem([ + @named iosys = ODESystem( + [ connect(source.output, lim.input), - connect(lim.output, int.input), + connect(lim.output, int.input) ], t, systems = [source, lim, int]) @@ -58,9 +60,10 @@ end @named c = Constant(; k = 1) @named int = Integrator(; k = 1) @named dz = DeadZone(; u_min = -2, u_max = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(c.output, int.input), - connect(int.output, dz.input), + connect(int.output, dz.input) ], t, systems = [int, c, dz]) @@ -77,9 +80,10 @@ end @named source = Sine(; amplitude = 3, frequency = 1 / 2) @named dz = DeadZone(; u_min = u_min, u_max = u_max) @named int = Integrator(; k = 1) - @named model = ODESystem([ + @named model = ODESystem( + [ connect(source.output, dz.input), - connect(dz.output, int.input), + connect(dz.output, int.input) ], t, systems = [int, source, dz]) @@ -103,7 +107,7 @@ end @named source = Sine(; frequency = 1 / 2) @named rl = SlewRateLimiter(; rising = 1, falling = -1, Td = 0.001, y_start = -1 / 3) @named iosys = ODESystem([ - connect(source.output, rl.input), + connect(source.output, rl.input) ], t, systems = [source, rl]) diff --git a/test/Blocks/sources.jl b/test/Blocks/sources.jl index 6c1ec6ca9..f2f9dc3e5 100644 --- a/test/Blocks/sources.jl +++ b/test/Blocks/sources.jl @@ -1,8 +1,8 @@ using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, - smooth_square, smooth_step, smooth_ramp, - smooth_triangular, triangular, square + smooth_square, smooth_step, smooth_ramp, + smooth_triangular, triangular, square using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str @@ -13,7 +13,7 @@ D = Differential(t) @named src = Constant(k = 2) @named int = Integrator() @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -31,10 +31,11 @@ end vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 @named src = TimeVaryingFunction(f) @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], + @named iosys = ODESystem( + [y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], t, systems = [int, src]) sys = structural_simplify(iosys) @@ -64,7 +65,7 @@ end @named src = Sine(frequency = frequency, amplitude = amplitude, phase = phase, offset = offset, start_time = start_time) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -83,7 +84,7 @@ end start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -93,7 +94,8 @@ end smooth_sol = solve(smooth_prob, Rodas4()) @test sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_sin.(smooth_sol.t, δ, frequency, amplitude, + @test smooth_sol[smooth_src.output.u]≈smooth_sin.( + smooth_sol.t, δ, frequency, amplitude, phase, offset, start_time) atol=1e-3 end @@ -118,7 +120,7 @@ end start_time = start_time, smooth = false) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -136,7 +138,7 @@ end start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -146,7 +148,8 @@ end smooth_sol = solve(smooth_prob, Rodas4()) @test smooth_sol.retcode == Success - @test smooth_sol[smooth_src.output.u]≈smooth_cos.(smooth_sol.t, δ, frequency, amplitude, + @test smooth_sol[smooth_src.output.u]≈smooth_cos.( + smooth_sol.t, δ, frequency, amplitude, phase, offset, start_time) atol=1e-3 end @@ -158,7 +161,7 @@ end @named src = ContinuousClock(offset = offset, start_time = start_time) @named int = Integrator() @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -184,7 +187,7 @@ end @named src = Ramp(offset = offset, height = height, duration = duration, start_time = start_time) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -199,7 +202,7 @@ end @named smooth_src = Ramp(offset = offset, height = height, duration = duration, start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -222,7 +225,7 @@ end @named src = Step(offset = offset, height = height, start_time = start_time, smooth = false) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -239,7 +242,7 @@ end @named src = Step(offset = offset, height = height, start_time = start_time, duration = duration, smooth = false) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -255,7 +258,7 @@ end @named smooth_src = Step(offset = offset, height = height, start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -272,7 +275,7 @@ end @named smooth_src = Step(offset = offset, height = height, start_time = start_time, smooth = true, duration = duration) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -298,7 +301,7 @@ end @named src = Square(frequency = frequency, amplitude = amplitude, offset = offset, start_time = start_time) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -313,7 +316,7 @@ end @named smooth_src = Square(frequency = frequency, amplitude = amplitude, offset = offset, start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -338,7 +341,7 @@ end @named src = Triangular(frequency = frequency, amplitude = amplitude, offset = offset, start_time = start_time) @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -353,7 +356,7 @@ end @named smooth_src = Triangular(frequency = frequency, amplitude = amplitude, offset = offset, start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -380,7 +383,7 @@ end phase = phase, offset = offset, start_time = start_time) @named int = Integrator() @named iosys = ODESystem([ - connect(src.output, int.input), + connect(src.output, int.input) ], t, systems = [int, src]) @@ -396,7 +399,7 @@ end damping = damping, phase = phase, offset = offset, start_time = start_time, smooth = true) @named smooth_iosys = ODESystem([ - connect(smooth_src.output, int.input), + connect(smooth_src.output, int.input) ], t, systems = [int, smooth_src]) @@ -422,10 +425,11 @@ end vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 @named src = SampledData(Float64) @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], + @named iosys = ODESystem( + [y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], t, systems = [int, src]) sys = structural_simplify(iosys) @@ -451,10 +455,11 @@ end vars = @variables y(t)=1 dy(t)=0 ddy(t)=0 @named src = SampledData(dt) @named int = Integrator() - @named iosys = ODESystem([y ~ src.output.u - D(y) ~ dy - D(dy) ~ ddy - connect(src.output, int.input)], + @named iosys = ODESystem( + [y ~ src.output.u + D(y) ~ dy + D(dy) ~ ddy + connect(src.output, int.input)], t, systems = [int, src]) sys = structural_simplify(iosys) diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index dcb15d6e5..5b2ded7bb 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -15,7 +15,7 @@ t = ModelingToolkit.get_iv(P) # Test with explicitly created AnalysisPoint ap = AnalysisPoint(:plant_input) eqs = [connect(P.output, C.input) - connect(C.output, ap, P.input)] + connect(C.output, ap, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :hej) ssys = structural_simplify(sys) @@ -46,7 +46,7 @@ T = comp_sensitivity(P, C) # or feedback(P*C) # Test with automatically created analysis point eqs = [connect(P.output, C.input) - connect(C.output, :plant_input, P.input)] + connect(C.output, :plant_input, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :hej) matrices, _ = get_sensitivity(sys, :plant_input) @@ -85,7 +85,7 @@ matrices, _ = linearize(open_sys, [u], [y]) # Test with more than one AnalysisPoint eqs = [connect(P.output, :plant_output, C.input) - connect(C.output, :plant_input, P.input)] + connect(C.output, :plant_input, P.input)] sys = ODESystem(eqs, t, systems = [P, C], name = :hej) matrices, _ = get_sensitivity(sys, :plant_input) @@ -112,8 +112,8 @@ matrices2, _ = linearize(sys, :plant_input, [P.output.u]) t = ModelingToolkit.get_iv(P) eqs = [connect(P.output, :plant_output, add.input2) - connect(add.output, C.input) - connect(C.output, :plant_input, P.input)] + connect(add.output, C.input) + connect(C.output, :plant_input, P.input)] # eqs = [connect(P.output, add.input2) # connect(add.output, C.input) @@ -125,7 +125,7 @@ sys_inner = ODESystem(eqs, t, systems = [P, C, add], name = :inner) @named F = FirstOrder(k = 1, T = 3) eqs = [connect(r.output, F.input) - connect(F.output, sys_inner.add.input1)] + connect(F.output, sys_inner.add.input1)] sys_outer = ODESystem(eqs, t, systems = [F, sys_inner, r], name = :outer) # test first that the structural_simplify works correctly @@ -164,8 +164,8 @@ c = 10 # Damping coefficient function SystemModel(u = nothing; name = :model) eqs = [connect(torque.flange, inertia1.flange_a) - connect(inertia1.flange_b, spring.flange_a, damper.flange_a) - connect(inertia2.flange_a, spring.flange_b, damper.flange_b)] + connect(inertia1.flange_b, spring.flange_a, damper.flange_a) + connect(inertia2.flange_a, spring.flange_b, damper.flange_b)] if u !== nothing push!(eqs, connect(torque.tau, u.output)) return @named model = ODESystem(eqs, t; @@ -175,7 +175,7 @@ function SystemModel(u = nothing; name = :model) inertia2, spring, damper, - u, + u ]) end ODESystem(eqs, t; systems = [torque, inertia1, inertia2, spring, damper], name) @@ -184,7 +184,7 @@ function AngleSensor(; name) @named flange = Flange() @named phi = RealOutput() eqs = [phi.u ~ flange.phi - flange.tau ~ 0] + flange.tau ~ 0] return ODESystem(eqs, t, [], []; name = name, systems = [flange, phi]) end @@ -196,11 +196,11 @@ model = SystemModel() @named er = Add(k2 = -1) connections = [connect(r.output, :r, filt.input) - connect(filt.output, er.input1) - connect(pid.ctr_output, :u, model.torque.tau) - connect(model.inertia2.flange_b, sensor.flange) - connect(sensor.phi, :y, er.input2) - connect(er.output, :e, pid.err_input)] + connect(filt.output, er.input1) + connect(pid.ctr_output, :u, model.torque.tau) + connect(model.inertia2.flange_b, sensor.flange) + connect(sensor.phi, :y, er.input2) + connect(er.output, :e, pid.err_input)] closed_loop = ODESystem(connections, t, systems = [model, pid, filt, sensor, r, er], name = :closed_loop) @@ -236,22 +236,27 @@ Si = ss(matrices...) @named P_inner = FirstOrder(k = 1, T = 1) @named feedback = Feedback() @named ref = Step() -### TODO -@named sys_inner = ODESystem([connect(P_inner.output, :y, feedback.input2) - connect(feedback.output, :u, P_inner.input) - connect(ref.output, :r, feedback.input1)], t, +@named sys_inner = ODESystem( + [connect(P_inner.output, :y, feedback.input2) + connect(feedback.output, :u, P_inner.input) + connect(ref.output, :r, feedback.input1)], + t, systems = [P_inner, feedback, ref]) Sinner = sminreal(ss(get_sensitivity(sys_inner, :u)[1]...)) -@named sys_inner = ODESystem([connect(P_inner.output, :y, feedback.input2) - connect(feedback.output, :u, P_inner.input)], t, +@named sys_inner = ODESystem( + [connect(P_inner.output, :y, feedback.input2) + connect(feedback.output, :u, P_inner.input)], + t, systems = [P_inner, feedback]) @named P_outer = FirstOrder(k = rand(), T = rand()) -@named sys_outer = ODESystem([connect(sys_inner.P_inner.output, :y2, P_outer.input) - connect(P_outer.output, :u2, sys_inner.feedback.input1)], t, +@named sys_outer = ODESystem( + [connect(sys_inner.P_inner.output, :y2, P_outer.input) + connect(P_outer.output, :u2, sys_inner.feedback.input1)], + t, systems = [P_outer, sys_inner]) Souter = sminreal(ss(get_sensitivity(sys_outer, :sys_inner_u)[1]...)) @@ -280,7 +285,7 @@ D = [0.0 0.0; 0.0 0.0] Kss = CS.ss(A, B, C, D) eqs = [connect(P.output, :plant_output, K.input) - connect(K.output, :plant_input, P.input)] + connect(K.output, :plant_input, P.input)] sys = ODESystem(eqs, t, systems = [P, K], name = :hej) matrices, _ = Blocks.get_sensitivity(sys, :plant_input) @@ -310,8 +315,8 @@ G = CS.feedback(Pss, Kss, pos_feedback = true) t = ModelingToolkit.get_iv(P) eqs = [connect(P.output, :plant_output, add.input2) - connect(add.output, C.input) - connect(C.output, :plant_input, P.input)] + connect(add.output, C.input) + connect(C.output, :plant_input, P.input)] sys_inner = ODESystem(eqs, t, systems = [P, C, add], name = :inner) @@ -319,7 +324,7 @@ sys_inner = ODESystem(eqs, t, systems = [P, C, add], name = :inner) @named F = FirstOrder(k = 1, T = 3) eqs = [connect(r.output, F.input) - connect(F.output, sys_inner.add.input1)] + connect(F.output, sys_inner.add.input1)] sys_outer = ODESystem(eqs, t, systems = [F, sys_inner, r], name = :outer) matrices, _ = get_sensitivity(sys_outer, [:inner_plant_input, :inner_plant_output]) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index 46e427806..b19924ff6 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -1,7 +1,7 @@ using ModelingToolkitStandardLibrary.Electrical, ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks: Step, - Constant, Sine, Cosine, ExpSine, Ramp, - Square, Triangular + Constant, Sine, Cosine, ExpSine, Ramp, + Square, Triangular using ModelingToolkitStandardLibrary.Blocks: square, triangular using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str @@ -23,15 +23,15 @@ D = Differential(t) @named power_sensor = PowerSensor() connections = [connect(source.output, voltage.V) - connect(voltage.p, resistor.p) - connect(resistor.n, current_sensor.p) - connect(current_sensor.n, power_sensor.pc) - connect(power_sensor.nc, capacitor.p) - connect(capacitor.n, voltage.n, ground.g) - connect(capacitor.p, voltage_sensor.p) - connect(capacitor.n, voltage_sensor.n) - connect(capacitor.p, power_sensor.pv) - connect(capacitor.n, power_sensor.nv)] + connect(voltage.p, resistor.p) + connect(resistor.n, current_sensor.p) + connect(current_sensor.n, power_sensor.pc) + connect(power_sensor.nc, capacitor.p) + connect(capacitor.n, voltage.n, ground.g) + connect(capacitor.p, voltage_sensor.p) + connect(capacitor.n, voltage_sensor.n) + connect(capacitor.p, power_sensor.pv) + connect(capacitor.n, power_sensor.nv)] @named model = ODESystem(connections, t; systems = [ @@ -42,7 +42,7 @@ D = Differential(t) ground, voltage_sensor, current_sensor, - power_sensor, + power_sensor ]) sys = structural_simplify(model) prob = ODEProblem(sys, [], (0.0, 10.0)) @@ -68,10 +68,10 @@ end @named short = Short() connections = [connect(source.output, voltage.V) - connect(voltage.p, R1.p) - connect(R1.n, short.p, R0.p) - connect(short.n, R0.n, R2.p) - connect(R2.n, voltage.n, ground.g)] + connect(voltage.p, R1.p) + connect(R1.n, short.p, R0.p) + connect(short.n, R0.n, R2.p) + connect(R2.n, voltage.n, ground.g)] @named model = ODESystem(connections, t, systems = [R0, R1, R2, source, short, voltage, ground]) @@ -95,9 +95,9 @@ end @named ground = Ground() connections = [connect(source.output, voltage.V) - connect(voltage.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, voltage.n, ground.g)] + connect(voltage.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, voltage.n, ground.g)] @named model = ODESystem(connections, t; systems = [resistor, capacitor, source, voltage, ground]) @@ -119,9 +119,9 @@ end @named ground = Ground() connections = [connect(source.output, voltage.V) - connect(voltage.p, resistor.p) - connect(resistor.n, inductor.p) - connect(inductor.n, voltage.n, ground.g)] + connect(voltage.p, resistor.p) + connect(resistor.n, inductor.p) + connect(inductor.n, voltage.n, ground.g)] @named model = ODESystem(connections, t; systems = [resistor, inductor, source, voltage, ground]) @@ -154,9 +154,9 @@ end for source in sources connections = [connect(source.output, voltage.V) - connect(voltage.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, voltage.n, ground.g)] + connect(voltage.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, voltage.n, ground.g)] @named model = ODESystem(connections, t; systems = [resistor, capacitor, source, ground, voltage]) @@ -181,9 +181,9 @@ end @named ground = Ground() connections = [connect(source.output, current.I) - connect(current.p, resistor.n) - connect(capacitor.n, resistor.p) - connect(capacitor.p, current.n, ground.g)] + connect(current.p, resistor.n) + connect(capacitor.n, resistor.p) + connect(capacitor.p, current.n, ground.g)] @named model = ODESystem(connections, t; systems = [ground, resistor, current, capacitor, source]) @@ -209,12 +209,12 @@ end @named sensor = VoltageSensor() connections = [connect(square_source.output, voltage.V) - connect(voltage.p, R1.p) - connect(R1.n, C1.n, R2.p, opamp.n1) - connect(opamp.p2, C1.p, R2.n) - connect(opamp.p1, ground.g, opamp.n2, voltage.n) - connect(opamp.p2, sensor.p) - connect(sensor.n, ground.g)] + connect(voltage.p, R1.p) + connect(R1.n, C1.n, R2.p, opamp.n1) + connect(opamp.p2, C1.p, R2.n) + connect(opamp.p1, ground.g, opamp.n2, voltage.n) + connect(opamp.p2, sensor.p) + connect(sensor.n, ground.g)] @named model = ODESystem(connections, t, systems = [ R1, @@ -224,11 +224,11 @@ end voltage, C1, ground, - sensor, + sensor ]) sys = structural_simplify(model) u0 = [C1.v => 0.0 - R1.v => 0.0] + R1.v => 0.0] prob = ODEProblem(sys, u0, (0, 100.0)) sol = solve(prob, Rodas4()) @test sol.retcode == Success @@ -270,13 +270,15 @@ _damped_sine_wave(x, f, A, st, ϕ, d) = exp((st - x) * d) * A * sin(2 * π * f * sources = [step, cosine, sine, damped_sine, ramp, tri, vsquare] #, vsawtooth] function waveforms(i, x) - getindex([o .+ _step.(x, h, st), + getindex( + [o .+ _step.(x, h, st), o .+ (x .> st) .* _cos_wave.(x, f, A, st, ϕ), o .+ (x .> st) .* _sine_wave.(x, f, A, st, ϕ), o .+ (x .> st) .* _damped_sine_wave.(x, f, A, st, ϕ, d), o .+ _ramp.(x, st, (et - st), h), triangular.(x, f, A, o, st), - square.(x, f, A, o, st)], i) + square.(x, f, A, o, st)], + i) end # o .+ (x .> st). * _sawtooth_wave.(x, δ, f, A, st), @@ -284,9 +286,9 @@ _damped_sine_wave(x, f, A, st, ϕ, d) = exp((st - x) * d) * A * sin(2 * π * f * source = sources[i] @info "Testing Voltage with $(source.name) source" eqs = [connect(source.output, voltage.V) - connect(voltage.p, voltage_sensor.p, res.p) - connect(res.n, cap.p) - connect(ground.g, voltage_sensor.n, voltage.n, cap.n)] + connect(voltage.p, voltage_sensor.p, res.p) + connect(res.n, cap.p) + connect(ground.g, voltage_sensor.n, voltage.n, cap.n)] @named vmodel = ODESystem(eqs, t, systems = [ voltage_sensor, @@ -294,7 +296,7 @@ _damped_sine_wave(x, f, A, st, ϕ, d) = exp((st - x) * d) * A * sin(2 * π * f * cap, source, voltage, - ground, + ground ]) vsys = structural_simplify(vmodel) @@ -337,13 +339,15 @@ end sources = [step, cosine, sine, damped_sine, ramp, tri, vsquare] #, idamped_sine] function waveforms(i, x) - getindex([o .+ _step.(x, h, st), + getindex( + [o .+ _step.(x, h, st), o .+ (x .> st) .* _cos_wave.(x, f, A, st, ϕ), o .+ (x .> st) .* _sine_wave.(x, f, A, st, ϕ), o .+ (x .> st) .* _damped_sine_wave.(x, f, A, st, ϕ, d), o .+ _ramp.(x, st, (et - st), h), triangular.(x, f, A, o, st), - square.(x, f, A, o, st)], i) + square.(x, f, A, o, st)], + i) end # # o .+ (x .> st). * _sawtooth_wave.(x, δ, f, A, st) @@ -351,10 +355,10 @@ end source = sources[i] @info "Testing Current with $(source.name) source" eqs = [connect(source.output, current.I) - connect(current.p, current_sensor.n) - connect(current_sensor.p, res.p) - connect(res.n, cap.p) - connect(current.n, ground.g, cap.n)] + connect(current.p, current_sensor.n) + connect(current_sensor.p, res.p) + connect(res.n, cap.p) + connect(current.n, ground.g, cap.n)] @named model = ODESystem(eqs, t, systems = [ current_sensor, @@ -362,7 +366,7 @@ end current, res, cap, - ground, + ground ]) isys = structural_simplify(model) diff --git a/test/Electrical/digital.jl b/test/Electrical/digital.jl index b4264031c..849e506da 100644 --- a/test/Electrical/digital.jl +++ b/test/Electrical/digital.jl @@ -21,7 +21,7 @@ using OrdinaryDiffEq: ReturnCode.Success @test zero(Logic) == zero(U) == F0 @test one(Logic) == one(U) == F1 @test ones(Logic, 2, 2) == [F1 F1 - F1 F1] + F1 F1] # Logic vectors u_logic = StdULogicVector([U, W, X, 1]) @@ -41,7 +41,7 @@ using OrdinaryDiffEq: ReturnCode.Success # Logic vector helpers test_logic_matrix = StdULogicVector([U F0 - F1 X]) + F1 X]) test_logic_vector = StdLogicVector([U, F0, F1, X]) size(test_logic_matrix) == (2, 2) diff --git a/test/Hydraulic/isothermal_compressible.jl b/test/Hydraulic/isothermal_compressible.jl index 2e2e0c836..e8d71a1c8 100644 --- a/test/Hydraulic/isothermal_compressible.jl +++ b/test/Hydraulic/isothermal_compressible.jl @@ -26,9 +26,9 @@ NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 100, relax = end eqs = [connect(stp.output, src.p) - connect(fluid, src.port) - connect(src.port, res.port_a) - connect(res.port_b, vol.port)] + connect(fluid, src.port) + connect(src.port, res.port_a) + connect(res.port_b, vol.port)] ODESystem(eqs, t, [], pars; name, systems) end @@ -41,7 +41,7 @@ NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 100, relax = probs = [ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.05)) for sys in syss] # sols = [solve(prob, ImplicitEuler(nlsolve = NEWTON); initializealg = NoInit(), - dt = 1e-4, adaptive = false) + dt = 1e-4, adaptive = false) for prob in probs] s1_2 = complete(sys1_2) @@ -79,9 +79,9 @@ end end eqs = [connect(fluid, sink.port) - connect(sink.port, valve.port_a) - connect(valve.port_b, vol.port) - connect(valve.area, ramp.output)] + connect(sink.port, valve.port_a) + connect(valve.port_b, vol.port) + connect(valve.area, ramp.output)] ODESystem(eqs, t, [], pars; name, systems) end @@ -127,12 +127,12 @@ end end eqs = [connect(fluid, src1.port) - connect(fluid, src2.port) - connect(src1.port, vol1.port) - connect(src2.port, vol2.port) - connect(vol1.flange, mass.flange, vol2.flange) - connect(src1.p, sin1.output) - connect(src2.p, sin2.output)] + connect(fluid, src2.port) + connect(src1.port, vol1.port) + connect(src2.port, vol2.port) + connect(vol1.flange, mass.flange, vol2.flange) + connect(src1.p, sin1.output) + connect(src2.p, sin2.output)] ODESystem(eqs, t, [], pars; name, systems) end @@ -261,17 +261,17 @@ end push!(systems, input) eqs = [connect(input.output, pos.s) - connect(valve.flange, pos.flange) - connect(valve.port_a, piston.port_a) - connect(piston.flange, body.flange) - connect(piston.port_b, m1.port_a) - connect(m1.port_b, pipe.port_b) - connect(pipe.port_a, m2.port_b) - connect(m2.port_a, valve.port_b) - connect(src.port, valve.port_s) - connect(snk.port, valve.port_r) - connect(fluid, src.port, snk.port) - D(body.v) ~ ddx] + connect(valve.flange, pos.flange) + connect(valve.port_a, piston.port_a) + connect(piston.flange, body.flange) + connect(piston.port_b, m1.port_a) + connect(m1.port_b, pipe.port_b) + connect(pipe.port_a, m2.port_b) + connect(m2.port_a, valve.port_b) + connect(src.port, valve.port_s) + connect(snk.port, valve.port_r) + connect(fluid, src.port, snk.port) + D(body.v) ~ ddx] ODESystem(eqs, t, vars, pars; name, systems) end @@ -322,7 +322,7 @@ end end eqs = [connect(fluid, cap.port, vol.port) - connect(vol.flange, mass.flange)] + connect(vol.flange, mass.flange)] ODESystem(eqs, t, [], pars; name, systems) end diff --git a/test/Magnetic/magnetic.jl b/test/Magnetic/magnetic.jl index e378c32f4..8c0d1cdf8 100644 --- a/test/Magnetic/magnetic.jl +++ b/test/Magnetic/magnetic.jl @@ -26,16 +26,16 @@ using OrdinaryDiffEq: ReturnCode.Success @named r_mFe = Magnetic.FluxTubes.ConstantReluctance(R_m = a * b * l_Fe * mu_Fe) @named r_mLeak = Magnetic.FluxTubes.ConstantReluctance(R_m = 1.2e6) connections = [connect(source.output, voltage.V) - connect(voltage.p, r.p) - connect(r.n, coil.p) - connect(voltage.n, coil.n) - connect(coil.port_p, r_mLeak.port_p) - connect(r_mLeak.port_p, r_mAirPar.port_p) - connect(r_mAirPar.port_n, r_mFe.port_p) - connect(r_mFe.port_n, r_mLeak.port_n) - connect(r_mFe.port_n, coil.port_n) - connect(ground.g, voltage.n) - connect(ground_m.port, r_mFe.port_n)] + connect(voltage.p, r.p) + connect(r.n, coil.p) + connect(voltage.n, coil.n) + connect(coil.port_p, r_mLeak.port_p) + connect(r_mLeak.port_p, r_mAirPar.port_p) + connect(r_mAirPar.port_n, r_mFe.port_p) + connect(r_mFe.port_n, r_mLeak.port_n) + connect(r_mFe.port_n, coil.port_n) + connect(ground.g, voltage.n) + connect(ground_m.port, r_mFe.port_n)] @named model = ODESystem(connections, t, systems = [ source, @@ -46,7 +46,7 @@ using OrdinaryDiffEq: ReturnCode.Success r_mAirPar, r_mFe, r_mLeak, - voltage, + voltage ]) sys = structural_simplify(model) prob = ODEProblem(sys, Pair[], (0, 0.1)) diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index f53307344..fdb396353 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -14,9 +14,9 @@ using Test # @named m2 = Mass(;m=0.5) eqs = [connect(link1.TX1, cart.flange) #, force.flange) - connect(link1.TY1, fixed.flange) - connect(link1.TX2, link2.TX1) - connect(link1.TY2, link2.TY1)] + connect(link1.TY1, fixed.flange) + connect(link1.TX2, link2.TX1) + connect(link1.TY2, link2.TY1)] @named model = ODESystem(eqs, t, [], []; systems = [link1, link2, cart, fixed]) diff --git a/test/Mechanical/rotational.jl b/test/Mechanical/rotational.jl index 47f588f5a..ef02dad2a 100644 --- a/test/Mechanical/rotational.jl +++ b/test/Mechanical/rotational.jl @@ -1,6 +1,6 @@ using ModelingToolkitStandardLibrary.Mechanical.Rotational, - ModelingToolkit, OrdinaryDiffEq, - Test + ModelingToolkit, OrdinaryDiffEq, + Test import ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq: ReturnCode.Success @@ -17,8 +17,8 @@ D = Differential(t) @named inertia2 = Inertia(J = 2, phi = pi / 2) connections = [connect(fixed.flange, inertia1.flange_b) - connect(inertia1.flange_b, spring.flange_a, damper.flange_a) - connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] + connect(inertia1.flange_b, spring.flange_a, damper.flange_a) + connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] @named model = ODESystem(connections, t, systems = [fixed, inertia1, inertia2, spring, damper]) @@ -40,8 +40,8 @@ D = Differential(t) @named springdamper = SpringDamper(; c = 1e4, d = 10) connections = [connect(fixed.flange, inertia1.flange_b) - connect(inertia1.flange_b, springdamper.flange_a) - connect(springdamper.flange_b, inertia2.flange_a)] + connect(inertia1.flange_b, springdamper.flange_a) + connect(springdamper.flange_b, inertia2.flange_a)] @named model = ODESystem(connections, t, systems = [fixed, inertia1, inertia2, springdamper]) @@ -70,10 +70,10 @@ end @named sine = Blocks.Sine(amplitude = amplitude, frequency = frequency) connections = [connect(sine.output, torque.tau) - connect(torque.support, fixed.flange) - connect(torque.flange, inertia1.flange_a) - connect(inertia1.flange_b, spring.flange_a, damper.flange_a) - connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] + connect(torque.support, fixed.flange) + connect(torque.flange, inertia1.flange_a) + connect(inertia1.flange_b, spring.flange_a, damper.flange_a) + connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] @named model = ODESystem(connections, t, systems = [ @@ -83,7 +83,7 @@ end inertia2, spring, damper, - sine, + sine ]) sys = structural_simplify(model) prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, @@ -101,9 +101,9 @@ end ## Test with constant torque source @named torque = ConstantTorque(use_support = true, tau_constant = 1) connections = [connect(torque.support, fixed.flange) - connect(torque.flange, inertia1.flange_a) - connect(inertia1.flange_b, spring.flange_a, damper.flange_a) - connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] + connect(torque.flange, inertia1.flange_a) + connect(inertia1.flange_b, spring.flange_a, damper.flange_a) + connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] @named model = ODESystem(connections, t, systems = [ @@ -112,7 +112,7 @@ end inertia1, inertia2, spring, - damper, + damper ]) sys = structural_simplify(model) @@ -142,15 +142,15 @@ end @named sine = Blocks.Sine(amplitude = amplitude, frequency = frequency) connections = [connect(inertia1.flange_b, idealGear.flange_a) - connect(idealGear.flange_b, inertia2.flange_a) - connect(inertia2.flange_b, spring.flange_a) - connect(spring.flange_b, inertia3.flange_a) - connect(damper.flange_a, inertia2.flange_b) - connect(damper.flange_b, fixed.flange) - connect(sine.output, torque.tau) - connect(torque.support, fixed.flange) - connect(idealGear.support, fixed.flange) - connect(torque.flange, inertia1.flange_a)] + connect(idealGear.flange_b, inertia2.flange_a) + connect(inertia2.flange_b, spring.flange_a) + connect(spring.flange_b, inertia3.flange_a) + connect(damper.flange_a, inertia2.flange_b) + connect(damper.flange_b, fixed.flange) + connect(sine.output, torque.tau) + connect(torque.support, fixed.flange) + connect(idealGear.support, fixed.flange) + connect(torque.flange, inertia1.flange_a)] @named model = ODESystem(connections, t, systems = [ @@ -162,7 +162,7 @@ end spring, inertia3, damper, - sine, + sine ]) @test_skip begin sys = structural_simplify(model) #key 7 not found @@ -180,8 +180,8 @@ end @named lim = Blocks.Limiter(y_max = 6) @named output = Blocks.RealOutput() connections = [connect(sine.output, dz.input) - connect(dz.output, lim.input) - connect(lim.output, output)] + connect(dz.output, lim.input) + connect(lim.output, output)] ODESystem(connections, t, [], []; name = name, systems = [sine, dz, lim, output]) end @@ -196,11 +196,11 @@ end @named angle_sensor = AngleSensor() connections = [connect(vel_profile.output, source.w_ref) - connect(source.flange, friction.flange_a) - connect(friction.flange_b, inertia.flange_a) - connect(inertia.flange_b, spring.flange_a, damper.flange_a) - connect(spring.flange_b, damper.flange_b, fixed.flange) - connect(angle_sensor.flange, inertia.flange_a)] + connect(source.flange, friction.flange_a) + connect(friction.flange_b, inertia.flange_a) + connect(inertia.flange_b, spring.flange_a, damper.flange_a) + connect(spring.flange_b, damper.flange_b, fixed.flange) + connect(angle_sensor.flange, inertia.flange_a)] @named model = ODESystem(connections, t, systems = [ @@ -211,7 +211,7 @@ end vel_profile, source, friction, - angle_sensor, + angle_sensor ]) sys = structural_simplify(model) prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 10.0)) @@ -239,16 +239,16 @@ end @named rel_speed_sensor = RelSpeedSensor() connections = [connect(fixed.flange, inertia1.flange_b, rel_speed_sensor.flange_b) - connect(inertia1.flange_b, torque_sensor.flange_a) - connect(torque_sensor.flange_b, spring.flange_a, damper.flange_a, - speed_sensor.flange, rel_speed_sensor.flange_a) - connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] + connect(inertia1.flange_b, torque_sensor.flange_a) + connect(torque_sensor.flange_b, spring.flange_a, damper.flange_a, + speed_sensor.flange, rel_speed_sensor.flange_a) + connect(spring.flange_b, damper.flange_b, inertia2.flange_a)] @named model = ODESystem(connections, t, systems = [ fixed, inertia1, inertia2, spring, damper, speed_sensor, - rel_speed_sensor, torque_sensor, + rel_speed_sensor, torque_sensor ]) sys = structural_simplify(model) diff --git a/test/Mechanical/translational.jl b/test/Mechanical/translational.jl index 58b825c49..95323d23b 100644 --- a/test/Mechanical/translational.jl +++ b/test/Mechanical/translational.jl @@ -19,7 +19,7 @@ D = Differential(t) end eqs = [connect(a.output, acc.a) - connect(mass.flange, acc.flange, free.flange)] + connect(mass.flange, acc.flange, free.flange)] ODESystem(eqs, t, [], []; name, systems) end @@ -49,7 +49,7 @@ end function simplify_and_solve(damping, spring, body, ground) eqs = [connect(spring.flange_a, body.flange, damping.flange_a) - connect(spring.flange_b, damping.flange_b, ground.flange)] + connect(spring.flange_b, damping.flange_b, ground.flange)] @named model = ODESystem(eqs, t; systems = [ground, body, spring, damping]) @@ -91,9 +91,9 @@ end function System(damping, spring, body, ground, f, source) eqs = [connect(f.f, source.output) - connect(f.flange, body.flange) - connect(spring.flange_a, body.flange, damping.flange_a) - connect(spring.flange_b, damping.flange_b, ground.flange)] + connect(f.flange, body.flange) + connect(spring.flange_a, body.flange, damping.flange_a) + connect(spring.flange_b, damping.flange_b, ground.flange)] @named model = ODESystem(eqs, t; systems = [ground, body, spring, damping, f, source]) @@ -136,11 +136,11 @@ end end eqs = [connect(pos.s, src1.output) - connect(force.f, src2.output) - connect(spring.flange_a, pos.flange, force_sensor.flange) - connect(spring.flange_b, force.flange, pos_sensor.flange) - connect(pos_value, pos_sensor.output) - connect(force_output, force_sensor.output)] + connect(force.f, src2.output) + connect(spring.flange_a, pos.flange, force_sensor.flange) + connect(spring.flange_b, force.flange, pos_sensor.flange) + connect(pos_value, pos_sensor.output) + connect(force_output, force_sensor.output)] ODESystem(eqs, t, [], []; name, systems) end diff --git a/test/Mechanical/translational_modelica.jl b/test/Mechanical/translational_modelica.jl index 8cd466890..23c21245e 100644 --- a/test/Mechanical/translational_modelica.jl +++ b/test/Mechanical/translational_modelica.jl @@ -14,7 +14,7 @@ D = Differential(t) @named fixed = TM.Fixed(s0 = 1) eqs = [connect(spring.flange_a, mass.flange_a, damper.flange_a) - connect(spring.flange_b, damper.flange_b, fixed.flange)] + connect(spring.flange_b, damper.flange_b, fixed.flange)] @named model = ODESystem(eqs, t; systems = [fixed, mass, spring, damper]) @@ -38,9 +38,9 @@ end @named source = Sine(frequency = 3, amplitude = 2, output__unit = u"N") eqs = [connect(force.f, source.output) - connect(force.flange, mass.flange_a) - connect(spring.flange_a, mass.flange_b, damper.flange_a) - connect(spring.flange_b, damper.flange_b, fixed.flange)] + connect(force.flange, mass.flange_a) + connect(spring.flange_a, mass.flange_b, damper.flange_a) + connect(spring.flange_b, damper.flange_b, fixed.flange)] @named model = ODESystem(eqs, t; systems = [fixed, mass, spring, damper, force, source]) diff --git a/test/Thermal/demo.jl b/test/Thermal/demo.jl index e27d7925b..af24f085e 100644 --- a/test/Thermal/demo.jl +++ b/test/Thermal/demo.jl @@ -17,7 +17,7 @@ D = Differential(t) connect(mass1.port, conduction.port_a), connect(conduction.port_b, mass2.port), connect(mass1.port, Tsensor1.port), - connect(mass2.port, Tsensor2.port), + connect(mass2.port, Tsensor2.port) ] @named model = ODESystem(connections, t, diff --git a/test/Thermal/thermal.jl b/test/Thermal/thermal.jl index 0e9711a65..1f840d77e 100644 --- a/test/Thermal/thermal.jl +++ b/test/Thermal/thermal.jl @@ -216,16 +216,16 @@ end @named winding_losses = Step(height = 900, offset = 100, start_time = 360, duration = Inf, smooth = false) connections = [connect(windingLosses.port, winding.port) - connect(coreLosses.port, core.port) - connect(winding.port, winding2core.port_a) - connect(winding2core.port_b, core.port) - connect(winding.port, T_winding.port) - connect(core.port, T_core.port) - connect(winding2core.port_b, convection.solid) - connect(convection.fluid, environment.port) - connect(amb.output, environment.T) - connect(winding_losses.output, windingLosses.Q_flow) - connect(core_losses_const.output, coreLosses.Q_flow)] + connect(coreLosses.port, core.port) + connect(winding.port, winding2core.port_a) + connect(winding2core.port_b, core.port) + connect(winding.port, T_winding.port) + connect(core.port, T_core.port) + connect(winding2core.port_b, convection.solid) + connect(convection.fluid, environment.port) + connect(amb.output, environment.T) + connect(winding_losses.output, windingLosses.Q_flow) + connect(core_losses_const.output, coreLosses.Q_flow)] @named model = ODESystem(connections, t, systems = [ diff --git a/test/chua_circuit.jl b/test/chua_circuit.jl index 15aa05c42..d47dc7da8 100644 --- a/test/chua_circuit.jl +++ b/test/chua_circuit.jl @@ -36,14 +36,14 @@ using DynamicQuantities: @u_str @named Gnd = Ground() connections = [connect(L.p, G.p) - connect(G.n, Nr.p) - connect(Nr.n, Gnd.g) - connect(C1.p, G.n) - connect(L.n, Ro.p) - connect(G.p, C2.p) - connect(C1.n, Gnd.g) - connect(C2.n, Gnd.g) - connect(Ro.n, Gnd.g)] + connect(G.n, Nr.p) + connect(Nr.n, Gnd.g) + connect(C1.p, G.n) + connect(L.n, Ro.p) + connect(G.p, C2.p) + connect(C1.n, Gnd.g) + connect(C2.n, Gnd.g) + connect(Ro.n, Gnd.g)] @named model = ODESystem(connections, t, systems = [L, Ro, G, C1, C2, Nr, Gnd]) sys = structural_simplify(model) diff --git a/test/multi_domain.jl b/test/multi_domain.jl index 6027b45c4..c313915bf 100644 --- a/test/multi_domain.jl +++ b/test/multi_domain.jl @@ -33,14 +33,14 @@ D = Differential(t) @named friction = Damper(d = f) connections = [connect(fixed.flange, emf.support, friction.flange_b) - connect(emf.flange, friction.flange_a, inertia.flange_a) - connect(inertia.flange_b, load.flange) - connect(load_step.output, load.tau) - connect(voltage_step.output, source.V) - connect(source.p, R1.p) - connect(R1.n, L1.p) - connect(L1.n, emf.p) - connect(emf.n, source.n, ground.g)] + connect(emf.flange, friction.flange_a, inertia.flange_a) + connect(inertia.flange_b, load.flange) + connect(load_step.output, load.tau) + connect(voltage_step.output, source.V) + connect(source.p, R1.p) + connect(R1.n, L1.p) + connect(L1.n, emf.p) + connect(emf.n, source.n, ground.g)] @named model = ODESystem(connections, t, systems = [ @@ -54,7 +54,7 @@ D = Differential(t) load, load_step, inertia, - friction, + friction ]) sys = structural_simplify(model) @@ -116,15 +116,15 @@ end @named speed_sensor = SpeedSensor() connections = [connect(fixed.flange, emf.support, friction.flange_b) - connect(emf.flange, friction.flange_a, inertia.flange_a) - connect(inertia.flange_b, load.flange) - connect(inertia.flange_b, speed_sensor.flange) - connect(load_step.output, load.tau) - connect(voltage_step.output, source.V) - connect(source.p, R1.p) - connect(R1.n, L1.p) - connect(L1.n, emf.p) - connect(emf.n, source.n, ground.g)] + connect(emf.flange, friction.flange_a, inertia.flange_a) + connect(inertia.flange_b, load.flange) + connect(inertia.flange_b, speed_sensor.flange) + connect(load_step.output, load.tau) + connect(voltage_step.output, source.V) + connect(source.p, R1.p) + connect(R1.n, L1.p) + connect(L1.n, emf.p) + connect(emf.n, source.n, ground.g)] @named model = ODESystem(connections, t, systems = [ @@ -139,7 +139,7 @@ end load_step, inertia, friction, - speed_sensor, + speed_sensor ]) sys = structural_simplify(model) @@ -189,10 +189,10 @@ end @named thermal_conductor = ThermalConductor(G = 50) @named env = FixedTemperature(T = 273.15 + 20) connections = [connect(source.n, ground.g, heating_resistor.n) - connect(source.p, heating_resistor.p) - connect(voltage_sine.output, source.V) - connect(heating_resistor.heat_port, thermal_conductor.port_a) - connect(thermal_conductor.port_b, env.port)] + connect(source.p, heating_resistor.p) + connect(voltage_sine.output, source.V) + connect(heating_resistor.heat_port, thermal_conductor.port_a) + connect(thermal_conductor.port_b, env.port)] @named model = ODESystem(connections, t, systems = [ @@ -201,7 +201,7 @@ end source, heating_resistor, thermal_conductor, - env, + env ]) sys = structural_simplify(model) From 506f1ad2a58fe592d93ff6d901b5fda4b707c41a Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 14:44:14 -0500 Subject: [PATCH 71/80] Implement `hash` for AnalysisPoint --- docs/src/tutorials/custom_component.md | 8 ++++---- src/Blocks/analysis_points.jl | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/src/tutorials/custom_component.md b/docs/src/tutorials/custom_component.md index f53f3f3fc..4472e839f 100644 --- a/docs/src/tutorials/custom_component.md +++ b/docs/src/tutorials/custom_component.md @@ -43,10 +43,10 @@ function NonlinearResistor(; name, Ga, Gb, Ve) pars = @parameters Ga=Ga Gb=Gb Ve=Ve eqs = [ i ~ ifelse(v < -Ve, - Gb * (v + Ve) - Ga * Ve, - ifelse(v > Ve, - Gb * (v - Ve) + Ga * Ve, - Ga * v)) + Gb * (v + Ve) - Ga * Ve, + ifelse(v > Ve, + Gb * (v - Ve) + Ga * Ve, + Ga * v)) ] extend(ODESystem(eqs, t, [], pars; name = name), oneport) end diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index 594031db2..cc0d08dbb 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -10,6 +10,12 @@ if Base.isdefined(ModelingToolkit, :isconnection) end Base.nameof(ap::AnalysisPoint) = ap.name +function Base.hash(ap::AnalysisPoint, seed::UInt) + h1 = hash(ap.in, seed) + h2 = hash(ap.out, h1) + h3 = hash(ap.name, h2) + h3 ⊻ (0xd29cdc51aa6562d4 % UInt) +end function ap_var(sys) if hasproperty(sys, :u) From d4ae0371b33a9bc39e66630d6f8f077829569f63 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 15:00:34 -0500 Subject: [PATCH 72/80] Add broadcast support --- src/Blocks/analysis_points.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index cc0d08dbb..99cf216a8 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -1,10 +1,11 @@ using ModelingToolkit: get_eqs, vars, @set!, get_iv -Base.@kwdef mutable struct AnalysisPoint <: Real +Base.@kwdef mutable struct AnalysisPoint in = nothing out = nothing name::Symbol = :nothing end +Base.broadcastable(x::AnalysisPoint) = Ref(x) if Base.isdefined(ModelingToolkit, :isconnection) ModelingToolkit.isconnection(::AnalysisPoint) = true end From 7502efe6e96eb5be6f041ff6d27cce6f09eb075d Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 19:20:35 -0500 Subject: [PATCH 73/80] Fix CI --- src/Blocks/continuous.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index 7b6fa47ed..be9bdf36c 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -626,10 +626,10 @@ See also [`StateSpace`](@ref) which handles MIMO systems, as well as [ControlSys if nx == 0 eqs = [y ~ d * u] else - eqs = [D(x_scaled[1]) ~ (-a[2:na]'x_scaled + a_end * u) / a[1] - D.(x_scaled[2:nx]) .~ x_scaled[1:(nx - 1)] - y ~ ((bb[2:na] - d * a[2:na])'x_scaled) / a_end + d * u - x .~ x_scaled ./ a_end] + eqs = Equation[D(x_scaled[1]) ~ (-a[2:na]'x_scaled + a_end * u) / a[1] + D.(x_scaled[2:nx]) .~ x_scaled[1:(nx - 1)] + y ~ ((bb[2:na] - d * a[2:na])'x_scaled) / a_end + d * u + x .~ x_scaled ./ a_end] end push!(eqs, input.u ~ u) push!(eqs, output.u ~ y) From d4c27ff93bcb41afaf2826c2310dc9935741abe6 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 19:27:59 -0500 Subject: [PATCH 74/80] Fix broken tests --- test/Blocks/test_analysis_points.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index 5b2ded7bb..fed439fa7 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -206,7 +206,7 @@ closed_loop = ODESystem(connections, t, systems = [model, pid, filt, sensor, r, name = :closed_loop) prob = ODEProblem(structural_simplify(closed_loop), Pair[], (0.0, 4.0)) -sol = solve(prob, Rodas4()) +sol = solve(prob, Rodas5P(), reltol = 1e-6, abstol = 1e-9) # plot( # plot(sol, vars = [filt.y, model.inertia1.phi, model.inertia2.phi]), # plot(sol, vars = [pid.ctr_output.u], title = "Control signal"), @@ -218,7 +218,7 @@ lsys = ss(matrices...) |> sminreal @test lsys.nx == 8 stepres = ControlSystemsBase.step(c2d(lsys, 0.001), 4) -@test stepres.y[:]≈sol(0:0.001:4, idxs = model.inertia2.phi) rtol=1e-4 +@test Array(stepres.y[:])≈Array(sol(0:0.001:4, idxs = model.inertia2.phi)) rtol=1e-4 # plot(stepres, plotx=true, ploty=true, size=(800, 1200), leftmargin=5Plots.mm) # plot!(sol, vars = [model.inertia2.phi], sp=1, l=:dash) From b65c467694a0fafa27d05d5650bb020d4d0b8137 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 21 Feb 2024 19:47:02 -0500 Subject: [PATCH 75/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index be766c4a7..fe3ef1cd3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ModelingToolkitStandardLibrary" uuid = "16a59e39-deab-5bd0-87e4-056b12336739" authors = ["Chris Rackauckas and Julia Computing"] -version = "2.3.4" +version = "2.3.5" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" From 9381c29458561a95a43d8d5f58360ad66e447bac Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:53:11 +0530 Subject: [PATCH 76/80] metadata(components): adds units to Magnetic/FluxTubes components --- src/Magnetic/FluxTubes/basic.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Magnetic/FluxTubes/basic.jl b/src/Magnetic/FluxTubes/basic.jl index d88c1b453..5e673b143 100644 --- a/src/Magnetic/FluxTubes/basic.jl +++ b/src/Magnetic/FluxTubes/basic.jl @@ -117,7 +117,6 @@ Initial magnetic flux flowing into the port_p can be set with `Phi` ([Wb]) v(t), [description = "Voltage difference from positive to negative pin", unit = u"V"] i(t), [description = "Current", unit = u"A"] - Phi, [description = "Magnetic flux", unit = Wb] end @extend V_m, Phi = two_port = TwoPort(; Phi) @components begin From 8fdad9429767b48d7e4f92710846557439ff7a31 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:15:44 +0530 Subject: [PATCH 77/80] metadata(components): adds units and descriptions to Rotational components --- src/Mechanical/Rotational/components.jl | 2 +- src/Mechanical/Rotational/sources.jl | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index 554ce1c6b..ed80aea50 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -94,7 +94,7 @@ Linear 1D rotational spring @symcheck c > 0 || throw(ArgumentError("Expected `c` to be positive")) end @parameters begin - c, [description = "Spring constant", unit = u"N*m"] + c, [description = "Spring constant", unit = u"N*m*rad^-1"] phi_rel0 = 0.0, [description = "Unstretched spring angle", unit = u"rad"] end @equations begin diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 5282cfa71..a0fd00c67 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -65,15 +65,21 @@ Constant torque source tau_constant, [ description = "Constant torque (if negative, torque is acting as load in positive direction of rotation)", - unit = u"N*m" + unit = u"N*m", ] end @extend flange, phi = partial_element = PartialTorque(; use_support = false) @variables begin - tau(t), [description = "Accelerating torque acting at flange (= -flange.tau)", - unit = u"N*m"] - w(t), [description = "Angular velocity of flange with respect to support", - unit = u"rad*s^-1"] + tau(t), + [ + description = "Accelerating torque acting at flange (= -flange.tau)", + unit = u"N*m", + ] + w(t), + [ + description = "Angular velocity of flange with respect to support", + unit = u"rad*s^-1", + ] end @equations begin w ~ D(phi) From 0ced53606e76bd82fbb8d21addb45e7dd66c1440 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Fri, 2 Feb 2024 16:29:11 +0530 Subject: [PATCH 78/80] refactor: use default t and D from MTKv9 --- docs/src/connectors/connections.md | 3 +-- docs/src/tutorials/custom_component.md | 3 +-- docs/src/tutorials/dc_motor_pi.md | 3 +-- docs/src/tutorials/input_component.md | 4 +--- docs/src/tutorials/thermal_model.md | 3 +-- src/Blocks/Blocks.jl | 6 +----- src/Blocks/continuous.jl | 2 +- src/Electrical/Electrical.jl | 4 +--- .../IsothermalCompressible/IsothermalCompressible.jl | 4 +--- src/Magnetic/FluxTubes/FluxTubes.jl | 4 +--- src/Mechanical/MultiBody2D/MultiBody2D.jl | 6 ++---- src/Mechanical/Rotational/Rotational.jl | 5 ++--- src/Mechanical/Translational/Translational.jl | 5 +---- .../TranslationalModelica/TranslationalModelica.jl | 6 ++---- .../TranslationalPosition/TranslationalPosition.jl | 6 ++---- src/Thermal/Thermal.jl | 6 ++---- test/Blocks/continuous.jl | 5 ++++- test/Blocks/math.jl | 4 +--- test/Blocks/nonlinear.jl | 3 +-- test/Blocks/sources.jl | 4 +--- test/Blocks/test_analysis_points.jl | 3 +-- test/Electrical/analog.jl | 1 + test/Electrical/digital.jl | 4 +--- test/Hydraulic/isothermal_compressible.jl | 6 +----- test/Magnetic/magnetic.jl | 3 +-- test/Mechanical/multibody.jl | 1 + test/Mechanical/rotational.jl | 4 +--- test/Mechanical/translational.jl | 4 +--- test/Mechanical/translational_modelica.jl | 4 +--- test/Thermal/demo.jl | 4 +--- test/Thermal/thermal.jl | 3 +-- test/chua_circuit.jl | 3 +-- test/multi_domain.jl | 4 +--- 33 files changed, 41 insertions(+), 89 deletions(-) diff --git a/docs/src/connectors/connections.md b/docs/src/connectors/connections.md index 83a43f2ca..ac4369977 100644 --- a/docs/src/connectors/connections.md +++ b/docs/src/connectors/connections.md @@ -90,10 +90,9 @@ As can be seen, this will give a 1 equation model matching our energy dissipatio ```@example connections using ModelingToolkitStandardLibrary.Electrical, ModelingToolkit, DifferentialEquations +using ModelingToolkit: t, D using Plots -@parameters t - @named resistor = Resistor(R = 1) @named capacitor = Capacitor(C = 1) @named ground = Ground() diff --git a/docs/src/tutorials/custom_component.md b/docs/src/tutorials/custom_component.md index 4472e839f..49a3bce6a 100644 --- a/docs/src/tutorials/custom_component.md +++ b/docs/src/tutorials/custom_component.md @@ -8,6 +8,7 @@ First, we need to make some imports. ```@example components using ModelingToolkit +using ModelingToolkit: t_nounits as t using ModelingToolkitStandardLibrary.Electrical using ModelingToolkitStandardLibrary.Electrical: OnePort using OrdinaryDiffEq @@ -35,8 +36,6 @@ end NonlinearResistor; this can almost be directly translated to the syntax of `ModelingToolkit`. ```@example components -@parameters t - function NonlinearResistor(; name, Ga, Gb, Ve) @named oneport = OnePort() @unpack v, i = oneport diff --git a/docs/src/tutorials/dc_motor_pi.md b/docs/src/tutorials/dc_motor_pi.md index 874dabb06..040e57a52 100644 --- a/docs/src/tutorials/dc_motor_pi.md +++ b/docs/src/tutorials/dc_motor_pi.md @@ -14,14 +14,13 @@ First, the needed packages are imported and the parameters of the model defined. ```@example dc_motor_pi using ModelingToolkit +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Electrical using ModelingToolkitStandardLibrary.Mechanical.Rotational using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq using Plots -@parameters t - R = 0.5 # [Ohm] armature resistance L = 4.5e-3 # [H] armature inductance k = 0.5 # [N.m/A] motor constant diff --git a/docs/src/tutorials/input_component.md b/docs/src/tutorials/input_component.md index 293294334..1a59a8dc3 100644 --- a/docs/src/tutorials/input_component.md +++ b/docs/src/tutorials/input_component.md @@ -14,13 +14,11 @@ The `ModelingToolkitStandardLibrary.Blocks.TimeVaryingFunction` component is eas ```julia using ModelingToolkit +using ModelingToolkit: t, D using ModelingToolkitStandardLibrary.Blocks using DataInterpolations using OrdinaryDiffEq -@parameters t -D = Differential(t) - function System(f; name) src = TimeVaryingFunction(f) diff --git a/docs/src/tutorials/thermal_model.md b/docs/src/tutorials/thermal_model.md index 233e2464d..9d104a615 100644 --- a/docs/src/tutorials/thermal_model.md +++ b/docs/src/tutorials/thermal_model.md @@ -8,8 +8,7 @@ from dividing the total initial energy in the system by the sum of the heat capa ```@example using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, Plots - -@parameters t +using ModelingToolkit: t C1 = 15 C2 = 15 diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index 23a7e431d..db34d7930 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -5,11 +5,7 @@ module Blocks using ModelingToolkit, Symbolics import IfElse: ifelse import ..@symcheck -using ModelingToolkit: getdefault -using ..DynamicQuantities: @u_str - -@parameters t [unit = u"s"] -D = Differential(t) +using ModelingToolkit: getdefault, t, D export RealInput, RealOutput, SISO include("utils.jl") diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index be9bdf36c..1f2a3763d 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -542,7 +542,7 @@ linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u ] # pars = @parameters A=A B=B C=C D=D # This is buggy eqs = [ # FIXME: if array equations work - [Differential(t)(x[i]) ~ sum(A[i, k] * x[k] for k in 1:nx) + + [D(x[i]) ~ sum(A[i, k] * x[k] for k in 1:nx) + sum(B[i, j] * (input.u[j] - u0[j]) for j in 1:nu) for i in 1:nx]..., # cannot use D here [output.u[j] ~ sum(C[j, i] * x[i] for i in 1:nx) + diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index 46e04531b..6d1394d74 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -5,6 +5,7 @@ This library contains electrical components to build up analog circuits. module Electrical using ModelingToolkit, Symbolics, IfElse +using ModelingToolkit: t, D using ..Thermal: HeatPort using ..Mechanical.Rotational: Flange, Support using ..Blocks: RealInput, RealOutput @@ -14,9 +15,6 @@ using ..DynamicQuantities: @u_str import ..rad import ..S -@parameters t [unit = u"s"] -D = Differential(t) - export Pin, OnePort include("utils.jl") diff --git a/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl b/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl index 41236e1de..c02bfb892 100644 --- a/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl +++ b/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl @@ -4,15 +4,13 @@ Library to model iso-thermal compressible liquid fluid flow module IsothermalCompressible using ModelingToolkit, Symbolics +using ModelingToolkit: t, D using ...Blocks: RealInput, RealOutput using ...Mechanical.Translational: MechanicalPort, Mass using IfElse: ifelse -@parameters t -D = Differential(t) - export HydraulicPort, HydraulicFluid include("utils.jl") diff --git a/src/Magnetic/FluxTubes/FluxTubes.jl b/src/Magnetic/FluxTubes/FluxTubes.jl index 1c9b0c48e..698c9dae9 100644 --- a/src/Magnetic/FluxTubes/FluxTubes.jl +++ b/src/Magnetic/FluxTubes/FluxTubes.jl @@ -1,12 +1,10 @@ module FluxTubes using ModelingToolkit +using ModelingToolkit: t, D using ...Electrical: Pin import ...Wb using ...DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - export PositiveMagneticPort, NegativeMagneticPort, TwoPort include("utils.jl") diff --git a/src/Mechanical/MultiBody2D/MultiBody2D.jl b/src/Mechanical/MultiBody2D/MultiBody2D.jl index 53dcf7ae0..af706cc76 100644 --- a/src/Mechanical/MultiBody2D/MultiBody2D.jl +++ b/src/Mechanical/MultiBody2D/MultiBody2D.jl @@ -1,11 +1,9 @@ module MultiBody2D using ModelingToolkit, Symbolics, IfElse -using ..Translational -using ...DynamicQuantities: @u_str +using ModelingToolkit: t, D -@parameters t [unit = u"s"] -D = Differential(t) +using ..TranslationalPosition export Link include("components.jl") diff --git a/src/Mechanical/Rotational/Rotational.jl b/src/Mechanical/Rotational/Rotational.jl index 4e0546263..de03c18ec 100644 --- a/src/Mechanical/Rotational/Rotational.jl +++ b/src/Mechanical/Rotational/Rotational.jl @@ -4,15 +4,14 @@ Library to model 1-dimensional, rotational mechanical systems module Rotational using ModelingToolkit, Symbolics, IfElse +using ModelingToolkit: t, D + using ...Blocks: RealInput, RealOutput import ...@symcheck using ...DynamicQuantities: @u_str import ...Wb import ...rad -@parameters t [unit = u"s"] -D = Differential(t) - export Flange, Support include("utils.jl") diff --git a/src/Mechanical/Translational/Translational.jl b/src/Mechanical/Translational/Translational.jl index 2560b002e..21cb17d05 100644 --- a/src/Mechanical/Translational/Translational.jl +++ b/src/Mechanical/Translational/Translational.jl @@ -4,15 +4,12 @@ Library to model 1-dimensional, translational mechanical systems module Translational using ModelingToolkit, Symbolics -using ModelingToolkit: getdefault +using ModelingToolkit: getdefault, t, D using ModelingToolkitStandardLibrary.Blocks: RealInput, RealOutput using IfElse: ifelse using ...DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - export MechanicalPort include("utils.jl") diff --git a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl index f2cf06b5a..9d3b41547 100644 --- a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl +++ b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl @@ -4,11 +4,9 @@ Library to model 1-dimensional, translational mechanical components. module TranslationalModelica using ModelingToolkit, Symbolics, IfElse -using ...Blocks: RealInput, RealOutput -using ...DynamicQuantities: @u_str +using ModelingToolkit: t, D -@parameters t [unit = u"s"] -D = Differential(t) +using ...Blocks: RealInput, RealOutput export Flange include("utils.jl") diff --git a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl index eb6a58153..b4cf0f1c1 100644 --- a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl +++ b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl @@ -4,11 +4,9 @@ Library to model 1-dimensional, translational mechanical components. module TranslationalPosition using ModelingToolkit, Symbolics, IfElse -using ...Blocks: RealInput, RealOutput -using ...DynamicQuantities: @u_str +using ModelingToolkit: t, D -@parameters t [unit = u"s"] -D = Differential(t) +using ...Blocks: RealInput, RealOutput export Flange include("utils.jl") diff --git a/src/Thermal/Thermal.jl b/src/Thermal/Thermal.jl index f798d4367..62f43dfd9 100644 --- a/src/Thermal/Thermal.jl +++ b/src/Thermal/Thermal.jl @@ -3,11 +3,9 @@ Library of thermal system components to model heat transfer. """ module Thermal using ModelingToolkit, Symbolics, IfElse -using ..Blocks: RealInput, RealOutput -using ..DynamicQuantities: @u_str +using ModelingToolkit: t, D -@parameters t [unit = u"s"] -D = Differential(t) +using ...Blocks: RealInput, RealOutput export HeatPort, Element1D include("utils.jl") diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index 11adea1f8..cdf1bc241 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -1,11 +1,15 @@ using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str using Test +<<<<<<< HEAD @parameters t [unit = u"s"] +======= +>>>>>>> 645278c (refactor: use default t and D from MTKv9) #= Testing strategy: The general strategy is to test systems using simple inputs where the solution @@ -139,7 +143,6 @@ Second order demo plant @component function Plant(; name, x = zeros(2)) @named input = RealInput() @named output = RealOutput() - D = Differential(t) sts = @variables x1(t)=x[1] x2(t)=x[2] eqs = [D(x1) ~ x2 D(x2) ~ -x1 - 0.5 * x2 + input.u diff --git a/test/Blocks/math.jl b/test/Blocks/math.jl index 185f60454..e200e4e36 100644 --- a/test/Blocks/math.jl +++ b/test/Blocks/math.jl @@ -1,12 +1,10 @@ using ModelingToolkitStandardLibrary.Blocks using ModelingToolkit, OrdinaryDiffEq, Test using ModelingToolkitStandardLibrary.Blocks: _clamp, _dead_zone -using ModelingToolkit: inputs, unbound_inputs, bound_inputs +using ModelingToolkit: inputs, unbound_inputs, bound_inputs, t using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str -@parameters t [unit = u"s"] - @testset "Gain" begin @named c = Constant(; k = 1) @named gain = Gain(; k = 1) diff --git a/test/Blocks/nonlinear.jl b/test/Blocks/nonlinear.jl index a902c4a2d..30130ea17 100644 --- a/test/Blocks/nonlinear.jl +++ b/test/Blocks/nonlinear.jl @@ -1,11 +1,10 @@ using ModelingToolkit, OrdinaryDiffEq +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Blocks: _clamp, _dead_zone using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str -@parameters t [unit = u"s"] - @testset "Limiter" begin @testset "Constant" begin @named c = Constant(; k = 1) diff --git a/test/Blocks/sources.jl b/test/Blocks/sources.jl index f2f9dc3e5..a0f5d775e 100644 --- a/test/Blocks/sources.jl +++ b/test/Blocks/sources.jl @@ -1,4 +1,5 @@ using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq +using ModelingToolkit: t, D using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damped_sin, smooth_square, smooth_step, smooth_ramp, @@ -6,9 +7,6 @@ using ModelingToolkitStandardLibrary.Blocks: smooth_sin, smooth_cos, smooth_damp using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - @testset "Constant" begin @named src = Constant(k = 2) @named int = Integrator() diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index fed439fa7..ea4b9edeb 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -2,7 +2,7 @@ using Test, LinearAlgebra using ModelingToolkit using ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq -using ModelingToolkit: get_eqs, vars, @set!, get_iv +using ModelingToolkit: get_eqs, vars, @set!, get_iv, t using ControlSystemsBase using DynamicQuantities: @u_str @@ -232,7 +232,6 @@ Si = ss(matrices...) @test tf(So) ≈ tf(Si) ## A simple multi-level system with loop openings -@parameters t [unit = u"s"] @named P_inner = FirstOrder(k = 1, T = 1) @named feedback = Feedback() @named ref = Step() diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index b19924ff6..0182e4d2b 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -1,4 +1,5 @@ using ModelingToolkitStandardLibrary.Electrical, ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Blocks: Step, Constant, Sine, Cosine, ExpSine, Ramp, Square, Triangular diff --git a/test/Electrical/digital.jl b/test/Electrical/digital.jl index 849e506da..0349c5732 100644 --- a/test/Electrical/digital.jl +++ b/test/Electrical/digital.jl @@ -4,7 +4,7 @@ using ModelingToolkitStandardLibrary.Electrical: U, X, F0, F1, Z, W, L, H, DC, U using ModelingToolkitStandardLibrary.Electrical: AndTable, OrTable, NotTable, XorTable using ModelingToolkitStandardLibrary.Electrical: get_logic_level using OrdinaryDiffEq: ReturnCode.Success - +# using ModelingToolkit: t, D # using ModelingToolkitStandardLibrary.Electrical: Set, Reset @testset "Logic, logic-vectors and helpers" begin @@ -96,8 +96,6 @@ end #= -@parameters t - @named set1 = Set() @named reset1 = Reset() @named set2 = Set() diff --git a/test/Hydraulic/isothermal_compressible.jl b/test/Hydraulic/isothermal_compressible.jl index e8d71a1c8..5e2f7c4ea 100644 --- a/test/Hydraulic/isothermal_compressible.jl +++ b/test/Hydraulic/isothermal_compressible.jl @@ -1,13 +1,11 @@ using ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D import ModelingToolkitStandardLibrary.Hydraulic.IsothermalCompressible as IC import ModelingToolkitStandardLibrary.Blocks as B import ModelingToolkitStandardLibrary.Mechanical.Translational as T using ModelingToolkitStandardLibrary.Blocks: Parameter -@parameters t -D = Differential(t) - NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 100, relax = 9 // 10) @testset "Fluid Domain and Tube" begin @@ -195,8 +193,6 @@ end @testset "Actuator System" begin function System(use_input, f; name) - @parameters t - pars = @parameters begin p_s = 200e5 p_r = 5e5 diff --git a/test/Magnetic/magnetic.jl b/test/Magnetic/magnetic.jl index 8c0d1cdf8..673237837 100644 --- a/test/Magnetic/magnetic.jl +++ b/test/Magnetic/magnetic.jl @@ -4,11 +4,10 @@ import ModelingToolkitStandardLibrary.Electrical import ModelingToolkitStandardLibrary.Blocks import ModelingToolkitStandardLibrary.Magnetic using ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using OrdinaryDiffEq: ReturnCode.Success # using Plots -@parameters t - @testset "Inductor" begin mu_air = 1 l_air = 0.0001 diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index fdb396353..1f0d629c1 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -1,4 +1,5 @@ using ModelingToolkit +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition using OrdinaryDiffEq diff --git a/test/Mechanical/rotational.jl b/test/Mechanical/rotational.jl index ef02dad2a..fad4a6cbf 100644 --- a/test/Mechanical/rotational.jl +++ b/test/Mechanical/rotational.jl @@ -1,14 +1,12 @@ using ModelingToolkitStandardLibrary.Mechanical.Rotational, ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D import ModelingToolkitStandardLibrary.Blocks using OrdinaryDiffEq: ReturnCode.Success # using Plots -@parameters t [unit = u"s"] -D = Differential(t) - @testset "two inertias" begin @named fixed = Fixed() @named inertia1 = Inertia(J = 2) # this one is fixed diff --git a/test/Mechanical/translational.jl b/test/Mechanical/translational.jl index 95323d23b..5d4757b32 100644 --- a/test/Mechanical/translational.jl +++ b/test/Mechanical/translational.jl @@ -1,4 +1,5 @@ using ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using ModelingToolkitStandardLibrary.Blocks import ModelingToolkitStandardLibrary: Mechanical @@ -6,9 +7,6 @@ import ModelingToolkitStandardLibrary.Mechanical.Translational as TV import ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition as TP using DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - @testset "Free" begin function System(; name) systems = @named begin diff --git a/test/Mechanical/translational_modelica.jl b/test/Mechanical/translational_modelica.jl index 23c21245e..e5b17e923 100644 --- a/test/Mechanical/translational_modelica.jl +++ b/test/Mechanical/translational_modelica.jl @@ -1,12 +1,10 @@ using ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using ModelingToolkitStandardLibrary.Blocks import ModelingToolkitStandardLibrary.Mechanical.TranslationalModelica as TM using DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - @testset "spring damper mass fixed" begin @named damper = TM.Damper(; d = 1) @named spring = TM.Spring(; c = 1, s_rel0 = 1) diff --git a/test/Thermal/demo.jl b/test/Thermal/demo.jl index af24f085e..a03aee0d1 100644 --- a/test/Thermal/demo.jl +++ b/test/Thermal/demo.jl @@ -1,10 +1,8 @@ using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) - # Modelica example @testset "demo" begin @named mass1 = HeatCapacitor(C = 15, T = 373.15) diff --git a/test/Thermal/thermal.jl b/test/Thermal/thermal.jl index 1f840d77e..1508a7646 100644 --- a/test/Thermal/thermal.jl +++ b/test/Thermal/thermal.jl @@ -1,10 +1,9 @@ using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using ModelingToolkitStandardLibrary.Blocks: Constant, Step using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str -@parameters t [unit = u"s"] -D = Differential(t) #= # Test HeatCapacitor, TemperatureSensor, RelativeTemperatureSensor, FixedTemperature @testset "Heat systems" begin diff --git a/test/chua_circuit.jl b/test/chua_circuit.jl index d47dc7da8..3cb8fdd5a 100644 --- a/test/chua_circuit.jl +++ b/test/chua_circuit.jl @@ -1,4 +1,5 @@ using ModelingToolkit +using ModelingToolkit: t using ModelingToolkitStandardLibrary.Electrical using ModelingToolkitStandardLibrary.Electrical: OnePort using OrdinaryDiffEq @@ -7,8 +8,6 @@ using IfElse: ifelse using DynamicQuantities: @u_str @testset "Chua Circuit" begin - @parameters t [unit = u"s"] - @mtkmodel NonlinearResistor begin @extend OnePort() @parameters begin diff --git a/test/multi_domain.jl b/test/multi_domain.jl index c313915bf..501d75d46 100644 --- a/test/multi_domain.jl +++ b/test/multi_domain.jl @@ -5,13 +5,11 @@ using ModelingToolkitStandardLibrary.Blocks using ModelingToolkitStandardLibrary.Thermal import ModelingToolkitStandardLibrary using ModelingToolkit, OrdinaryDiffEq, Test +using ModelingToolkit: t, D using OrdinaryDiffEq: ReturnCode.Success using DynamicQuantities: @u_str # using Plots -@parameters t [unit = u"s"] -D = Differential(t) - @testset "DC motor" begin R = 0.5 L = 4.5e-3 From 07c65c88114820addaad4e5ee8c4ebc35096efa5 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Wed, 14 Feb 2024 15:40:02 +0530 Subject: [PATCH 79/80] refactor: use `unknowns`, fix some tests --- src/Blocks/analysis_points.jl | 2 +- test/Blocks/test_analysis_points.jl | 4 ++-- test/Mechanical/multibody.jl | 2 +- test/Mechanical/rotational.jl | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index 99cf216a8..c5ee4b3a3 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -23,7 +23,7 @@ function ap_var(sys) # collect to turn symbolic arrays into arrays of symbols return length(sys.u) == 1 ? sys.u : collect(sys.u) end - x = states(sys) + x = unknowns(sys) length(x) == 1 && return x[1] error("Could not determine the analysis-point variable in system $(nameof(sys)). To use an analysis point, apply it to a connection between two causal blocks containing connectors of type `RealInput/RealOutput` from ModelingToolkitStandardLibrary.Blocks.") end diff --git a/test/Blocks/test_analysis_points.jl b/test/Blocks/test_analysis_points.jl index ea4b9edeb..79bfd0b62 100644 --- a/test/Blocks/test_analysis_points.jl +++ b/test/Blocks/test_analysis_points.jl @@ -21,8 +21,8 @@ sys = ODESystem(eqs, t, systems = [P, C], name = :hej) ssys = structural_simplify(sys) prob = ODEProblem(ssys, [P.x => 1], (0, 10)) sol = solve(prob, Rodas5()) -@test norm(sol[1]) >= 1 -@test norm(sol[end]) < 1e-6 # This fails without the feedback through C +@test norm(sol.u[1]) >= 1 +@test norm(sol.u[end]) < 1e-6 # This fails without the feedback through C # plot(sol) matrices, _ = get_sensitivity(sys, ap) diff --git a/test/Mechanical/multibody.jl b/test/Mechanical/multibody.jl index 1f0d629c1..968c3b03b 100644 --- a/test/Mechanical/multibody.jl +++ b/test/Mechanical/multibody.jl @@ -22,7 +22,7 @@ eqs = [connect(link1.TX1, cart.flange) #, force.flange) @named model = ODESystem(eqs, t, [], []; systems = [link1, link2, cart, fixed]) sys = structural_simplify(model) -@test length(states(sys)) == 6 +@test length(unknowns(sys)) == 6 # The below code does work... #= diff --git a/test/Mechanical/rotational.jl b/test/Mechanical/rotational.jl index fad4a6cbf..5bf33a902 100644 --- a/test/Mechanical/rotational.jl +++ b/test/Mechanical/rotational.jl @@ -30,7 +30,7 @@ using OrdinaryDiffEq: ReturnCode.Success sol = solve(prob, Rodas4()) @test SciMLBase.successful_retcode(sol) - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 10.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, Pair[], (0, 10.0)) sol = solve(prob, DFBDF()) @test SciMLBase.successful_retcode(sol) @test all(sol[inertia1.w] .== 0) @@ -84,8 +84,8 @@ end sine ]) sys = structural_simplify(model) - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, - [D(D(inertia2.phi)) => 1.0; D.(states(model)) .=> 0.0], (0, 10.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, + [D(D(inertia2.phi)) => 1.0; D.(unknowns(model)) .=> 0.0], (0, 10.0)) sol = solve(prob, DFBDF()) @test SciMLBase.successful_retcode(sol) @@ -212,7 +212,7 @@ end angle_sensor ]) sys = structural_simplify(model) - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 10.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, Pair[], (0, 10.0)) sol = solve(prob, DFBDF()) @test SciMLBase.successful_retcode(sol) @@ -259,7 +259,7 @@ end @test all(sol[rel_speed_sensor.w_rel.u] .== sol[speed_sensor.w.u]) @test all(sol[torque_sensor.tau.u] .== -sol[inertia1.flange_b.tau]) - prob = DAEProblem(sys, D.(states(sys)) .=> 0.0, Pair[], (0, 10.0)) + prob = DAEProblem(sys, D.(unknowns(sys)) .=> 0.0, Pair[], (0, 10.0)) sol = solve(prob, DFBDF()) @test SciMLBase.successful_retcode(sol) @test all(sol[inertia1.w] .== 0) From 51b9bf5376be0c5ebe76cdb11d197419cfadab44 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 22 Feb 2024 11:15:20 +0530 Subject: [PATCH 80/80] refactor: add DynamicQuantities --- Project.toml | 1 + src/Blocks/Blocks.jl | 1 + src/Electrical/Electrical.jl | 1 + src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl | 2 +- src/Mechanical/MultiBody2D/MultiBody2D.jl | 1 + src/Mechanical/Rotational/Rotational.jl | 1 + src/Mechanical/Translational/Translational.jl | 1 + src/Mechanical/TranslationalModelica/TranslationalModelica.jl | 1 + src/Mechanical/TranslationalPosition/TranslationalPosition.jl | 1 + src/Thermal/Thermal.jl | 1 + 10 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index fe3ef1cd3..c4e52f67b 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "2.3.5" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" +DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index db34d7930..5503b0b5c 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -5,6 +5,7 @@ module Blocks using ModelingToolkit, Symbolics import IfElse: ifelse import ..@symcheck +using DynamicQuantities using ModelingToolkit: getdefault, t, D export RealInput, RealOutput, SISO diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index 6d1394d74..847ef2470 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -6,6 +6,7 @@ module Electrical using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ..Thermal: HeatPort using ..Mechanical.Rotational: Flange, Support using ..Blocks: RealInput, RealOutput diff --git a/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl b/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl index c02bfb892..b58647d84 100644 --- a/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl +++ b/src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl @@ -4,7 +4,7 @@ Library to model iso-thermal compressible liquid fluid flow module IsothermalCompressible using ModelingToolkit, Symbolics -using ModelingToolkit: t, D +using ModelingToolkit: t_nounits as t, D_nounits as D using ...Blocks: RealInput, RealOutput using ...Mechanical.Translational: MechanicalPort, Mass diff --git a/src/Mechanical/MultiBody2D/MultiBody2D.jl b/src/Mechanical/MultiBody2D/MultiBody2D.jl index af706cc76..9a0c048b8 100644 --- a/src/Mechanical/MultiBody2D/MultiBody2D.jl +++ b/src/Mechanical/MultiBody2D/MultiBody2D.jl @@ -2,6 +2,7 @@ module MultiBody2D using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ..TranslationalPosition diff --git a/src/Mechanical/Rotational/Rotational.jl b/src/Mechanical/Rotational/Rotational.jl index de03c18ec..0c172a9fb 100644 --- a/src/Mechanical/Rotational/Rotational.jl +++ b/src/Mechanical/Rotational/Rotational.jl @@ -5,6 +5,7 @@ module Rotational using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ...Blocks: RealInput, RealOutput import ...@symcheck diff --git a/src/Mechanical/Translational/Translational.jl b/src/Mechanical/Translational/Translational.jl index 21cb17d05..b8aa7dbab 100644 --- a/src/Mechanical/Translational/Translational.jl +++ b/src/Mechanical/Translational/Translational.jl @@ -5,6 +5,7 @@ module Translational using ModelingToolkit, Symbolics using ModelingToolkit: getdefault, t, D +using DynamicQuantities using ModelingToolkitStandardLibrary.Blocks: RealInput, RealOutput using IfElse: ifelse diff --git a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl index 9d3b41547..041338d7c 100644 --- a/src/Mechanical/TranslationalModelica/TranslationalModelica.jl +++ b/src/Mechanical/TranslationalModelica/TranslationalModelica.jl @@ -5,6 +5,7 @@ module TranslationalModelica using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ...Blocks: RealInput, RealOutput diff --git a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl index b4cf0f1c1..bc20d29eb 100644 --- a/src/Mechanical/TranslationalPosition/TranslationalPosition.jl +++ b/src/Mechanical/TranslationalPosition/TranslationalPosition.jl @@ -5,6 +5,7 @@ module TranslationalPosition using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ...Blocks: RealInput, RealOutput diff --git a/src/Thermal/Thermal.jl b/src/Thermal/Thermal.jl index 62f43dfd9..7f7048444 100644 --- a/src/Thermal/Thermal.jl +++ b/src/Thermal/Thermal.jl @@ -4,6 +4,7 @@ Library of thermal system components to model heat transfer. module Thermal using ModelingToolkit, Symbolics, IfElse using ModelingToolkit: t, D +using DynamicQuantities using ...Blocks: RealInput, RealOutput