diff --git a/src/systems/unit_check.jl b/src/systems/unit_check.jl index acf7451065..1a6e00c33f 100644 --- a/src/systems/unit_check.jl +++ b/src/systems/unit_check.jl @@ -213,6 +213,17 @@ function _validate(terms::Vector, labels::Vector{String}; info::String = "") valid end +function _validate(ap::AnalysisPoint; info::String = "") + is_valid = false + if (ap.outputs == nothing) + is_valid = true + else + conn_eq = connect(ap.input, ap.outputs...) + is_valid = _validate(conn_eq.rhs, info=info) + end + return is_valid +end + function _validate(conn::Connection; info::String = "") valid = true syss = get_systems(conn) @@ -277,7 +288,7 @@ function validate(jumps::Vector{JumpType}, t::Symbolic) end function validate(eq::Union{Inequality, Equation}; info::String = "") - if typeof(eq.lhs) == Connection + if typeof(eq.lhs) <: Union{Connection, AnalysisPoint} _validate(eq.rhs; info) else _validate([eq.lhs, eq.rhs], ["left", "right"]; info) diff --git a/src/systems/validation.jl b/src/systems/validation.jl index d416a02ea2..6372be1100 100644 --- a/src/systems/validation.jl +++ b/src/systems/validation.jl @@ -2,7 +2,7 @@ module UnitfulUnitCheck using ..ModelingToolkit, Symbolics, SciMLBase, Unitful, RecursiveArrayTools using ..ModelingToolkit: ValidationError, - ModelingToolkit, Connection, instream, JumpType, VariableUnit, + ModelingToolkit, Connection, instream, JumpType, VariableUnit, AnalysisPoint, get_systems, Conditional, Comparison using JumpProcesses: MassActionJump, ConstantRateJump, VariableRateJump @@ -182,6 +182,17 @@ function _validate(terms::Vector, labels::Vector{String}; info::String = "") valid end +function _validate(ap::AnalysisPoint; info::String = "") + is_valid = false + if (ap.outputs == nothing) + is_valid = true + else + conn_eq = connect(ap.input, ap.outputs...) + is_valid = _validate(conn_eq.rhs, info=info) + end + return is_valid +end + function _validate(conn::Connection; info::String = "") valid = true syss = get_systems(conn) @@ -242,7 +253,7 @@ function validate(jumps::Vector{JumpType}, t::Symbolic) end function validate(eq::MT.Equation; info::String = "") - if typeof(eq.lhs) == Connection + if typeof(eq.lhs) <: Union{AnalysisPoint, Connection} _validate(eq.rhs; info) else _validate([eq.lhs, eq.rhs], ["left", "right"]; info) diff --git a/test/analysis_points.jl b/test/analysis_points.jl index f718e7bc59..77ad59d261 100644 --- a/test/analysis_points.jl +++ b/test/analysis_points.jl @@ -1,16 +1,100 @@ -using ModelingToolkit, ModelingToolkitStandardLibrary.Blocks, ControlSystemsBase -using ModelingToolkitStandardLibrary.Mechanical.Rotational -using ModelingToolkitStandardLibrary.Blocks -using OrdinaryDiffEq, LinearAlgebra -using Test +using ModelingToolkit using ModelingToolkit: t_nounits as t, D_nounits as D, AnalysisPoint, AbstractSystem import ModelingToolkit as MTK +using ModelingToolkitStandardLibrary +using ModelingToolkitStandardLibrary.Blocks +using ModelingToolkitStandardLibrary.Mechanical.Rotational +using ControlSystemsBase import ControlSystemsBase as CS +using OrdinaryDiffEq, LinearAlgebra +using Test using Symbolics: NAMESPACE_SEPARATOR +using Unitful + +@testset "AnalysisPoint is ignored when verifying units" begin + # no units first + @mtkmodel FirstOrderTest begin + @components begin + in = Step() + fb = Feedback() + fo = SecondOrder(k = 1, w = 1, d = 0.1) + end + @equations begin + connect(in.output, :u, fb.input1) + connect(fb.output, :e, fo.input) + connect(fo.output, :y, fb.input2) + end + end + @named model = FirstOrderTest() + @test model isa System + + @connector function UnitfulOutput(; name) + vars = @variables begin + u(t), [unit=u"m", output=true] + end + return System(Equation[], t, vars, []; name) + end + @connector function UnitfulInput(; name) + vars = @variables begin + u(t), [unit=u"m", input=true] + end + return System(Equation[], t, vars, []; name) + end + @component function UnitfulBlock(; name) + pars = @parameters begin + offset, [unit=u"m"] + start_time + height, [unit=u"m"] + duration + end + systems = @named begin + output = UnitfulOutput() + end + eqs = [ + output.u ~ offset + height*(0.5 + (1/pi)*atan(1e5*(t - start_time))) + ] + return System(eqs, t, [], pars; systems, name) + end + @mtkmodel TestAPAroundUnits begin + @components begin + input = UnitfulInput() + end + @variables begin + output(t), [output=true, unit=u"m^2"] + end + @components begin + ub = UnitfulBlock() + end + @equations begin + connect(ub.output, :ap, input) + output ~ input.u^2 + end + end + @named sys = TestAPAroundUnits() + @test sys isa System + + @mtkmodel TestAPWithNoOutputs begin + @components begin + input = UnitfulInput() + end + @variables begin + output(t), [output=true, unit=u"m^2"] + end + @components begin + ub = UnitfulBlock() + end + @equations begin + connect(ub.output, :ap, input) + output ~ input.u^2 + end + end + @named sys2 = TestAPWithNoOutputs() + @test sys2 isa System +end @testset "AnalysisPoint is lowered to `connect`" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = -1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = -1) ap = AnalysisPoint(:plant_input) eqs = [connect(P.output, C.input) @@ -30,7 +114,7 @@ end @testset "Inverse causality throws a warning" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = -1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = -1) ap = AnalysisPoint(:plant_input) @test_warn ["1-th argument", "plant_input", "not a output"] connect( @@ -41,7 +125,7 @@ end # also tests `connect(input, name::Symbol, outputs...)` syntax @testset "AnalysisPoint is accessible via `getproperty`" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = -1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = -1) eqs = [connect(P.output, C.input), connect(C.output, :plant_input, P.input)] sys_ap = System(eqs, t, systems = [P, C], name = :hej) @@ -61,7 +145,7 @@ end ### Ported from MTKStdlib @named P = FirstOrder(k = 1, T = 1) -@named C = Gain(; k = -1) +@named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = -1) ap = AnalysisPoint(:plant_input) eqs = [connect(P.output, C.input), connect(C.output, ap, P.input)] @@ -266,7 +350,7 @@ end @testset "Duplicate `connect` statements across subsystems with AP transforms - standard `connect`" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = 1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = 1) @named add = Blocks.Add(k2 = -1) eqs = [connect(P.output, :plant_output, add.input2) @@ -302,7 +386,7 @@ end @testset "Duplicate `connect` statements across subsystems with AP transforms - causal variable `connect`" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = 1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = 1) @named add = Blocks.Add(k2 = -1) eqs = [connect(P.output.u, :plant_output, add.input2.u) @@ -338,7 +422,7 @@ end @testset "Duplicate `connect` statements across subsystems with AP transforms - mixed `connect`" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = 1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = 1) @named add = Blocks.Add(k2 = -1) eqs = [connect(P.output.u, :plant_output, add.input2.u) @@ -460,7 +544,7 @@ end @testset "multiple analysis points" begin @named P = FirstOrder(k = 1, T = 1) - @named C = Gain(; k = 1) + @named C = ModelingToolkitStandardLibrary.Blocks.Gain(; k = 1) @named add = Blocks.Add(k2 = -1) eqs = [connect(P.output, :plant_output, add.input2)