From 0db6bf3444cd6d2bfd61b02f12b488a97755c6b9 Mon Sep 17 00:00:00 2001 From: anand jain <anandj@uchicago.edu> Date: Wed, 18 Jan 2023 16:52:37 -0500 Subject: [PATCH] basic nameswapping utility for SimBiology --- Project.toml | 1 + src/SBMLToolkit.jl | 2 ++ src/nameswap.jl | 72 ++++++++++++++++++++++++++++++++++++++ test/data/simpleModel.sbml | 39 +++++++++++++++++++++ test/nameswap.jl | 13 +++++++ test/runtests.jl | 1 + 6 files changed, 128 insertions(+) create mode 100644 src/nameswap.jl create mode 100644 test/data/simpleModel.sbml create mode 100644 test/nameswap.jl diff --git a/Project.toml b/Project.toml index ab80221..161fb88 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.1.19" Catalyst = "479239e8-5488-4da2-87a7-35f2df7eef83" MathML = "abcecc63-2b08-419c-80c4-c63dca6fa478" SBML = "e5567a89-2604-4b09-9718-f5f78e97c3bb" +Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" [compat] diff --git a/src/SBMLToolkit.jl b/src/SBMLToolkit.jl index 897094b..019e88b 100644 --- a/src/SBMLToolkit.jl +++ b/src/SBMLToolkit.jl @@ -3,12 +3,14 @@ module SBMLToolkit using Catalyst using SBML using SymbolicUtils +using Setfield include("systems.jl") include("reactions.jl") include("rules.jl") include("events.jl") include("utils.jl") +include("nameswap.jl") export ReactionSystem, ODESystem export readSBML, readSBMLFromString, set_level_and_version, convert_simplify_math diff --git a/src/nameswap.jl b/src/nameswap.jl new file mode 100644 index 0000000..613a88b --- /dev/null +++ b/src/nameswap.jl @@ -0,0 +1,72 @@ +function swap_id_name(k, v; prop = :name) + new_prop = getproperty(v, prop) + @set! v.$prop = k + new_prop, v +end + +function replace_math_ident(id_name_dict, node) + if typeof(node) == SBML.MathIdent + return SBML.MathIdent(id_name_dict[node.id]) + elseif typeof(node) == SBML.MathApply + return SBML.MathApply(node.fn, replace_math_ident(id_name_dict, node.args)) + elseif isa(node, AbstractArray) + return map(x -> replace_math_ident(id_name_dict, x), node) + else + return node + end +end + +"SimBiology makes the names of everything a hashed ID, but keeps the ID in the name" +function fix_simbio_names(m::SBML.Model) + cid_name_d = Dict(keys(m.compartments) .=> getproperty.(values(m.compartments), :name)) + pid_name_d = Dict(keys(m.parameters) .=> getproperty.(values(m.parameters), :name)) + rid_name_d = Dict(keys(m.reactions) .=> getproperty.(values(m.reactions), :name)) + + for prop in [:compartments, :parameters, :reactions] + xs = getproperty(m, prop) + new_xs = Dict() + for (k, v) in xs + nk, nv = swap_id_name(k, v) + new_xs[nk] = nv + end + @set! m.$prop = new_xs + end + + new_ss = Dict() + for (k, v) in m.species + nk, nv = swap_id_name(k, v) + cname = cid_name_d[v.compartment] + # for species, we want to concat the name and compartment name like in simbiology and MTK + sname = string(cname, "₊", nk) + @set! nv.compartment = cname + new_ss[sname] = nv + end + @set! m.species = new_ss + + sid_name_d = Dict(reverse.(keys(new_ss) .=> getproperty.(values(new_ss), :name))) + + ds = [cid_name_d, pid_name_d, rid_name_d, sid_name_d] + slen = sum(length.(ds)) + id_name_dict = merge(ds...) + @assert slen == length(id_name_dict) + + for (k, v) in m.reactions + for (i, sr) in enumerate(v.reactants) + @set! sr.species = id_name_dict[sr.species] + m.reactions[k].reactants[i] = sr + end + for (i, sr) in enumerate(v.products) + @set! sr.species = id_name_dict[sr.species] + m.reactions[k].products[i] = sr + end + @set! m.reactions[k].kinetic_math = replace_math_ident(id_name_dict, v.kinetic_math) + end + + for (i, r) in enumerate(m.rules) + @set! m.rules[i].variable = id_name_dict[r.variable] + new_tree = replace_math_ident(id_name_dict, r.math) + @set! m.rules[i].math = new_tree + end + + m +end diff --git a/test/data/simpleModel.sbml b/test/data/simpleModel.sbml new file mode 100644 index 0000000..c2a20e6 --- /dev/null +++ b/test/data/simpleModel.sbml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<sbml xmlns="http://www.sbml.org/sbml/level2/version4" level="2" version="4"> + <annotation> + <SimBiology xmlns="http://www.mathworks.com"> + <Version Major="6" Minor="4" Point="0"/> + </SimBiology> + </annotation> + <model id="mw97c68eca_aecf_484b_b128_48c812a0c25c" name="simpleModel"> + <listOfCompartments> + <compartment id="mwfb3bbd58_15e9_4f26_9be2_0c1221191847" name="unnamed" size="1" constant="true"/> + </listOfCompartments> + <listOfSpecies> + <species id="mw31016a43_49a9_41bf_8a3f_346484470a6e" name="A" compartment="mwfb3bbd58_15e9_4f26_9be2_0c1221191847" initialConcentration="10" boundaryCondition="false" constant="false"/> + <species id="mw6966d9de_9719_43d5_8fbe_40a117eee9c7" name="B" compartment="mwfb3bbd58_15e9_4f26_9be2_0c1221191847" initialConcentration="0" boundaryCondition="false" constant="false"/> + </listOfSpecies> + <listOfReactions> + <reaction id="mw8998ca1f_76ee_4090_948e_b455153beda5" name="Reaction_1" reversible="false"> + <listOfReactants> + <speciesReference species="mw31016a43_49a9_41bf_8a3f_346484470a6e" stoichiometry="1"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="mw6966d9de_9719_43d5_8fbe_40a117eee9c7" stoichiometry="1"/> + </listOfProducts> + <kineticLaw> + <math xmlns="http://www.w3.org/1998/Math/MathML"> + <apply> + <times/> + <ci> mwbb21d5b0_5b44_4d2f_8d51_1aaff3f8bf6d </ci> + <ci> mw31016a43_49a9_41bf_8a3f_346484470a6e </ci> + </apply> + </math> + <listOfParameters> + <parameter id="mwbb21d5b0_5b44_4d2f_8d51_1aaff3f8bf6d" name="k" value="0.5" constant="true"/> + </listOfParameters> + </kineticLaw> + </reaction> + </listOfReactions> + </model> +</sbml> diff --git a/test/nameswap.jl b/test/nameswap.jl new file mode 100644 index 0000000..37c0328 --- /dev/null +++ b/test/nameswap.jl @@ -0,0 +1,13 @@ +using SBML + +sbml_fn = joinpath(@__DIR__, "data/simpleModel.sbml") + +m_ = readSBML(sbml_fn, doc -> begin + set_level_and_version(3, 2)(doc) + convert_simplify_math(doc) +end) + +m = deepcopy(m_) +m2 = SBML.fix_simbio_names(m) + +@test keys(m2.species) == Set(["unnamed₊A", "unnamed₊B"]) diff --git a/test/runtests.jl b/test/runtests.jl index 545d195..9737dda 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,4 +8,5 @@ using SafeTestsets, Test @safetestset "Utils" begin include("utils.jl") end @safetestset "Simulation results" begin include("simresults.jl") end @safetestset "Wuschel" begin include("wuschel.jl") end + @safetestset "Nameswap" begin include("nameswap.jl") end end