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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + mwbb21d5b0_5b44_4d2f_8d51_1aaff3f8bf6d + mw31016a43_49a9_41bf_8a3f_346484470a6e + + + + + + + + + + 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