diff --git a/docs/src/API/blocks.md b/docs/src/API/blocks.md index ac6cff905..ebeb90c19 100644 --- a/docs/src/API/blocks.md +++ b/docs/src/API/blocks.md @@ -36,6 +36,11 @@ Add Add3 Product Division +UnaryMinus +Power +Modulo +Floor +Ceil StaticNonLinearity Abs Sign diff --git a/src/Blocks/Blocks.jl b/src/Blocks/Blocks.jl index 447da8aea..b4b18948c 100644 --- a/src/Blocks/Blocks.jl +++ b/src/Blocks/Blocks.jl @@ -10,7 +10,7 @@ using ModelingToolkit: getdefault, t_nounits as t, D_nounits as D export RealInput, RealInputArray, RealOutput, RealOutputArray, SISO include("utils.jl") -export Gain, Sum, MatrixGain, Feedback, Add, Add3, Product, Division +export Gain, Sum, MatrixGain, Feedback, Add, Add3, Product, Division, Power, Modulo, UnaryMinus, Floor, Ceil export Abs, Sign, Sqrt, Sin, Cos, Tan, Asin, Acos, Atan, Atan2, Sinh, Cosh, Tanh, Exp export Log, Log10 include("math.jl") diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index 7d453cd50..45e0c8203 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -208,6 +208,112 @@ Output first input divided by second input. end end +""" + Power(; name) + +Output the exponential with base as the first input and exponent as second input i.e u1^u2 + +# Connectors: + + - `input1` + - `input2` + - `output` +""" +@mtkmodel Power begin + @components begin + input1 = RealInput() + input2 = RealInput() # denominator can not be zero + output = RealOutput() + end + @equations begin + output.u ~ input1.u ^ input2.u + end +end + +""" + Modulo(; name) + +Output the remainder when the first input is divided by second input. + +# Connectors: + + - `input1` + - `input2` + - `output` +""" +@mtkmodel Modulo begin + @components begin + input1 = RealInput() + input2 = RealInput(guess = 1.0) # denominator can not be zero + output = RealOutput() + end + @equations begin + output.u ~ mod(input1.u, input2.u) + end +end + +""" + UnaryMinus(; name) + +Output the product of -1 and the input. + +# Connectors: + + - `input` + - `output` +""" +@mtkmodel UnaryMinus begin + @components begin + input = RealInput() + output = RealOutput() + end + @equations begin + output.u ~ -(input.u) + end +end + +## Rounding functions add after the symbolic functions are registered +""" + Floor(; name) + +Output the floor rounding of the input. + +# Connectors: + + - `input` + - `output` +""" +@mtkmodel Floor begin + @components begin + input = RealInput() + output = RealOutput() + end + @equations begin + output.u ~ floor(input.u) + end +end + +""" + Ceil(; name) + +Output the ceiling rounding of the input. + +# Connectors: + + - `input` + - `output` +""" +@mtkmodel Ceil begin + @components begin + input = RealInput() + output = RealOutput() + end + @equations begin + output.u ~ ceil(input.u) + end +end + + """ StaticNonLinearity(func; name) diff --git a/test/Blocks/math.jl b/test/Blocks/math.jl index c29bba069..5812ccf3d 100644 --- a/test/Blocks/math.jl +++ b/test/Blocks/math.jl @@ -157,6 +157,103 @@ end @test sol[prod.output.u] ≈ 2 * sin.(2 * pi * sol.t) end +@testset "Power" begin + @named c1 = Sine(; frequency = 1) + @named c2 = Constant(; k = 2) + @named pow = Power(;) + @named int = Integrator(; k = 1) + @named model = ODESystem( + [ + connect(c1.output, pow.input1), + connect(c2.output, pow.input2), + connect(pow.output, int.input) + ], + t, + systems = [int, pow, c1, c2]) + sys = structural_simplify(model) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 1.0)) + sol = solve(prob, Rodas4()) + @test isequal(unbound_inputs(sys), []) + @test sol.retcode == Success + @test sol[pow.output.u] ≈ sin.(2 * pi * sol.t) .^ 2 +end + +@testset "Modulo" begin + @named c1 = Ramp(height = 2, duration = 1, offset = 1, start_time = 0, smooth = false) + @named c2 = Constant(; k = 1) + @named modl = Modulo(;) + @named model = ODESystem( + [ + connect(c1.output, modl.input1), + connect(c2.output, modl.input2) + ], + t, + systems = [modl, c1, c2]) + sys = structural_simplify(model) + prob = ODEProblem(sys, [], (0.0, 1.0)) + sol = solve(prob, Rodas4()) + @test isequal(unbound_inputs(sys), []) + @test sol.retcode == Success + @test sol[modl.output.u] ≈ mod.(2 * sol.t,1) +end + +@testset "UnaryMinus" begin + @named c1 = Sine(; frequency = 1) + @named minu = UnaryMinus(;) + @named int = Integrator(; k = 1) + @named model = ODESystem( + [ + connect(c1.output, minu.input), + connect(minu.output, int.input) + ], + t, + systems = [int, minu, c1]) + sys = structural_simplify(model) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 1.0)) + sol = solve(prob, Rodas4()) + @test isequal(unbound_inputs(sys), []) + @test sol.retcode == Success + @test sol[minu.output.u] ≈ - sin.(2 * pi * sol.t) +end + +@testset "Floor" begin + @named c1 = Sine(; frequency = 1) + @named flr = Floor(;) + @named int = Integrator(; k = 1) + @named model = ODESystem( + [ + connect(c1.output, flr.input), + connect(flr.output, int.input) + ], + t, + systems = [int, flr, c1]) + sys = structural_simplify(model) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 1.0)) + sol = solve(prob, Rodas4()) + @test isequal(unbound_inputs(sys), []) + @test sol.retcode == Success + @test sol[flr.output.u] ≈ floor.(sin.(2 * pi * sol.t)) +end + +@testset "Ceil" begin + @named c1 = Sine(; frequency = 1) + @named cel = Ceil(;) + @named int = Integrator(; k = 1) + @named model = ODESystem( + [ + connect(c1.output, cel.input), + connect(cel.output, int.input) + ], + t, + systems = [int, cel, c1]) + sys = structural_simplify(model) + prob = ODEProblem(sys, Pair[int.x => 0.0], (0.0, 1.0)) + sol = solve(prob, Rodas4()) + @test isequal(unbound_inputs(sys), []) + @test sol.retcode == Success + @test sol[cel.output.u] ≈ ceil.(sin.(2 * pi * sol.t)) +end + @testset "Division" begin @named c1 = Sine(; frequency = 1) @named c2 = Constant(; k = 2)