SBMLModel.jl 6.58 KB
Newer Older
1
2
3
4
5
6
7
"""
    struct SBMLModel

Thin wrapper around the model from SBML.jl library. Allows easy conversion from
SBML to any other model format.
"""
struct SBMLModel <: MetabolicModel
8
    sbml::SBML.Model
9
10
end

11
12
13
14
15
"""
    reactions(model::SBMLModel)::Vector{String}

Get reactions from a [`SBMLModel`](@ref).
"""
16
reactions(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.reactions)]
17
18
19
20
21
22

"""
    metabolites(model::SBMLModel)::Vector{String}

Get metabolites from a [`SBMLModel`](@ref).
"""
23
metabolites(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.species)]
24
25
26
27
28
29

"""
    n_reactions(model::SBMLModel)::Int

Efficient counting of reactions in [`SBMLModel`](@ref).
"""
30
n_reactions(model::SBMLModel)::Int = length(model.sbml.reactions)
31
32
33
34
35
36

"""
    n_metabolites(model::SBMLModel)::Int

Efficient counting of metabolites in [`SBMLModel`](@ref).
"""
37
n_metabolites(model::SBMLModel)::Int = length(model.sbml.species)
38
39

"""
40
    stoichiometry(model::SBMLModel)::SparseMat
41

42
Recreate the stoichiometry matrix from the [`SBMLModel`](@ref).
43
"""
44
45
function stoichiometry(model::SBMLModel)::SparseMat
    _, _, S = SBML.getS(model.sbml)
46
47
48
49
    return S
end

"""
50
    bounds(model::SBMLModel)::Tuple{SparseVec,SparseVec}
51

52
Get the lower and upper flux bounds of model [`SBMLModel`](@ref). Throws `DomainError` in
53
54
case if the SBML contains mismatching units.
"""
55
56
57
function bounds(model::SBMLModel)::Tuple{SparseVec,SparseVec}
    lbu = SBML.getLBs(model.sbml)
    ubu = SBML.getUBs(model.sbml)
58
59
60
61
62
63

    unit = lbu[1][2]
    getvalue = (val, _)::Tuple -> val
    getunit = (_, unit)::Tuple -> unit

    allunits = unique([getunit.(lbu) getunit.(ubu)])
64
65
66
67
68
69
70
71
    length(allunits) == 1 || throw(
        DomainError(
            allunits,
            "The SBML file uses multiple units; loading needs conversion",
        ),
    )

    return sparse.((getvalue.(lbu), getvalue.(ubu)))
72
73
end

74
75
76
77
78
"""
    balance(model::SBMLModel)::SparseVec

Balance vector of a [`SBMLModel`](@ref). This is always zero.
"""
79
balance(model::SBMLModel)::SparseVec = spzeros(n_metabolites(model))
80
81
82
83
84
85

"""
    objective(model::SBMLModel)::SparseVec

Objective of the [`SBMLModel`](@ref). 
"""
86
objective(model::SBMLModel)::SparseVec = SBML.getOCs(model.sbml)
87

88
89
90
91
92
"""
    genes(model::SBMLModel)::Vector{String}

Get genes of a [`SBMLModel`](@ref).
"""
93
genes(model::SBMLModel)::Vector{String} = [k for k in keys(model.sbml.gene_products)]
94
95
96
97
98
99

"""
    n_genes(model::SBMLModel)::Int

Get number of genes in [`SBMLModel`](@ref).
"""
100
n_genes(model::SBMLModel)::Int = length(model.sbml.gene_products)
101

102
103
104
105
106
"""
    reaction_gene_association(model::SBMLModel, rid::String)::Maybe{GeneAssociation}

Retrieve the [`GeneAssociation`](@ref) from [`SBMLModel`](@ref).
"""
107
108
reaction_gene_association(model::SBMLModel, rid::String)::Maybe{GeneAssociation} =
    _maybemap(_parse_grr, model.sbml.reactions[rid].gene_product_association)
109

110
111
112
113
114
"""
    metabolite_formula(model::SBMLModel, mid::String)::Maybe{MetaboliteFormula}

Get [`MetaboliteFormula`](@ref) from a chosen metabolite from [`SBMLModel`](@ref).
"""
115
116
metabolite_formula(model::SBMLModel, mid::String)::Maybe{MetaboliteFormula} =
    _maybemap(_parse_formula, model.sbml.species[mid].formula)
117

118
119
120
121
122
"""
    metabolite_charge(model::SBMLModel, mid::String)::Maybe{Int}

Get charge of a chosen metabolite from [`SBMLModel`](@ref).
"""
123
124
metabolite_charge(model::SBMLModel, mid::String)::Maybe{Int} =
    model.sbml.species[mid].charge
125
126
127
128
129
130
131
132
133
134
135
136
137

function _sbml_export_annotation(annotation)::Maybe{String}
    if isnothing(annotation) || isempty(annotation)
        nothing
    elseif length(annotation) != 1 || first(annotation).first != ""
        @_io_log @warn "Possible data loss: annotation cannot be exported to SBML" annotation
        join(["$k: $v" for (k, v) in annotation], "\n")
    else
        first(annotation).second
    end
end

_sbml_export_notes = _sbml_export_annotation
138

St. Elmo's avatar
St. Elmo committed
139
140

"""
St. Elmo's avatar
St. Elmo committed
141
    reaction_stoichiometry(model::SBMLModel, rxn_id::String)::Dict{String, Float64}
St. Elmo's avatar
St. Elmo committed
142
143
144
145

Return the reaction equation of reaction with id `rxn_id` in model. The reaction
equation maps metabolite ids to their stoichiometric coefficients.
"""
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
146
function reaction_stoichiometry(m::SBMLModel, rxn_id::String)::Dict{String,Float64}
St. Elmo's avatar
St. Elmo committed
147
148
149
    m.sbml.reactions[rxn_id].stoichiometry
end

150
151
152
153
154
"""
    Base.convert(::Type{SBMLModel}, mm::MetabolicModel)

Convert any metabolic model to [`SBMLModel`](@ref).
"""
155
156
157
function Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
    if typeof(mm) == SBMLModel
        return mm
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
158
159
    end

160
161
162
163
164
165
    mets = metabolites(mm)
    rxns = reactions(mm)
    stoi = stoichiometry(mm)
    (lbs, ubs) = bounds(mm)
    ocs = objective(mm)
    comps = _default.("", metabolite_compartment.(Ref(mm), mets))
166
    compss = Set(comps)
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
167
168

    return SBMLModel(
169
170
171
        SBML.Model(
            Dict(), # parameters
            Dict("" => []), # units
cylon-x's avatar
cylon-x committed
172
173
174
175
176
177
178
            Dict([
                comp =>
                    SBML.Compartment(nothing, nothing, nothing, nothing, nothing, nothing)
                for comp in compss
            ],),
            Dict([
                mid => SBML.Species(
179
                    nothing, # name
180
                    _default("", comps[mi]), # compartment
181
                    nothing, # no information about boundary conditions
182
183
                    metabolite_formula(mm, mid),
                    metabolite_charge(mm, mid),
184
185
                    nothing, # initial amount
                    nothing, # only substance unit flags
186
187
                    _sbml_export_notes(metabolite_notes(mm, mid)),
                    _sbml_export_annotation(metabolite_annotations(mm, mid)),
cylon-x's avatar
cylon-x committed
188
189
190
191
192
193
194
195
                ) for (mi, mid) in enumerate(mets)
            ],),
            Dict([
                rid => SBML.Reaction(
                    Dict([
                        mets[i] => stoi[i, ri] for
                        i in SparseArrays.nonzeroinds(stoi[:, ri])
                    ],),
196
197
198
                    (lbs[ri], ""),
                    (ubs[ri], ""),
                    ocs[ri],
199
                    _maybemap(
200
                        x -> _unparse_grr(SBML.GeneProductAssociation, x),
201
                        reaction_gene_association(mm, rid),
202
203
                    ),
                    nothing,
204
205
                    _sbml_export_notes(reaction_notes(mm, rid)),
                    _sbml_export_annotation(reaction_annotations(mm, rid)),
cylon-x's avatar
cylon-x committed
206
207
208
209
                ) for (ri, rid) in enumerate(rxns)
            ],),
            Dict([
                gid => SBML.GeneProduct(
210
211
                    nothing,
                    nothing,
212
213
                    _sbml_export_notes(gene_notes(mm, gid)),
                    _sbml_export_annotation(gene_annotations(mm, gid)),
cylon-x's avatar
cylon-x committed
214
215
                ) for gid in genes(mm)
            ],),
216
            Dict(), # function definitions
Miroslav Kratochvil's avatar
Miroslav Kratochvil committed
217
218
        ),
    )
219
end