diff --git a/Project.toml b/Project.toml index 0e5287b..4065d98 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CellMLToolkit" uuid = "03cb29e0-1ef4-4721-aa24-cf58a006576f" authors = ["Shahriar Iravanian "] -version = "2.1.1" +version = "2.1.0" [deps] EzXML = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615" @@ -12,16 +12,17 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] EzXML = "1.1" +JSON3 = "1.8" MathML = "0.1" ModelingToolkit = "5.13" SymbolicUtils = "0.9" julia = "1.5" [extras] -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" +JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "ModelingToolkit", "DifferentialEquations", "Plots"] +test = ["Test", "ModelingToolkit", "OrdinaryDiffEq", "JSON3"] diff --git a/src/CellMLToolkit.jl b/src/CellMLToolkit.jl index 05b8760..0483b22 100644 --- a/src/CellMLToolkit.jl +++ b/src/CellMLToolkit.jl @@ -6,6 +6,9 @@ using SymbolicUtils: FnType, Sym, operation, arguments using ModelingToolkit using EzXML +include("utils.jl") +export curl_exposures + include("cellml.jl") """ @@ -97,7 +100,7 @@ end val is the new value """ function update_list!(l, sym::Symbol, val) - for (i,x) in enumerate(l) + for (i, x) in enumerate(l) if first(x).name == sym l[i] = (first(x) => val) return diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 0000000..b972e83 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1,23 @@ +"""queries the cellml repo api for links to cellml model repo""" +function curl_exposures() + run(`curl -sL -H 'Accept: application/vnd.physiome.pmr2.json.1' + https://models.physiomeproject.org/search -d '{ + "template": {"data": [ + {"name": "Subject", "value": "CellML Model"}, + {"name": "portal_type", "value": "ExposureFile"} + ]} + }' -o cellml.json`) +end + +""" +downloads the cellml model repository +todo use Base.Downloads to speed this up +""" +function grab(ls) + !ispath("data") && mkpath("data") + @sync Threads.@threads for l in ls + fn = splitdir(l)[end] + download(l, "data/$(fn)") + end + nothing +end diff --git a/test/beeler.jl b/test/beeler.jl new file mode 100644 index 0000000..172eefa --- /dev/null +++ b/test/beeler.jl @@ -0,0 +1,32 @@ +path = @__DIR__ +ml = CellModel(path * "/../models/beeler_reuter_1977.cellml.xml") + +# @test length(ml.eqs) == 8 +# @test ml.iv.op.name == :time + +# eqs, vs = CellMLToolkit.flat_equations(ml) +# @test length(vs) == 8 + +# @test find_V(ml).op.name == :V + +prob = ODEProblem(ml, (0,10000.0)) +sol1 = solve(prob, Euler(), dt=0.01, saveat=1.0) +sol2 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) +V1 = map(x -> x[1], sol1.u) +V2 = map(x -> x[1], sol2.u) +err = sum(abs.(V1 .- V2)) / length(V1) +@test err < 0.1 + +# prob = ODEProblem(ml, (0,10000.0); jac=true) +# sol3 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) +# V3 = map(x -> x[1], sol2.u) +# err = sum(abs.(V1 .- V3)) / length(V1) +# @test err < 0.1 + +p = list_params(ml) +update_list!(p, "IstimPeriod", 280.0) +prob = ODEProblem(ml, (0,10000.0); jac=false, p=p) +sol4 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) +V4 = map(x -> x[1], sol2.u) +err = sum(abs.(V1[1:250] .- V4[1:250])) / 250 +@test err < 0.1 diff --git a/test/runtests.jl b/test/runtests.jl index 9e9a899..6b8413d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,36 +1,12 @@ using Test using CellMLToolkit -using DifferentialEquations +using OrdinaryDiffEq +using JSON3, Base.Threads +using ModelingToolkit -path = @__DIR__ -ml = CellModel(path * "/../models/beeler_reuter_1977.cellml.xml") +@testset "CellMLToolkit.jl" begin + @testset "beeler.jl" begin include("beeler.jl") end -# @test length(ml.eqs) == 8 -# @test ml.iv.op.name == :time - -# eqs, vs = CellMLToolkit.flat_equations(ml) -# @test length(vs) == 8 - -# @test find_V(ml).op.name == :V - -prob = ODEProblem(ml, (0,10000.0)) -sol1 = solve(prob, Euler(), dt=0.01, saveat=1.0) -sol2 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) -V1 = map(x -> x[1], sol1.u) -V2 = map(x -> x[1], sol2.u) -err = sum(abs.(V1 .- V2)) / length(V1) -@test err < 0.1 - -# prob = ODEProblem(ml, (0,10000.0); jac=true) -# sol3 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) -# V3 = map(x -> x[1], sol2.u) -# err = sum(abs.(V1 .- V3)) / length(V1) -# @test err < 0.1 - -p = list_params(ml) -update_list!(p, "IstimPeriod", 280.0) -prob = ODEProblem(ml, (0,10000.0); jac=false, p=p) -sol4 = solve(prob, TRBDF2(), dtmax=0.5, saveat=1.0) -V4 = map(x -> x[1], sol2.u) -err = sum(abs.(V1[1:250] .- V4[1:250])) / 250 -@test err < 0.1 + # mainly a used as a bin/ don't want to test every PR + # @testset "physiome.jl" begin include("test_physiome.jl") end +end \ No newline at end of file diff --git a/test/test_physiome.jl b/test/test_physiome.jl new file mode 100644 index 0000000..3f236c0 --- /dev/null +++ b/test/test_physiome.jl @@ -0,0 +1,63 @@ +" +in place modifies a matrix with data about how far to `solve` +we get for a given set of cellml files +uses @threads +" +function test_cellmls!(mat, fns) + @sync Threads.@threads for i in eachindex(fns) + @show i fns[i] + mat[i, :] = test_cellml!(mat[i, :], fns[i]) + end + mat +end + +" +in place modifies a row with data about how far to `solve` +we get for a given cellml file +" +function test_cellml!(row, fn) + row[1] = fn + try + ml = CellModel(fn) + sys = ml.sys + row[2] = true + row[5] = length(states(sys)) + row[6] = length(parameters(sys)) + prob = ODEProblem(ml, tspan) + row[3] = true + sol = solve(prob) + row[4] = true + catch e + row[end] = e + end + row +end + +""" +a +""" +function main(dir="data/") + tspan = (0., 1.) + fns = readdir(dir; join=true) + names = [:filename, :to_system, :to_problem, :to_solve, :states, :parameters, :error] + n = length(names) + mat = Array{Any,2}(nothing, length(fns), n) + test_cellmls!(mat, fns) + replace!(mat, nothing => missing) + names .=> eachcol(mat) # used with DataFrame() + # CSV.write("aggregate_stats.csv", df) + # print(df) +end + +function json_to_cellml_links() + s = read("cellml.json", String); + j = JSON3.read(s); + x = j.collection.links + map(x -> x.href[1:end - 5], x) # remove `/view` from urls +end + +ls = json_to_cellml_links(curl_exposures()) +@test length(ls) == 2379 +CellMLToolkit.grab(ls[1:10]) +df = main() +@test eltype(df) == Pair