-
-
Notifications
You must be signed in to change notification settings - Fork 45
Input component and Hydraulic changes #163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
ddcb4f9
Actuator system
9a6e92c
added tests
59db855
fixed system test
e0ca1c7
fixed FlowDivider
53eab0b
update
01b2e13
added time comparison using TimeVaryingFunction
daf8d06
added Fast Solve
a24b11d
added isequal
705b440
fixed speed with Parameter type stability
07ee921
publish update
2606160
fixed docs
dc76969
fixed tests
57058b6
fixed Mechanical test and Force component
a48af53
IfElse fix
8ae4427
fixed IfElse issue
7ca521c
format
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Running Models with Discrete Data | ||
|
||
There are 2 ways to include data as part of a model. | ||
|
||
1. using `ModelingToolkitStandardLibrary.Blocks.TimeVaryingFunction` | ||
2. using a custom component with external data or sensor | ||
3. using `ModelingToolkitStandardLibrary.Blocks.Input` | ||
|
||
This tutorial demonstrate each case and explain the pros and cons of each. | ||
|
||
## TimeVaryingFunction Component | ||
|
||
This component is easy to use and is performative. However the data is locked to the `ODESystem` and can only be changed by building a new `ODESystem`. Therefore, running a batch of data would not be efficient. Below is an example of how to use the `TimeVaryingFunction` with `DataInterpolations` to build the function from discrete data. | ||
|
||
```julia | ||
using ModelingToolkit | ||
using ModelingToolkitStandardLibrary.Blocks | ||
using DataInterpolations | ||
using OrdinaryDiffEq | ||
|
||
@parameters t | ||
D = Differential(t) | ||
|
||
function System(f; name) | ||
src = TimeVaryingFunction(f) | ||
|
||
vars = @variables f(t)=0 x(t)=0 dx(t)=0 ddx(t)=0 | ||
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] | ||
|
||
ODESystem(eqs, t, vars, pars; systems = [src], name) | ||
end | ||
|
||
dt = 4e-4 | ||
time = 0:dt:0.1 | ||
data = sin.(2 * pi * time * 100) # example data | ||
|
||
f = LinearInterpolation(data, time) | ||
|
||
@named system = System(f) | ||
sys = structural_simplify(system) | ||
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 this. | ||
|
||
## Custom Component with External Data | ||
|
||
The below code shows how to include data using a `Ref` and registered `input` function. This example uses a very basic function which requires non-adaptive solving and sampled data. As can be seen, the data can easily be set and changed before solving. | ||
|
||
```julia | ||
const rdata = Ref{Vector{Float64}}() | ||
|
||
# Data Sets | ||
data1 = sin.(2 * pi * time * 100) | ||
data2 = cos.(2 * pi * time * 50) | ||
|
||
function input(t) | ||
i = floor(Int, t / dt) + 1 | ||
x = rdata[][i] | ||
|
||
return x | ||
end | ||
|
||
Symbolics.@register_symbolic input(t) | ||
|
||
function System(; name) | ||
vars = @variables f(t)=0 x(t)=0 dx(t)=0 ddx(t)=0 | ||
pars = @parameters m=10 k=1000 d=1 | ||
|
||
eqs = [f ~ input(t) | ||
ddx * 10 ~ k * x + d * dx + f | ||
D(x) ~ dx | ||
D(dx) ~ ddx] | ||
|
||
ODESystem(eqs, t, vars, pars; name) | ||
end | ||
|
||
@named system = System() | ||
sys = structural_simplify(system) | ||
prob = ODEProblem(sys, [], (0, time[end])) | ||
|
||
rdata[] = data1 | ||
sol1 = solve(prob, ImplicitEuler(); dt, adaptive = false); | ||
ddx1 = sol1[sys.ddx] | ||
|
||
rdata[] = data2 | ||
sol2 = solve(prob, ImplicitEuler(); dt, adaptive = false) | ||
ddx2 = sol2[sys.ddx] | ||
``` | ||
|
||
The drawback of this method is that the solution observables can be linked to the data `Ref`, which means that if the data changes then the observables are no longer valid. In this case `ddx` is an observable that is derived directly from the data. Therefore, `sol1[sys.ddx]` is no longer correct after the data is changed for `sol2`. Additional code could be added to resolve this issue, for example by using a `Ref{Dict}` that could link a parameter of the model to the data source. This would also be necessary for parallel processing. | ||
|
||
```julia | ||
# the following test will fail | ||
@test all(ddx1 .== sol1[sys.ddx]) #returns false | ||
``` | ||
|
||
## Input Component | ||
|
||
To resolve the issues presented above, the `Input` 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 solving. | ||
|
||
```julia | ||
function System(; name) | ||
src = Input(Float64) | ||
|
||
vars = @variables f(t)=0 x(t)=0 dx(t)=0 ddx(t)=0 | ||
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] | ||
|
||
ODESystem(eqs, t, vars, pars; systems = [src], name) | ||
end | ||
|
||
@named system = System() | ||
sys = structural_simplify(system) | ||
s = complete(system) | ||
prob = ODEProblem(sys, [], (0, time[end]); tofloat = false) | ||
defs = ModelingToolkit.defaults(sys) | ||
|
||
function get_prob(data) | ||
defs[s.src.buffer] = Parameter(data, dt) | ||
# ensure p is a uniform type of Vector{Parameter{Float64}} (converting from Vector{Any}) | ||
p = Parameter.(ModelingToolkit.varmap_to_vars(defs, parameters(sys); tofloat = false)) | ||
remake(prob; p) | ||
end | ||
|
||
prob1 = get_prob(data1) | ||
prob2 = get_prob(data2) | ||
|
||
sol1 = Ref{ODESolution}() | ||
sol2 = Ref{ODESolution}() | ||
@sync begin | ||
@async sol1[] = solve(prob1, ImplicitEuler()) | ||
@async sol2[] = solve(prob2, ImplicitEuler()) | ||
end | ||
``` | ||
|
||
Note, in the above example, we can build the system with an empty `Input` component, only setting the expected data type: `@named src = Input(Float64)`. It's also possible to initialize the component with real data: `@named src = Input(data, dt)`. Additionally note that before running an `ODEProblem` using the `Input` component that the parameter vector should be a uniform type so that Julia is not slowed down by type instability. Because the `Input` component contains the `buffer` parameter of type `Parameter`, 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 all `Parameter` type by running `p = Parameter.(p)`. This will wrap all the single values in a `Parameter` type which will be mathmatically equivalent to a `Number`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.