Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
LCSB-BioCore
SBML.jl
Commits
06a4db5c
Unverified
Commit
06a4db5c
authored
Apr 16, 2021
by
Miroslav Kratochvil
🚴
Committed by
GitHub
Apr 16, 2021
Browse files
Merge pull request #39 from LCSB-BioCore/mk-species-units
Extract kinetics-relevant information
parents
778673e9
3233b323
Changes
5
Hide whitespace changes
Inline
Side-by-side
Project.toml
View file @
06a4db5c
name
=
"SBML"
uuid
=
"e5567a89-2604-4b09-9718-f5f78e97c3bb"
authors
=
[
"Mirek Kratochvil <miroslav.kratochvil@uni.lu>"
,
"LCSB R3 team <lcsb-r3@uni.lu>"
]
version
=
"0.
3.2
"
version
=
"0.
4.0
"
[deps]
Libdl
=
"8f399da3-3557-5675-b5ff-fb832c97cbdb"
...
...
src/SBML.jl
View file @
06a4db5c
...
...
@@ -6,6 +6,7 @@ using SparseArrays
include
(
"structs.jl"
)
include
(
"version.jl"
)
include
(
"readsbml.jl"
)
include
(
"math.jl"
)
include
(
"utils.jl"
)
sbml
=
(
sym
::
Symbol
)
->
dlsym
(
SBML_jll
.
libsbml_handle
,
sym
)
...
...
src/math.jl
0 → 100644
View file @
06a4db5c
"""
ast_is(ast::VPtr, what::Symbol)::Bool
Helper for quickly recognizing kinds of ASTs
"""
ast_is
(
ast
::
VPtr
,
what
::
Symbol
)
::
Bool
=
ccall
(
sbml
(
what
),
Cint
,
(
VPtr
,),
ast
)
!=
0
"""
parse_math_children(ast::VPtr)::Vector{Math}
Recursively parse all children of an AST node.
"""
parse_math_children
(
ast
::
VPtr
)
::
Vector
{
Math
}
=
[
parse_math
(
ccall
(
sbml
(
:
ASTNode_getChild
),
VPtr
,
(
VPtr
,
Cuint
),
ast
,
i
-
1
))
for
i
=
1
:
ccall
(
sbml
(
:
ASTNode_getNumChildren
),
Cuint
,
(
VPtr
,),
ast
)
]
"""
parse_math(ast::VPtr)::Math
This attempts to parse out a decent Julia-esque ([`Math`](@ref) AST from a
pointer to `ASTNode_t`.
"""
function
parse_math
(
ast
::
VPtr
)
::
Math
if
ast_is
(
ast
,
:
ASTNode_isName
)
||
ast_is
(
ast
,
:
ASTNode_isConstant
)
return
MathIdent
(
get_string
(
ast
,
:
ASTNode_getName
))
elseif
ast_is
(
ast
,
:
ASTNode_isInteger
)
return
MathVal
(
ccall
(
sbml
(
:
ASTNode_getInteger
),
Cint
,
(
VPtr
,),
ast
))
elseif
ast_is
(
ast
,
:
ASTNode_isReal
)
return
MathVal
(
ccall
(
sbml
(
:
ASTNode_getReal
),
Cdouble
,
(
VPtr
,),
ast
))
elseif
ast_is
(
ast
,
:
ASTNode_isFunction
)
return
MathApply
(
get_string
(
ast
,
:
ASTNode_getName
),
parse_math_children
(
ast
))
elseif
ast_is
(
ast
,
:
ASTNode_isOperator
)
return
MathApply
(
string
(
Char
(
ccall
(
sbml
(
:
ASTNode_getCharacter
),
Cchar
,
(
VPtr
,),
ast
))),
parse_math_children
(
ast
),
)
elseif
ast_is
(
ast
,
:
ASTNode_isLambda
)
children
=
parse_math_children
(
ast
)
if
!
isempty
(
children
)
body
=
pop!
(
children
)
return
MathLambda
(
broadcast
((
x
::
MathIdent
)
->
x
.
id
,
children
),
body
)
else
@warn
"invalid function definition found"
return
MathIdent
(
"?invalid?"
)
end
else
@warn
"unsupported math element found"
return
MathIdent
(
"?unsupported?"
)
end
end
src/readsbml.jl
View file @
06a4db5c
...
...
@@ -24,7 +24,7 @@ Like [`get_string`](@Ref), but returns `nothing` instead of throwing an
exception.
This is used to get notes and annotations and several other things (see
`get
N
otes`, `get
A
nnotations`)
`get
_n
otes`, `get
_a
nnotations`)
"""
function
get_optional_string
(
x
::
VPtr
,
fn_sym
)
::
Maybe
{
String
}
str
=
ccall
(
sbml
(
fn_sym
),
Cstring
,
(
VPtr
,),
x
)
...
...
@@ -35,6 +35,45 @@ function get_optional_string(x::VPtr, fn_sym)::Maybe{String}
end
end
"""
get_optional_bool(x::VPtr, is_sym, get_sym)::Maybe{Bool}
Helper for getting out boolean flags.
"""
function
get_optional_bool
(
x
::
VPtr
,
is_sym
,
get_sym
)
::
Maybe
{
Bool
}
if
ccall
(
sbml
(
is_sym
),
Cint
,
(
VPtr
,),
x
)
!=
0
return
ccall
(
sbml
(
get_sym
),
Cint
,
(
VPtr
,),
x
)
!=
0
else
return
nothing
end
end
"""
get_optional_int(x::VPtr, is_sym, get_sym)::Maybe{UInt}
Helper for getting out unsigned integers.
"""
function
get_optional_int
(
x
::
VPtr
,
is_sym
,
get_sym
)
::
Maybe
{
Int
}
if
ccall
(
sbml
(
is_sym
),
Cint
,
(
VPtr
,),
x
)
!=
0
return
ccall
(
sbml
(
get_sym
),
Cint
,
(
VPtr
,),
x
)
else
return
nothing
end
end
"""
get_optional_double(x::VPtr, is_sym, get_sym)::Maybe{Float64}
Helper for getting out C doubles aka Float64s.
"""
function
get_optional_double
(
x
::
VPtr
,
is_sym
,
get_sym
)
::
Maybe
{
Float64
}
if
ccall
(
sbml
(
is_sym
),
Cint
,
(
VPtr
,),
x
)
!=
0
return
ccall
(
sbml
(
get_sym
),
Cdouble
,
(
VPtr
,),
x
)
else
return
nothing
end
end
"""
function readSBML(fn::String)::Model
...
...
@@ -65,8 +104,8 @@ function readSBML(fn::String)::Model
end
end
get
N
otes
(
x
::
VPtr
)
::
Maybe
{
String
}
=
get_optional_string
(
x
,
:
SBase_getNotesString
)
get
A
nnotation
(
x
::
VPtr
)
::
Maybe
{
String
}
=
get_optional_string
(
x
,
:
SBase_getAnnotationString
)
get
_n
otes
(
x
::
VPtr
)
::
Maybe
{
String
}
=
get_optional_string
(
x
,
:
SBase_getNotesString
)
get
_a
nnotation
(
x
::
VPtr
)
::
Maybe
{
String
}
=
get_optional_string
(
x
,
:
SBase_getAnnotationString
)
"""
function getAssociation(x::VPtr)::GeneProductAssociation
...
...
@@ -143,18 +182,31 @@ function extractModel(mdl::VPtr)::Model
end
# parse out compartment names
compartments = [
get_string(
ccall(sbml(:Model_getCompartment), VPtr, (VPtr, Cuint), mdl, i - 1),
:Compartment_getId,
) for i = 1:ccall(sbml(:Model_getNumCompartments), Cuint, (VPtr,), mdl)
]
compartments = Dict{String,Compartment}()
for i = 1:ccall(sbml(:Model_getNumCompartments), Cuint, (VPtr,), mdl)
co = ccall(sbml(:Model_getCompartment), VPtr, (VPtr, Cuint), mdl, i - 1)
compartments[get_string(co, :Compartment_getId)] = Compartment(
get_optional_string(co, :Compartment_getName),
get_optional_bool(co, :Compartment_isSetConstant, :Compartment_getConstant),
get_optional_int(
co,
:Compartment_isSetSpatialDimensions,
:Compartment_getSpatialDimensions,
),
get_optional_double(co, :Compartment_isSetSize, :Compartment_getSize),
get_optional_string(co, :Compartment_getUnits),
get_notes(co),
get_annotation(co),
)
end
# parse out species
species = Dict{String,Species}()
for i = 1:ccall(sbml(:Model_getNumSpecies), Cuint, (VPtr,), mdl)
sp = ccall(sbml(:Model_getSpecies), VPtr, (VPtr, Cuint), mdl, i - 1)
sp_fbc = ccall(sbml(:SBase_getPlugin), VPtr, (VPtr, Cstring), sp, "
fbc
") # FbcSpeciesPlugin_t
formula = nothing
charge = nothing
if sp_fbc != C_NULL
...
...
@@ -167,13 +219,33 @@ function extractModel(mdl::VPtr)::Model
charge = ccall(sbml(:FbcSpeciesPlugin_getCharge), Cint, (VPtr,), sp_fbc)
end
end
ia = nothing
if ccall(sbml(:Species_isSetInitialAmount), Cint, (VPtr,), sp) != 0
ia = (
ccall(sbml(:Species_getInitialAmount), Cdouble, (VPtr,), sp),
get_string(sp, :Species_getSubstanceUnits),
)
end
species[get_string(sp, :Species_getId)] = Species(
get_optional_string(sp, :Species_getName),
get_string(sp, :Species_getCompartment),
get_optional_bool(
sp,
:Species_isSetBoundaryCondition,
:Species_getBoundaryCondition,
),
formula,
charge,
getNotes(sp),
getAnnotation(sp),
ia,
get_optional_bool(
sp,
:Species_isSetHasOnlySubstanceUnits,
:Species_getHasOnlySubstanceUnits,
),
get_notes(sp),
get_annotation(sp),
)
end
...
...
@@ -204,6 +276,7 @@ function extractModel(mdl::VPtr)::Model
lb = (-Inf, "") # (bound value, unit id)
ub = (Inf, "")
oc = 0.0
math = nothing
# kinetic laws store a second version of the bounds and objectives
kl = ccall(sbml(:Reaction_getKineticLaw), VPtr, (VPtr,), re)
...
...
@@ -221,6 +294,10 @@ function extractModel(mdl::VPtr)::Model
oc = pval()
end
end
if ccall(sbml(:KineticLaw_isSetMath), Cint, (VPtr,), kl) != 0
math = parse_math(ccall(sbml(:KineticLaw_getMath), VPtr, (VPtr,), kl))
end
end
# TRICKY: SBML spec is completely silent about what should happen if
...
...
@@ -260,6 +337,7 @@ function extractModel(mdl::VPtr)::Model
add_stoi(sr, 1)
end
# gene product associations
association = nothing
if re_fbc != C_NULL
gpa = ccall(
...
...
@@ -282,8 +360,9 @@ function extractModel(mdl::VPtr)::Model
ub,
haskey(objectives_fbc, reid) ? objectives_fbc[reid] : oc,
association,
getNotes(re),
getAnnotation(re),
math,
get_notes(re),
get_annotation(re),
)
end
...
...
@@ -305,13 +384,30 @@ function extractModel(mdl::VPtr)::Model
gene_products[id] = GeneProduct(
get_optional_string(gp, :GeneProduct_getName),
get_optional_string(gp, :GeneProduct_getLabel),
get
N
otes(gp),
get
A
nnotation(gp),
get
_n
otes(gp),
get
_a
nnotation(gp),
)
end
end
end
function_definitions = Dict{String,FunctionDefinition}()
for i = 1:ccall(sbml(:Model_getNumFunctionDefinitions), Cuint, (VPtr,), mdl)
fd = ccall(sbml(:Model_getFunctionDefinition), VPtr, (VPtr, Cuint), mdl, i - 1)
def = nothing
if ccall(sbml(:FunctionDefinition_isSetMath), Cint, (VPtr,), fd) != 0
def = parse_math(ccall(sbml(:FunctionDefinition_getMath), VPtr, (VPtr,), fd))
end
function_definitions[get_string(fd, :FunctionDefinition_getId)] =
FunctionDefinition(
get_optional_string(fd, :FunctionDefinition_getName),
def,
get_notes(fd),
get_annotation(fd),
)
end
return Model(
parameters,
units,
...
...
@@ -319,7 +415,8 @@ function extractModel(mdl::VPtr)::Model
species,
reactions,
gene_products,
getNotes(mdl),
getAnnotation(mdl),
function_definitions,
get_notes(mdl),
get_annotation(mdl),
)
end
src/structs.jl
View file @
06a4db5c
...
...
@@ -55,6 +55,54 @@ struct GPAOr <: GeneProductAssociation
terms
::
Vector
{
GeneProductAssociation
}
end
"""
A simplified representation of MathML-specified math AST
"""
abstract type
Math
end
"""
A literal value (usually a numeric constant) in mathematical expression
"""
struct
MathVal
{
T
}
<:
Math
where
{
T
}
val
::
T
end
"""
An identifier (usually a variable name) in mathematical expression
"""
struct
MathIdent
<:
Math
id
::
String
end
"""
Function application ("
call
by
name
", no tricks allowed) in mathematical expression
"""
struct
MathApply
<:
Math
fn
::
String
args
::
Vector
{
Math
}
end
"""
Function definition (aka "
lambda
") in mathematical expression
"""
struct
MathLambda
<:
Math
args
::
Vector
{
String
}
body
::
Math
end
"""
SBML Compartment with sizing information.
"""
struct
Compartment
name
::
Maybe
{
String
}
constant
::
Maybe
{
Bool
}
spatial_dimensions
::
Maybe
{
Int
}
size
::
Maybe
{
Float64
}
units
::
Maybe
{
String
}
notes
::
Maybe
{
String
}
annotation
::
Maybe
{
String
}
Compartment
(
na
,
c
,
sd
,
s
,
u
,
no
=
nothing
,
an
=
nothing
)
=
new
(
na
,
c
,
sd
,
s
,
u
,
no
,
an
)
end
"""
Reaction with stoichiometry that assigns reactants and products their relative
...
...
@@ -68,9 +116,10 @@ struct Reaction
ub
::
Tuple
{
Float64
,
String
}
oc
::
Float64
gene_product_association
::
Maybe
{
GeneProductAssociation
}
kinetic_math
::
Maybe
{
Math
}
notes
::
Maybe
{
String
}
annotation
::
Maybe
{
String
}
Reaction
(
s
,
l
,
u
,
o
,
as
,
n
=
nothing
,
an
=
nothing
)
=
new
(
s
,
l
,
u
,
o
,
as
,
n
,
an
)
Reaction
(
s
,
l
,
u
,
o
,
as
,
km
,
n
=
nothing
,
an
=
nothing
)
=
new
(
s
,
l
,
u
,
o
,
as
,
km
,
n
,
an
)
end
"""
...
...
@@ -80,11 +129,15 @@ identifier, `formula`, `charge`, and additional `notes` and `annotation`.
struct
Species
name
::
Maybe
{
String
}
compartment
::
String
boundary_condition
::
Maybe
{
Bool
}
formula
::
Maybe
{
String
}
charge
::
Maybe
{
Int
}
initial_amount
::
Maybe
{
Tuple
{
Float64
,
String
}}
only_substance_units
::
Maybe
{
Bool
}
notes
::
Maybe
{
String
}
annotation
::
Maybe
{
String
}
Species
(
na
,
co
,
f
,
ch
,
no
=
nothing
,
a
=
nothing
)
=
new
(
na
,
co
,
f
,
ch
,
no
,
a
)
Species
(
na
,
co
,
b
,
f
,
ch
,
ia
,
osu
,
no
=
nothing
,
a
=
nothing
)
=
new
(
na
,
co
,
b
,
f
,
ch
,
ia
,
osu
,
no
,
a
)
end
"""
...
...
@@ -98,6 +151,17 @@ struct GeneProduct
GeneProduct
(
na
,
l
,
no
=
nothing
,
a
=
nothing
)
=
new
(
na
,
l
,
no
,
a
)
end
"""
Custom function definition.
"""
struct
FunctionDefinition
name
::
Maybe
{
String
}
body
::
Maybe
{
Math
}
notes
::
Maybe
{
String
}
annotation
::
Maybe
{
String
}
FunctionDefinition
(
na
,
b
,
no
=
nothing
,
a
=
nothing
)
=
new
(
na
,
b
,
no
,
a
)
end
"""
Structure that collects the model-related data. Contains `parameters`, `units`,
`compartments`, `species` and `reactions` and `gene_products`, and additional
...
...
@@ -108,11 +172,12 @@ objects.
struct
Model
parameters
::
Dict
{
String
,
Float64
}
units
::
Dict
{
String
,
Vector
{
UnitPart
}}
compartments
::
Vector
{
String
}
compartments
::
Dict
{
String
,
Compartment
}
species
::
Dict
{
String
,
Species
}
reactions
::
Dict
{
String
,
Reaction
}
gene_products
::
Dict
{
String
,
GeneProduct
}
function_definitions
::
Dict
{
String
,
FunctionDefinition
}
notes
::
Maybe
{
String
}
annotation
::
Maybe
{
String
}
Model
(
p
,
u
,
c
,
s
,
r
,
g
,
n
=
nothing
,
a
=
nothing
)
=
new
(
p
,
u
,
c
,
s
,
r
,
g
,
n
,
a
)
Model
(
p
,
u
,
c
,
s
,
r
,
g
,
f
,
n
=
nothing
,
a
=
nothing
)
=
new
(
p
,
u
,
c
,
s
,
r
,
g
,
f
,
n
,
a
)
end
cylon-x
🤖
@cylon-x
mentioned in commit
6b844ae9
·
Apr 16, 2021
mentioned in commit
6b844ae9
mentioned in commit 6b844ae9a5d67b34a69d4d1fe220371327916d9a
Toggle commit list
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment