@@ -123,6 +123,9 @@ function SymbolicIndexingInterface.observed(sys::ExampleSystem, sym::Expr)
123123end
124124```
125125
126+ In case a type does not support such observed quantities, ` is_observed ` must be
127+ defined to always return ` false ` , and ` observed ` does not need to be implemented.
128+
126129### Note about constant structure
127130
128131Note that the method definitions are all assuming ` constant_structure(p) == true ` .
@@ -174,35 +177,91 @@ mutable struct ExampleIntegrator
174177 u:: Vector{Float64}
175178 p:: Vector{Float64}
176179 t:: Float64
177- state_index:: Dict{Symbol,Int}
178- parameter_index:: Dict{Symbol,Int}
179- independent_variable:: Symbol
180+ sys:: ExampleSystem
180181end
181- ```
182182
183- Assume that it implements the mandatory part of the interface as described above, and
184- the following methods below:
185-
186- ``` julia
183+ # define a fallback for the interface methods
184+ SymbolicIndexingInterface. symbolic_container (integ:: ExampleIntegrator ) = integ. sys
187185SymbolicIndexingInterface. state_values (sys:: ExampleIntegrator ) = sys. u
188186SymbolicIndexingInterface. parameter_values (sys:: ExampleIntegrator ) = sys. p
189187SymbolicIndexingInterface. current_time (sys:: ExampleIntegrator ) = sys. t
190188```
191189
192190Then the following example would work:
193191``` julia
194- integrator = ExampleIntegrator ([1.0 , 2.0 , 3.0 ], [4.0 , 5.0 ], 6.0 , Dict (:x => 1 , :y => 2 , :z => 3 ), Dict (:a => 1 , :b => 2 ), :t )
195- getx = getu (integrator, :x )
192+ sys = ExampleSystem (Dict (:x => 1 , :y => 2 , :z => 3 ), Dict (:a => 1 , :b => 2 ), :t , Dict ())
193+ integrator = ExampleIntegrator ([1.0 , 2.0 , 3.0 ], [4.0 , 5.0 ], 6.0 , sys)
194+ getx = getu (sys, :x )
196195getx (integrator) # 1.0
197196
198- get_expr = getu (integrator , :(x + y + t))
197+ get_expr = getu (sys , :(x + y + t))
199198get_expr (integrator) # 13.0
200199
201- setx! = setu (integrator , :y )
200+ setx! = setu (sys , :y )
202201setx! (integrator, 0.0 )
203202getx (integrator) # 0.0
204203```
205204
205+ In case a type stores timeseries data (such as solutions), then it must also implement
206+ the [ ` Timeseries ` ] ( @ref ) trait. The type would then return a timeseries from
207+ [ ` state_values ` ] ( @ref ) and [ ` current_time ` ] ( @ref ) and the function returned from
208+ [ ` getu ` ] ( @ref ) would then return a timeseries as well. For example, consider the
209+ ` ExampleSolution ` below:
210+
211+ ``` julia
212+ struct ExampleSolution
213+ u:: Vector{Vector{Float64}}
214+ t:: Vector{Float64}
215+ p:: Vector{Float64}
216+ sys:: ExampleSystem
217+ end
218+
219+ # define a fallback for the interface methods
220+ SymbolicIndexingInterface. symbolic_container (sol:: ExampleSolution ) = sol. sys
221+ SymbolicIndexingInterface. parameter_values (sol:: ExampleSolution ) = sol. p
222+ # define the trait
223+ SymbolicIndexingInterface. is_timeseries (:: Type{ExampleSolution} ) = Timeseries ()
224+ # both state_values and current_time return a timeseries, which must be
225+ # the same length
226+ SymbolicIndexingInterface. state_values (sol:: ExampleSolution ) = sol. u
227+ SymbolicIndexingInterface. current_time (sol:: ExampleSolution ) = sol. t
228+ ```
229+
230+ Then the following example would work:
231+ ``` julia
232+ # using the same system that the ExampleIntegrator used
233+ sol = ExampleSolution ([[1.0 , 2.0 , 3.0 ], [1.5 , 2.5 , 3.5 ]], [4.0 , 5.0 ], [6.0 , 7.0 ], sys)
234+ getx = getu (sys, :x )
235+ getx (sol) # [1.0, 1.5]
236+
237+ get_expr = getu (sys, :(x + y + t))
238+ get_expr (sol) # [9.0, 11.0]
239+
240+ get_arr = getu (sys, [:y , :(x + a)])
241+ get_arr (sol) # [[2.0, 5.0], [2.5, 5.5]]
242+
243+ get_tuple = getu (sys, (:z , :(z * t)))
244+ get_tuple (sol) # [(3.0, 18.0), (3.5, 24.5)]
245+ ```
246+
247+ Note that ` setu ` is not designed to work for ` Timeseries ` objects.
248+
249+ If a type needs to perform some additional actions when updating the state/parameters
250+ or if it is not possible to return a mutable reference to the state/parameter vector
251+ which can directly be modified, the functions [ ` set_state! ` ] ( @ref ) and/or
252+ [ ` set_parameter! ` ] ( @ref ) can be used. For example, suppose our ` ExampleIntegrator `
253+ had an additional field ` u_modified::Bool ` to allow it to keep track of when a
254+ discontinuity occurs and handle it appropriately. This flag needs to be set to ` true `
255+ whenever the state is modified. The ` set_state! ` function can then be implemented as
256+ follows:
257+
258+ ``` julia
259+ function SymbolicIndexingInterface. set_state! (integrator:: ExampleIntegrator , val, idx)
260+ integrator. u[idx] = val
261+ integrator. u_modified = true
262+ end
263+ ```
264+
206265# Implementing the ` SymbolicTypeTrait ` for a type
207266
208267The ` SymbolicTypeTrait ` is used to identify values that can act as symbolic variables. It
0 commit comments