Skip to content

Commit 0e68e2a

Browse files
committed
save progress
1 parent fd56e7a commit 0e68e2a

File tree

3 files changed

+268
-247
lines changed

3 files changed

+268
-247
lines changed

src/reaction_structure.jl

Lines changed: 160 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
### Species Structure ###
1+
### Species Symbolic Variable Definition ###
22

3-
# Catalyst specific symbolics to support SBML
3+
# Defines species-related metadata.
44
struct ParameterConstantSpecies end
55
struct VariableBCSpecies end
66
struct VariableSpecies end
77
Symbolics.option_to_metadata_type(::Val{:isconstantspecies}) = ParameterConstantSpecies
88
Symbolics.option_to_metadata_type(::Val{:isbcspecies}) = VariableBCSpecies
99
Symbolics.option_to_metadata_type(::Val{:isspecies}) = VariableSpecies
1010

11+
# Defines species-related metadata getters.
1112
"""
1213
Catalyst.isconstant(s)
1314
@@ -52,14 +53,13 @@ function tospecies(s)
5253
MT.setmetadata(s, VariableSpecies, true)
5354
end
5455

55-
# true for species which shouldn't change from the reactions, including non-species
56-
# variables
56+
# Function returning `true`` for species which shouldn't change from the reactions,
57+
# including non-species variables.
5758
drop_dynamics(s) = isconstant(s) || isbc(s) || (!isspecies(s))
5859

5960

6061
### Reaction Structure ###
6162

62-
6363
"""
6464
$(TYPEDEF)
6565
@@ -129,13 +129,7 @@ struct Reaction{S, T}
129129
metadata::Vector{Pair{Symbol, Any}}
130130
end
131131

132-
"""
133-
isvalidreactant(s)
134-
135-
Test if a species is valid as a reactant (i.e. a species variable or a constant parameter).
136-
"""
137-
isvalidreactant(s) = MT.isparameter(s) ? isconstant(s) : (isspecies(s) && !isconstant(s))
138-
132+
# Five-argument constructor accepting rate, substrates, and products, and their stoichiometries.
139133
function Reaction(rate, subs, prods, substoich, prodstoich;
140134
netstoich = nothing, metadata = Pair{Symbol, Any}[],
141135
only_use_rate = metadata_only_use_rate_check(metadata), kwargs...)
@@ -208,18 +202,53 @@ function Reaction(rate, subs, prods, substoich, prodstoich;
208202
Reaction(value(rate), subs, prods, substoich′, prodstoich′, ns, only_use_rate, metadata)
209203
end
210204

205+
# Three argument constructor assumes stoichiometric coefs are one and integers.
206+
function Reaction(rate, subs, prods; kwargs...)
207+
sstoich = isnothing(subs) ? nothing : ones(Int, length(subs))
208+
pstoich = isnothing(prods) ? nothing : ones(Int, length(prods))
209+
Reaction(rate, subs, prods, sstoich, pstoich; kwargs...)
210+
end
211+
212+
"""
213+
isvalidreactant(s)
214+
215+
Test if a species is valid as a reactant (i.e. a species variable or a constant parameter).
216+
"""
217+
isvalidreactant(s) = MT.isparameter(s) ? isconstant(s) : (isspecies(s) && !isconstant(s))
218+
211219
# Checks if a metadata input has an entry :only_use_rate => true
212220
function metadata_only_use_rate_check(metadata)
213221
only_use_rate_idx = findfirst(:only_use_rate == entry[1] for entry in metadata)
214222
isnothing(only_use_rate_idx) && return false
215223
return Bool(metadata[only_use_rate_idx][2])
216224
end
217225

218-
# three argument constructor assumes stoichiometric coefs are one and integers
219-
function Reaction(rate, subs, prods; kwargs...)
220-
sstoich = isnothing(subs) ? nothing : ones(Int, length(subs))
221-
pstoich = isnothing(prods) ? nothing : ones(Int, length(prods))
222-
Reaction(rate, subs, prods, sstoich, pstoich; kwargs...)
226+
# calculates the net stoichiometry of a reaction as a vector of pairs (sub,substoich)
227+
function get_netstoich(subs, prods, sstoich, pstoich)
228+
# stoichiometry as a Dictionary
229+
nsdict = Dict{Any, eltype(sstoich)}(sub => -sstoich[i] for (i, sub) in enumerate(subs))
230+
for (i, p) in enumerate(prods)
231+
coef = pstoich[i]
232+
@inbounds nsdict[p] = haskey(nsdict, p) ? nsdict[p] + coef : coef
233+
end
234+
235+
# stoichiometry as a vector
236+
[el for el in nsdict if !_iszero(el[2])]
237+
end
238+
239+
# Get the net stoichiometries' type.
240+
netstoich_stoichtype(::Vector{Pair{S, T}}) where {S, T} = T
241+
242+
243+
### Base and MTK Accessors ###
244+
245+
# Show function for `Reaction`s.
246+
function Base.show(io::IO, rx::Reaction)
247+
print(io, rx.rate, ", ")
248+
print_rxside(io, rx.substrates, rx.substoich)
249+
arrow = rx.only_use_rate ? "" : "-->"
250+
print(io, " ", arrow, " ")
251+
print_rxside(io, rx.products, rx.prodstoich)
223252
end
224253

225254
function print_rxside(io::IO, specs, stoich)
@@ -244,21 +273,6 @@ function print_rxside(io::IO, specs, stoich)
244273
nothing
245274
end
246275

247-
function Base.show(io::IO, rx::Reaction)
248-
print(io, rx.rate, ", ")
249-
print_rxside(io, rx.substrates, rx.substoich)
250-
arrow = rx.only_use_rate ? "" : "-->"
251-
print(io, " ", arrow, " ")
252-
print_rxside(io, rx.products, rx.prodstoich)
253-
end
254-
255-
function apply_if_nonempty(f, v)
256-
isempty(v) && return v
257-
s = similar(v)
258-
map!(f, s, v)
259-
s
260-
end
261-
262276
function ModelingToolkit.namespace_equation(rx::Reaction, name; kw...)
263277
f = Base.Fix2(namespace_expr, name)
264278
rate = f(rx.rate)
@@ -275,20 +289,19 @@ function ModelingToolkit.namespace_equation(rx::Reaction, name; kw...)
275289
Reaction(rate, subs, prods, substoich, prodstoich, netstoich, rx.only_use_rate, rx.metadata)
276290
end
277291

278-
netstoich_stoichtype(::Vector{Pair{S, T}}) where {S, T} = T
292+
function apply_if_nonempty(f, v)
293+
isempty(v) && return v
294+
s = similar(v)
295+
map!(f, s, v)
296+
s
297+
end
279298

280-
# calculates the net stoichiometry of a reaction as a vector of pairs (sub,substoich)
281-
function get_netstoich(subs, prods, sstoich, pstoich)
282-
# stoichiometry as a Dictionary
283-
nsdict = Dict{Any, eltype(sstoich)}(sub => -sstoich[i] for (i, sub) in enumerate(subs))
284-
for (i, p) in enumerate(prods)
285-
coef = pstoich[i]
286-
@inbounds nsdict[p] = haskey(nsdict, p) ? nsdict[p] + coef : coef
287-
end
299+
# Overwrites functions in ModelingToolkit to give the correct input.
300+
ModelingToolkit.is_diff_equation(rx::Reaction) = false
301+
ModelingToolkit.is_alg_equation(rx::Reaction) = false
288302

289-
# stoichiometry as a vector
290-
[el for el in nsdict if !_iszero(el[2])]
291-
end
303+
304+
### Reaction-specific Accessors ###
292305

293306
"""
294307
isbcbalanced(rx::Reaction)
@@ -315,8 +328,107 @@ function isbcbalanced(rx::Reaction)
315328
true
316329
end
317330

318-
### Reaction Acessor Functions ###
331+
### Reaction Metadata Implementation ###
332+
# These are currently considered internal, but can be used by public accessor functions like get_noise_scaling.
319333

320-
# Overwrites functions in ModelingToolkit to give the correct input.
321-
ModelingToolkit.is_diff_equation(rx::Reaction) = false
322-
ModelingToolkit.is_alg_equation(rx::Reaction) = false
334+
"""
335+
getmetadata_dict(reaction::Reaction)
336+
337+
Retrives the `ImmutableDict` containing all of the metadata associated with a specific reaction.
338+
339+
Arguments:
340+
- `reaction`: The reaction for which we wish to retrive all metadata.
341+
342+
Example:
343+
```julia
344+
reaction = @reaction k, 0 --> X, [description="Production reaction"]
345+
getmetadata_dict(reaction)
346+
```
347+
"""
348+
function getmetadata_dict(reaction::Reaction)
349+
return reaction.metadata
350+
end
351+
352+
"""
353+
hasmetadata(reaction::Reaction, md_key::Symbol)
354+
355+
Checks if a `Reaction` have a certain metadata field. If it does, returns `true` (else returns `false`).
356+
357+
Arguments:
358+
- `reaction`: The reaction for which we wish to check if a specific metadata field exist.
359+
- `md_key`: The metadata for which we wish to check existence of.
360+
361+
Example:
362+
```julia
363+
reaction = @reaction k, 0 --> X, [description="Production reaction"]
364+
hasmetadata(reaction, :description)
365+
```
366+
"""
367+
function hasmetadata(reaction::Reaction, md_key::Symbol)
368+
return any(isequal(md_key, entry[1]) for entry in getmetadata_dict(reaction))
369+
end
370+
371+
"""
372+
getmetadata(reaction::Reaction, md_key::Symbol)
373+
374+
Retrives a certain metadata value from a `Reaction`. If the metadata does not exists, throws an error.
375+
376+
Arguments:
377+
- `reaction`: The reaction for which we wish to retrive a specific metadata value.
378+
- `md_key`: The metadata for which we wish to retrive.
379+
380+
Example:
381+
```julia
382+
reaction = @reaction k, 0 --> X, [description="Production reaction"]
383+
getmetadata(reaction, :description)
384+
```
385+
"""
386+
function getmetadata(reaction::Reaction, md_key::Symbol)
387+
if !hasmetadata(reaction, md_key)
388+
error("The reaction does not have a metadata field $md_key. It does have the following metadata fields: $(keys(getmetadata_dict(reaction))).")
389+
end
390+
metadata = getmetadata_dict(reaction)
391+
return metadata[findfirst(isequal(md_key, entry[1]) for entry in getmetadata_dict(reaction))][2]
392+
end
393+
394+
### Implemented Reaction Metadata ###
395+
396+
# Noise scaling.
397+
"""
398+
has_noise_scaling(reaction::Reaction)
399+
400+
Checks whether a specific reaction has the metadata field `noise_scaing`. If so, returns `true`, else
401+
returns `false`.
402+
403+
Arguments:
404+
- `reaction`: The reaction for which we wish to check.
405+
406+
Example:
407+
```julia
408+
reaction = @reaction k, 0 --> X, [noise_scaling=0.0]
409+
has_noise_scaling(reaction)
410+
"""
411+
function has_noise_scaling(reaction::Reaction)
412+
return hasmetadata(reaction, :noise_scaling)
413+
end
414+
415+
"""
416+
get_noise_scaling(reaction::Reaction)
417+
418+
Returns the noise_scaling metadata from a specific reaction.
419+
420+
Arguments:
421+
- `reaction`: The reaction for which we wish to retrive all metadata.
422+
423+
Example:
424+
```julia
425+
reaction = @reaction k, 0 --> X, [noise_scaling=0.0]
426+
get_noise_scaling(reaction)
427+
"""
428+
function get_noise_scaling(reaction::Reaction)
429+
if has_noise_scaling(reaction)
430+
return getmetadata(reaction, :noise_scaling)
431+
else
432+
error("Attempts to access noise_scaling metadata field for a reaction which does not have a value assigned for this metadata.")
433+
end
434+
end

src/reactionsystem_conversions.jl

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
2-
3-
4-
######################## Conversion to ODEs/SDEs/jump, etc ##############################
1+
### ODE & SDE Assembly ###
52

63
"""
74
oderatelaw(rx; combinatoric_ratelaw=true)
@@ -142,6 +139,8 @@ function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true,
142139
eqs
143140
end
144141

142+
### Jumps Assembly ###
143+
145144
"""
146145
jumpratelaw(rx; combinatoric_ratelaw=true)
147146
@@ -375,6 +374,8 @@ function assemble_jumps(rs; combinatoric_ratelaws = true)
375374
vcat(meqs, ceqs, veqs)
376375
end
377376

377+
### Equation Coupling ###
378+
378379
# merge constraint components with the ReactionSystem components
379380
# also handles removing BC and constant species
380381
function addconstraints!(eqs, rs::ReactionSystem, ists, ispcs; remove_conserved = false)
@@ -427,20 +428,16 @@ function error_if_constraints(::Type{T}, sys::ReactionSystem) where {T <: MT.Abs
427428
nothing
428429
end
429430

430-
# used by flattened systems that don't support differential equation constraint eqs
431-
function error_if_constraint_odes(::Type{T},
432-
rs::ReactionSystem) where {T <: MT.AbstractSystem}
433-
any(eq -> (eq isa Equation) && MT.isdiffeq(eq), get_eqs(rs)) &&
434-
error("Cannot convert to system type $T when there are ODE constraint equations.")
435-
nothing
436-
end
431+
### Utility ###
437432

438433
function spatial_convert_err(rs::ReactionSystem, systype)
439434
isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.")
440435
end
441436

442437
COMPLETENESS_ERROR = "A ReactionSystem must be complete before it can be converted to other system types. A ReactionSystem can be marked as complete using the `complete` function."
443438

439+
### System Conversions ###
440+
444441
"""
445442
```julia
446443
Base.convert(::Type{<:ODESystem},rs::ReactionSystem)
@@ -560,7 +557,7 @@ function nonlinear_convert_differentials_check(rs::ReactionSystem)
560557
561558
If you still would like to perform this conversions, please use the `all_differentials_permitted = true` option. In this case, all differential will be set to `0`.
562559
However, it is recommended to proceed with caution to ensure that the produced nonlinear equation makes sense for you intended application."
563-
)
560+
)
564561
end
565562
end
566563
end
@@ -666,7 +663,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs
666663
kwargs...)
667664
end
668665

669-
### Converts a reaction system to ODE or SDE problems ###
666+
### Problems ###
670667

671668
# ODEProblem from AbstractReactionNetwork
672669
function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan,

0 commit comments

Comments
 (0)