Skip to content

Commit f779736

Browse files
Merge pull request #3334 from vyudu/add-metadata-getters
fix: Add metadata getters
2 parents 35c8f4c + 6276635 commit f779736

File tree

4 files changed

+151
-17
lines changed

4 files changed

+151
-17
lines changed

docs/src/basics/Variable_metadata.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ using ModelingToolkit: t_nounits as t, D_nounits as D
5454
5555
@variables i(t) [connect = Flow]
5656
@variables k(t) [connect = Stream]
57+
hasconnect(i)
58+
```
59+
```@example connect
60+
getconnect(k)
5761
```
5862

5963
## Input or output
@@ -177,8 +181,44 @@ A variable can be marked `irreducible` to prevent it from being moved to an
177181
`observed` state. This forces the variable to be computed during solving so that
178182
it can be accessed in [callbacks](@ref events)
179183

180-
```julia
184+
```@example metadata
181185
@variable important_value [irreducible = true]
186+
isirreducible(important_value)
187+
```
188+
189+
## State Priority
190+
191+
When a model is structurally simplified, the algorithm will try to ensure that the variables with higher state priority become states of the system. A variable's state priority is a number set using the `state_priority` metadata.
192+
193+
```@example metadata
194+
@variable important_dof [state_priority = 10] unimportant_dof [state_priority = -2]
195+
state_priority(important_dof)
196+
```
197+
198+
## Units
199+
200+
Units for variables can be designated using symbolic metadata. For more information, please see the [model validation and units](@ref units) section of the docs. Note that `getunit` is not equivalent to `get_unit` - the former is a metadata getter for individual variables (and is provided so the same interface function for `unit` exists like other metadata), while the latter is used to handle more general symbolic expressions.
201+
202+
```@example metadata
203+
@variable speed [unit=u"m/s"]
204+
hasunit(speed)
205+
```
206+
```@example metadata
207+
getunit(speed)
208+
```
209+
210+
## Miscellaneous metadata
211+
212+
User-defined metadata can be added using the `misc` metadata. This can be queried
213+
using the `hasmisc` and `getmisc` functions.
214+
215+
```@example metadata
216+
@variables u [misc = :conserved_parameter] y [misc = [2, 4, 6]]
217+
hasmisc(u)
218+
```
219+
220+
```@example metadata
221+
getmisc(y)
182222
```
183223

184224
## Additional functions

src/ModelingToolkit.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,9 @@ export initial_state, transition, activeState, entry, ticksInState, timeInState
247247
export @component, @mtkmodel, @mtkbuild
248248
export isinput, isoutput, getbounds, hasbounds, getguess, hasguess, isdisturbance,
249249
istunable, getdist, hasdist,
250-
tunable_parameters, isirreducible, getdescription, hasdescription
250+
tunable_parameters, isirreducible, getdescription, hasdescription,
251+
hasunit, getunit, hasconnect, getconnect,
252+
hasmisc, getmisc
251253
export ode_order_lowering, dae_order_lowering, liouville_transform
252254
export PDESystem
253255
export Differential, expand_derivatives, @derivatives

src/variables.jl

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ struct VariableStatePriority end
88
struct VariableMisc end
99
Symbolics.option_to_metadata_type(::Val{:unit}) = VariableUnit
1010
Symbolics.option_to_metadata_type(::Val{:connect}) = VariableConnectType
11-
Symbolics.option_to_metadata_type(::Val{:noise}) = VariableNoiseType
1211
Symbolics.option_to_metadata_type(::Val{:input}) = VariableInput
1312
Symbolics.option_to_metadata_type(::Val{:output}) = VariableOutput
1413
Symbolics.option_to_metadata_type(::Val{:irreducible}) = VariableIrreducible
@@ -29,7 +28,7 @@ ModelingToolkit.dump_variable_metadata(p)
2928
"""
3029
function dump_variable_metadata(var)
3130
uvar = unwrap(var)
32-
vartype, name = get(uvar.metadata, VariableSource, (:unknown, :unknown))
31+
variable_source, name = Symbolics.getmetadata(uvar, VariableSource, (:unknown, :unknown))
3332
type = symtype(uvar)
3433
if type <: AbstractArray
3534
shape = Symbolics.shape(var)
@@ -39,14 +38,13 @@ function dump_variable_metadata(var)
3938
else
4039
shape = nothing
4140
end
42-
unit = get(uvar.metadata, VariableUnit, nothing)
43-
connect = get(uvar.metadata, VariableConnectType, nothing)
44-
noise = get(uvar.metadata, VariableNoiseType, nothing)
41+
unit = getunit(uvar)
42+
connect = getconnect(uvar)
4543
input = isinput(uvar) || nothing
4644
output = isoutput(uvar) || nothing
47-
irreducible = get(uvar.metadata, VariableIrreducible, nothing)
48-
state_priority = get(uvar.metadata, VariableStatePriority, nothing)
49-
misc = get(uvar.metadata, VariableMisc, nothing)
45+
irreducible = isirreducible(var)
46+
state_priority = Symbolics.getmetadata(uvar, VariableStatePriority, nothing)
47+
misc = getmisc(uvar)
5048
bounds = hasbounds(uvar) ? getbounds(uvar) : nothing
5149
desc = getdescription(var)
5250
if desc == ""
@@ -57,16 +55,16 @@ function dump_variable_metadata(var)
5755
disturbance = isdisturbance(uvar) || nothing
5856
tunable = istunable(uvar, isparameter(uvar))
5957
dist = getdist(uvar)
60-
type = symtype(uvar)
58+
variable_type = getvariabletype(uvar)
6159

6260
meta = (
6361
var = var,
64-
vartype,
62+
variable_source,
6563
name,
64+
variable_type,
6665
shape,
6766
unit,
6867
connect,
69-
noise,
7068
input,
7169
output,
7270
irreducible,
@@ -85,11 +83,28 @@ function dump_variable_metadata(var)
8583
return NamedTuple(k => v for (k, v) in pairs(meta) if v !== nothing)
8684
end
8785

86+
### Connect
8887
abstract type AbstractConnectType end
8988
struct Equality <: AbstractConnectType end # Equality connection
9089
struct Flow <: AbstractConnectType end # sum to 0
9190
struct Stream <: AbstractConnectType end # special stream connector
9291

92+
"""
93+
getconnect(x)
94+
95+
Get the connect type of x. See also [`hasconnect`](@ref).
96+
"""
97+
getconnect(x) = getconnect(unwrap(x))
98+
getconnect(x::Symbolic) = Symbolics.getmetadata(x, VariableConnectType, nothing)
99+
"""
100+
hasconnect(x)
101+
102+
Determine whether variable `x` has a connect type. See also [`getconnect`](@ref).
103+
"""
104+
hasconnect(x) = getconnect(x) !== nothing
105+
setconnect(x, t::Type{T}) where T <: AbstractConnectType = setmetadata(x, VariableConnectType, t)
106+
107+
### Input, Output, Irreducible
93108
isvarkind(m, x::Union{Num, Symbolics.Arr}) = isvarkind(m, value(x))
94109
function isvarkind(m, x)
95110
iskind = getmetadata(x, m, nothing)
@@ -98,15 +113,17 @@ function isvarkind(m, x)
98113
getmetadata(x, m, false)
99114
end
100115

101-
setinput(x, v) = setmetadata(x, VariableInput, v)
102-
setoutput(x, v) = setmetadata(x, VariableOutput, v)
103-
setio(x, i, o) = setoutput(setinput(x, i), o)
116+
setinput(x, v::Bool) = setmetadata(x, VariableInput, v)
117+
setoutput(x, v::Bool) = setmetadata(x, VariableOutput, v)
118+
setio(x, i::Bool, o::Bool) = setoutput(setinput(x, i), o)
119+
104120
isinput(x) = isvarkind(VariableInput, x)
105121
isoutput(x) = isvarkind(VariableOutput, x)
122+
106123
# Before the solvability check, we already have handled IO variables, so
107124
# irreducibility is independent from IO.
108125
isirreducible(x) = isvarkind(VariableIrreducible, x)
109-
setirreducible(x, v) = setmetadata(x, VariableIrreducible, v)
126+
setirreducible(x, v::Bool) = setmetadata(x, VariableIrreducible, v)
110127
state_priority(x) = convert(Float64, getmetadata(x, VariableStatePriority, 0.0))::Float64
111128

112129
function default_toterm(x)
@@ -545,3 +562,38 @@ function get_default_or_guess(x)
545562
return getguess(x)
546563
end
547564
end
565+
566+
## Miscellaneous metadata ======================================================================
567+
"""
568+
getmisc(x)
569+
570+
Fetch any miscellaneous data associated with symbolic variable `x`.
571+
See also [`hasmisc(x)`](@ref).
572+
"""
573+
getmisc(x) = getmisc(unwrap(x))
574+
getmisc(x::Symbolic) = Symbolics.getmetadata(x, VariableMisc, nothing)
575+
"""
576+
hasmisc(x)
577+
578+
Determine whether a symbolic variable `x` has misc
579+
metadata associated with it.
580+
581+
See also [`getmisc(x)`](@ref).
582+
"""
583+
hasmisc(x) = getmisc(x) !== nothing
584+
setmisc(x, miscdata) = setmetadata(x, VariableMisc, miscdata)
585+
586+
## Units ======================================================================
587+
"""
588+
getunit(x)
589+
590+
Fetch the unit associated with variable `x`. This function is a metadata getter for an individual variable, while `get_unit` is used for unit inference on more complicated sdymbolic expressions.
591+
"""
592+
getunit(x) = getunit(unwrap(x))
593+
getunit(x::Symbolic) = Symbolics.getmetadata(x, VariableUnit, nothing)
594+
"""
595+
hasunit(x)
596+
597+
Check if the variable `x` has a unit.
598+
"""
599+
hasunit(x) = getunit(x) !== nothing

test/test_variable_metadata.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using ModelingToolkit
2+
using DynamicQuantities
23

34
# Bounds
45
@variables u [bounds = (-1, 1)]
@@ -185,3 +186,42 @@ params_meta = ModelingToolkit.dump_parameters(sys)
185186
params_meta = Dict([ModelingToolkit.getname(meta.var) => meta for meta in params_meta])
186187
@test params_meta[:p].default == 3.0
187188
@test isequal(params_meta[:q].dependency, 2p)
189+
190+
# Connect
191+
@variables x [connect = Flow]
192+
@test hasconnect(x)
193+
@test getconnect(x) == Flow
194+
@test ModelingToolkit.dump_variable_metadata(x).connect == Flow
195+
x = ModelingToolkit.setconnect(x, ModelingToolkit.Stream)
196+
@test getconnect(x) == ModelingToolkit.Stream
197+
198+
struct BadConnect end
199+
@test_throws Exception ModelingToolkit.setconnect(x, BadConnect)
200+
201+
# Unit
202+
@variables x [unit = u"s"]
203+
@test hasunit(x)
204+
@test getunit(x) == u"s"
205+
@test ModelingToolkit.dump_variable_metadata(x).unit == u"s"
206+
207+
# Misc data
208+
@variables x [misc = [:good]]
209+
@test hasmisc(x)
210+
@test getmisc(x) == [:good]
211+
x = ModelingToolkit.setmisc(x, "okay")
212+
@test getmisc(x) == "okay"
213+
214+
# Variable Type
215+
@variables x
216+
@test ModelingToolkit.getvariabletype(x) == ModelingToolkit.VARIABLE
217+
@test ModelingToolkit.dump_variable_metadata(x).variable_type == ModelingToolkit.VARIABLE
218+
@test ModelingToolkit.dump_variable_metadata(x).variable_source == :variables
219+
x = ModelingToolkit.toparam(x)
220+
@test ModelingToolkit.getvariabletype(x) == ModelingToolkit.PARAMETER
221+
@test ModelingToolkit.dump_variable_metadata(x).variable_source == :variables
222+
223+
@parameters y
224+
@test ModelingToolkit.getvariabletype(y) == ModelingToolkit.PARAMETER
225+
226+
@brownian z
227+
@test ModelingToolkit.getvariabletype(z) == ModelingToolkit.BROWNIAN

0 commit comments

Comments
 (0)