|
1 | 1 | using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq
|
2 | 2 | using ModelingToolkitStandardLibrary.Blocks
|
3 | 3 | using OrdinaryDiffEq: ReturnCode.Success
|
| 4 | +using Test |
4 | 5 |
|
5 | 6 | @parameters t
|
6 | 7 |
|
|
408 | 409 | @test sol[plant.output.u][end]≈re_val atol=1e-3 # zero control error after 100s
|
409 | 410 | @test all(-1.5 .<= sol[pid_controller.ctr_output.u] .<= 1.5) # test limit
|
410 | 411 | end
|
| 412 | + |
| 413 | + @testset "TransferFunction" begin |
| 414 | + pt1_func(t, k, T) = k * (1 - exp(-t / T)) # Known solution to first-order system |
| 415 | + |
| 416 | + @named c = Constant(; k = 1) |
| 417 | + @named pt1 = TransferFunction(b = [1.2], a = [3.14, 1]) |
| 418 | + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) |
| 419 | + sys = structural_simplify(iosys) |
| 420 | + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) |
| 421 | + sol = solve(prob, Rodas4()) |
| 422 | + @test sol.retcode == Success |
| 423 | + @test sol[pt1.output.u]≈pt1_func.(sol.t, 1.2, 3.14) atol=1e-3 |
| 424 | + |
| 425 | + # Test logic for a_end by constructing an integrator |
| 426 | + @named c = Constant(; k = 1) |
| 427 | + @named pt1 = TransferFunction(b = [1.2], a = [3.14, 0]) |
| 428 | + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) |
| 429 | + sys = structural_simplify(iosys) |
| 430 | + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) |
| 431 | + sol = solve(prob, Rodas4()) |
| 432 | + @test sol.retcode == Success |
| 433 | + @test sol[pt1.output.u] ≈ sol.t .* (1.2 / 3.14) |
| 434 | + @test sol[pt1.x[1]] ≈ sol.t .* (1 / 3.14) # Test that scaling of state works properly |
| 435 | + |
| 436 | + # Test higher order |
| 437 | + |
| 438 | + function pt2_func(t, k, w, d) |
| 439 | + y = if d == 0 |
| 440 | + -k * (-1 + cos(t * w)) |
| 441 | + else |
| 442 | + d = complex(d) |
| 443 | + real(k * (1 + |
| 444 | + (-cosh(sqrt(-1 + d^2) * t * w) - |
| 445 | + (d * sinh(sqrt(-1 + d^2) * t * w)) / sqrt(-1 + d^2)) / |
| 446 | + exp(d * t * w))) |
| 447 | + end |
| 448 | + end |
| 449 | + |
| 450 | + k, w, d = 1.0, 1.0, 0.5 |
| 451 | + @named pt1 = TransferFunction(b = [w^2], a = [1, 2d * w, w^2]) |
| 452 | + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) |
| 453 | + sys = structural_simplify(iosys) |
| 454 | + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) |
| 455 | + sol = solve(prob, Rodas4()) |
| 456 | + @test sol.retcode == Success |
| 457 | + @test sol[pt1.output.u]≈pt2_func.(sol.t, k, w, d) atol=1e-3 |
| 458 | + |
| 459 | + # test zeros (high-pass version of first test) |
| 460 | + @named c = Constant(; k = 1) |
| 461 | + @named pt1 = TransferFunction(b = [1, 0], a = [1, 1]) |
| 462 | + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) |
| 463 | + sys = structural_simplify(iosys) |
| 464 | + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) |
| 465 | + sol = solve(prob, Rodas4()) |
| 466 | + @test sol.retcode == Success |
| 467 | + @test sol[pt1.output.u]≈1 .- pt1_func.(sol.t, 1, 1) atol=1e-3 |
| 468 | + @test sol[pt1.x[1]]≈pt1_func.(sol.t, 1, 1) atol=1e-3 # Test that scaling of state works properly |
| 469 | + |
| 470 | + # Test with no state |
| 471 | + @named pt1 = TransferFunction(b = [2.7], a = [pi]) |
| 472 | + @named iosys = ODESystem(connect(c.output, pt1.input), t, systems = [pt1, c]) |
| 473 | + sys = structural_simplify(iosys) |
| 474 | + prob = ODEProblem(sys, Pair[pt1.a_end => 1], (0.0, 100.0)) |
| 475 | + sol = solve(prob, Rodas4()) |
| 476 | + @test sol.retcode == Success |
| 477 | + @test all(==(2.7 / pi), sol[pt1.output.u]) |
| 478 | + end |
411 | 479 | end
|
0 commit comments