From e9fb84e417c0d8e6725c5842e026d6813fa6ee35 Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Tue, 11 Jul 2023 11:56:37 +0200 Subject: [PATCH 01/23] improved import (speed) --- src/export/generated.jl | 51 +++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/export/generated.jl b/src/export/generated.jl index ad64069..6462425 100644 --- a/src/export/generated.jl +++ b/src/export/generated.jl @@ -262,7 +262,8 @@ module ExportUtils # defaults for centerpos: transform into 2D array for export exportcollect(::Type{<:AbstractParticle}, ::FieldExport{:centerpos}) = :(InPartS.collectvectors(vector)) - importcollect(::Type{<:AbstractParticle}, ::FieldExport{:centerpos}) = :([InPartS.SVector{size(blob)[2], Float64}(blob[i,:]) for i ∈ 1:size(blob)[1]]) #TODO: faster‽ + importcollect(::Type{<:AbstractParticle{2}}, ::FieldExport{:centerpos}) = :(@views InPartS.SVector{2, Float64}.(blob[:, 1], blob[:, 2])) + importcollect(::Type{<:AbstractParticle{3}}, ::FieldExport{:centerpos}) = :(@views InPartS.SVector{3, Float64}.(blob[:, 1], blob[:, 2], blob[:, 3])) # default names (trivial for FieldExport, irrelevant for AdditionalExport) exportname(::Type, ::FieldExport{S}) where S = string(S) @@ -790,7 +791,6 @@ the export system, consult the InPartS manual. _deserialize_function(T::Type) = quote function ExportUtils.deserialize(group, type::Type{<:$T}; sim::Union{Simulation, Nothing} = nothing) $(_deserialize_block(T)) - return items end end @@ -822,41 +822,48 @@ function _deserialize_block(T::Type, ::ScalarExport) end constructor = Expr(:call, :(ExportUtils.importconstruct), datakwargs, :type) - return :(items = $constructor) + return :($constructor) end function _deserialize_block(T::Type, ::VectorExport) - datakwargs = Expr(:parameters) inames = Expr(:parameters) - + inamelist = Symbol[] + assignments = [] for e ∈ allexports(T) iname = ExportUtils.importname(T, e) source = :(group[$(ExportUtils.exportname(T, e))]) collect = ExportUtils.importcollect(T, e) transform = ExportUtils.importtransform(T, e) - push!(datakwargs.args, Expr( - :kw, - iname, - quote - blob = $source - datavec = $collect - values = map(data->$transform, datavec) - end - )) - - push!(inames.args, Expr(:kw, iname, :(data.$iname[i]))) + push!(inames.args, Expr(:kw, iname, :($iname[i]))) + push!(inamelist, iname) + push!(assignments, + Expr(Symbol("="), iname, + if transform == :data + quote + blob = $source + $collect + end + else + quote + blob = $source + datavec = $collect + map(data->$transform, datavec) + end + end)) end - datatuple = Expr(:tuple, datakwargs) return quote # all the importing happens here! - data = $datatuple - nitems = length(data[1]) - items = Vector{type}(undef, nitems) - for i ∈ 1:nitems - items[i] = ExportUtils.importconstruct($inames, type) + $(assignments...) + items = Vector{type}(undef, length($(inamelist[1]))) + lambda = function(items, $(inamelist...)) + @inbounds for i ∈ eachindex(items) + items[i] = ExportUtils.importconstruct($inames, eltype(items)) + end end + lambda(items, $(inamelist...)) + return items end end -- GitLab From 87e848c7c32c7f42608a423e5ff585084b8a42d6 Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Tue, 11 Jul 2023 11:56:55 +0200 Subject: [PATCH 02/23] empty internalforces for force extraction --- src/particles.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/particles.jl b/src/particles.jl index 1de15cb..e1bc496 100644 --- a/src/particles.jl +++ b/src/particles.jl @@ -68,7 +68,7 @@ Can be used to compute forces that do not depend on the state of other particles Forces should be stored within the particle struct. Any values returned by this function are discarded. """ -function internalforces!(p::AbstractParticle, s::Simulation) end +@forcedef function internalforces!(p::AbstractParticle, s::Simulation) end -- GitLab From 69905778a4226206ddeb7741513c8fa7f36d950b Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Mon, 24 Jul 2023 10:02:04 +0000 Subject: [PATCH 03/23] Fix ParamType(SpheroidDiskCell) --- src/InPartS.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/InPartS.jl b/src/InPartS.jl index 908dc4a..0518a43 100644 --- a/src/InPartS.jl +++ b/src/InPartS.jl @@ -105,9 +105,12 @@ Base.ndims(::AbstractParticleContainer{N}) where {N} = N Returns the parameter type for particles of type `PT`. """ function ParamType(T::Type{<:AbstractParticle}) + T isa UnionAll && return ParamType(T.body) i = findfirst(==(:params), fieldnames(T)) - isnothing(i) && throw(TypeError("Type $T has no field params.")) - return T.types[i] + isnothing(i) && error("Type $T has no field params.") + P = T.types[i] + P isa DataType || error("Failed to determine parameter type for type $T.") + return P end ## Includes -- GitLab From 2c811cb81e5629592470c2403177a58aedec4d27 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Mon, 24 Jul 2023 10:16:09 +0000 Subject: [PATCH 04/23] export some accessors --- src/particlecontainers/simpleparticlecontainer.jl | 3 +-- src/simulation.jl | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/particlecontainers/simpleparticlecontainer.jl b/src/particlecontainers/simpleparticlecontainer.jl index 0d65d00..d582f6e 100644 --- a/src/particlecontainers/simpleparticlecontainer.jl +++ b/src/particlecontainers/simpleparticlecontainer.jl @@ -1,5 +1,4 @@ -export SimpleParticleContainer, - hasobstacles, particles, obstacles +export SimpleParticleContainer hasobstacles(::Type{<:AbstractParticleContainer}) = false diff --git a/src/simulation.jl b/src/simulation.jl index c7ed78c..1a1000c 100644 --- a/src/simulation.jl +++ b/src/simulation.jl @@ -1,4 +1,5 @@ -export Simulation +export Simulation, current_time, current_step, particles, obstacles, particletype, hasobstacles + mutable struct Simulation{ PTC<:AbstractParticleContainer, @@ -170,8 +171,12 @@ Returns the particle type of the simulation """ particletype(sim::Simulation) = particletype(sim.particles) - """ + hasobstacles(sim) +Returns true if the simulation can contain obstacles and [`obstacles`](@sim) can be safely called +""" +hasobstacles(sim) = hasobstacles(sim.particles) + reset_clock!(sim::Simulation) Set simulation time to zero """ -- GitLab From 79371516a4c851f1df05b9b2a9060f408b3b152e Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Tue, 25 Jul 2023 14:58:24 +0000 Subject: [PATCH 05/23] Tovectors --- src/export/generated.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/export/generated.jl b/src/export/generated.jl index 6462425..5f80652 100644 --- a/src/export/generated.jl +++ b/src/export/generated.jl @@ -262,6 +262,7 @@ module ExportUtils # defaults for centerpos: transform into 2D array for export exportcollect(::Type{<:AbstractParticle}, ::FieldExport{:centerpos}) = :(InPartS.collectvectors(vector)) + importcollect(::Type{<:AbstractParticle}, ::FieldExport{:centerpos}) = :(InPartS.tosvectors(blob)) importcollect(::Type{<:AbstractParticle{2}}, ::FieldExport{:centerpos}) = :(@views InPartS.SVector{2, Float64}.(blob[:, 1], blob[:, 2])) importcollect(::Type{<:AbstractParticle{3}}, ::FieldExport{:centerpos}) = :(@views InPartS.SVector{3, Float64}.(blob[:, 1], blob[:, 2], blob[:, 3])) @@ -925,3 +926,14 @@ function collectvectors(vs::AbstractVector{SVector{D, T}}) where {D, T} return out end + +function tosvectors(blob) + N = size(blob, 2) + if N == 2 + return @views SVector{2, Float64}.(blob[:, 1], blob[:, 2]) + elseif N == 3 + return @views SVector{3, Float64}.(blob[:, 1], blob[:, 2], blob[:, 3]) + else + return @views SVector{N,Float64}.((blob[:,i] for i=1:N)...) + end +end -- GitLab From 6ed87f0cad42cb262327ac6334e321edfaa0723f Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Wed, 26 Jul 2023 10:51:32 +0000 Subject: [PATCH 06/23] whoops --- src/simulation.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simulation.jl b/src/simulation.jl index 1a1000c..f7f1e87 100644 --- a/src/simulation.jl +++ b/src/simulation.jl @@ -177,6 +177,7 @@ Returns true if the simulation can contain obstacles and [`obstacles`](@sim) can """ hasobstacles(sim) = hasobstacles(sim.particles) +""" reset_clock!(sim::Simulation) Set simulation time to zero """ -- GitLab From ffafd58034c655ad2fbb299b4eb66195a92485c5 Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Wed, 26 Jul 2023 11:14:22 +0000 Subject: [PATCH 07/23] add a new forcecontribution function --- src/deprecations.jl | 30 ++++++++++++++++++++++++++++++ src/extras/forceextraction.jl | 25 +++++++++++-------------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/deprecations.jl b/src/deprecations.jl index cc5b800..64a077b 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -5,3 +5,33 @@ # there is a kwarg-only deprecation in the SimpleAdaptiveStepper constructor that cannot be moved here @deprecate latest_full_snap(f) lastfullsnap(f) + + +# HACK: this is not nice +""" + forcetuple(r::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} + forcetuple(r::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} +Returns a named tuple containing the individual components of `r`, `f` and `δ`. + +In the 2D case, the names of the tuple entries are `rx`, `ry`, `fx`, `fy` and `δx`, `δy`; +in 3D, the additional entries are called `rz`, `fz` and `δz` respectively + +DEPRECATED! Use [`forcecontribution`](@ref) instead. +""" +function forcetuple(r::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} + Base.depwarn("forcetuple was used for a semantically suboptimal force extraction. Use forcecontribution instead", :forcetuple) + return ( + rx = r[1], ry = r[2], + fx = f[1], fy = f[2], + δx = δ[1], δy = δ[2] + ) +end + +function forcetuple(r::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} + Base.depwarn("forcetuple was used for a semantically suboptimal force extraction. Use forcecontribution instead", :forcetuple) + return ( + rx = r[1], ry = r[2], rz = r[3], + fx = f[1], fy = f[2], fz = f[3], + δx = δ[1], δy = δ[2], δz = δ[3] + ) +end diff --git a/src/extras/forceextraction.jl b/src/extras/forceextraction.jl index 48c3e54..9c7a223 100644 --- a/src/extras/forceextraction.jl +++ b/src/extras/forceextraction.jl @@ -1,6 +1,6 @@ using MacroTools -export @force, @forcedef, @forcefun +export @force, @forcedef, @forcefun, forcecontribution """ @@ -101,30 +101,27 @@ macro forcedef(ex) end end - - -# HACK: this is not nice """ - forcetuple(r::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} - forcetuple(r::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} -Returns a named tuple containing the individual components of `r`, `f` and `δ`. + forcecontribution(c::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} + forcecontribution(c::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} +Returns a named tuple containing the individual components of `c`, `f` and `δ`. -In the 2D case, the names of the tuple entries are `rx`, `ry`, `fx`, `fy` and `δx`, `δy`; -in 3D, the additional entries are called `rz`, `fz` and `δz` respectively +In the 2D case, the names of the tuple entries are `cx`, `cy`, `fx`, `fy` and `δx`, `δy`; +in 3D, the additional entries are called `cz`, `fz` and `δz` respectively -Useful for dipole accumulation using [`@forcedef`](@ref). +Useful for force accumulation using [`@forcedef`](@ref). """ -function forcetuple(r::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} +function forcecontribution(c::SVector{2, T}, f::SVector{2, T}, δ::SVector{2, T}) where {T} return ( - rx = r[1], ry = r[2], + cx = c[1], cy = c[2], fx = f[1], fy = f[2], δx = δ[1], δy = δ[2] ) end -function forcetuple(r::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} +function forcecontribution(c::SVector{3, T}, f::SVector{3, T}, δ::SVector{3, T}) where {T} return ( - rx = r[1], ry = r[2], rz = r[3], + cx = c[1], cy = c[2], cz = c[3], fx = f[1], fy = f[2], fz = f[3], δx = δ[1], δy = δ[2], δz = δ[3] ) -- GitLab From a83e3537ef2e1fe3825a3dc87abdb2eac2b9c72b Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 13:33:45 +0200 Subject: [PATCH 08/23] kill pirates --- src/export/generic.jl | 4 +-- src/export/jld2wrapper.jl | 61 +++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/export/generic.jl b/src/export/generic.jl index b7db702..dbad0df 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -15,7 +15,7 @@ function backend(filename::String) error("File appears to be a $modulename file but library is not loaded.") end if modulename == :JLD2 - return JLD2.JLDFile + return JLD2Wrapper elseif modulename == :HDF5 return H5Wrapper else @@ -927,7 +927,7 @@ function write_sim(filename, sim::Simulation; dumprng = true) Base.require(Main, modulename) return Base.invokelatest(write_sim, filename, sim; dumprng) elseif modulename == :JLD2 - FT = JLD2.JLDFile + FT = JLD2Wrapper elseif modulename == :HDF5 FT = H5Wrapper else diff --git a/src/export/jld2wrapper.jl b/src/export/jld2wrapper.jl index 3d640d9..a92f1a3 100644 --- a/src/export/jld2wrapper.jl +++ b/src/export/jld2wrapper.jl @@ -1,24 +1,41 @@ +export JLD2Wrapper using .JLD2 + +const JLD2Obj = Union{JLD2.JLDFile, JLD2.Group} + +struct JLD2Wrapper{T<:JLD2Obj} + _handle::T +end + +function Base.getindex(j2w::JLD2Wrapper, key) + item = j2w._handle[key] + return item isa JLD2Obj ? JLD2Wrapper(item) : item +end + +Base.get(j2w::JLD2Wrapper, key, default) = get(j2w._handle, key, default) + +Base.setindex!(j2w::JLD2Wrapper, val, key) = setindex!(j2w._handle, val, key) +# For backwards compatibility with HDF5 write all SArrays as Arrays +Base.setindex!(j2w::JLD2Wrapper, value::SArray, key::String) = setindex!(j2w, collect(value), key) +# use writedict for recursive dicts +Base.setindex!(j2w::JLD2Wrapper, value::Dict{String, Any}, key::String) = InPartS.writedict(j2w, value; name = key) + + # InPartS IO Backend API -dfopen(type::Type{JLD2.JLDFile}, filename::String, mode::String = "r") = JLD2.jldopen(filename, mode) +InPartS.dfopen(::Type{<:JLD2Wrapper}, filename::String, mode::String = "r") = JLD2Wrapper(JLD2.jldopen(filename, mode)) -dfclose(f::JLD2.JLDFile) = close(f) -function dfrename(::Type{JLD2.JLDFile}, oldname, newname; force = false) +InPartS.dfclose(j2w::JLD2Wrapper) = close(j2w._handle) + +function InPartS.dfrename(::Type{JLD2Wrapper}, oldname, newname; force = false) cp(oldname, newname; force = force) rm(oldname) end -gcreate(g::Union{JLD2.JLDFile, JLD2.Group}, name::String; kwargs...) = JLD2.Group(g, name; kwargs...) - -isgroup(g::Union{JLD2.JLDFile, JLD2.Group}, key) = (g[key] isa JLD2.Group) -Base.length(g::Union{JLD2.JLDFile, JLD2.Group}) = length(keys(g)) +InPartS.gcreate(g::JLD2Wrapper, name::String; kwargs...) = JLD2.Group(g._handle, name; kwargs...) -# For backwards compatibility with HDF5 write all SArrays as Arrays -Base.setindex!(g::JLD2.Group, value::SArray, key::String) = setindex!(g, collect(value), key) -Base.setindex!(g::JLD2.JLDFile, value::SArray, key::String) = setindex!(g, collect(value), key) -Base.setindex!(g::JLD2.Group, value::Dict{String, Any}, key::String) = InPartS.writedict(g, value; name = key) -Base.setindex!(g::JLD2.JLDFile, value::Dict{String, Any}, key::String) = InPartS.writedict(g, value; name = key) +InPartS.isgroup(g::JLD2Wrapper, key) = (g[key] isa JLD2Wrapper{<:JLD2Group}) +Base.length(g::JLD2Wrapper) = length(keys(g._handle)) @@ -27,7 +44,8 @@ Base.setindex!(g::JLD2.JLDFile, value::Dict{String, Any}, key::String) = InPartS # to preserve nestedness # is faster than generic version in generic.jl because reading via `g[k]` # only happens once -function readdict(g::Union{JLD2.JLDFile, JLD2.Group}; kwargs...) +function InPartS.readdict(j2w::JLD2Wrapper; kwargs...) + g = j2w._handle d = Dict{String, Any}() for k in keys(g) v = g[k] @@ -39,5 +57,18 @@ function readdict(g::Union{JLD2.JLDFile, JLD2.Group}; kwargs...) end return d end - -readdict(d::Dict) = d + +InPartS.readdict(d::Dict) = d + + + +# legacy stuff +InPartS.dfopen(::Type{<:JLD2.JLDFile}, args...; kwargs...) = dfopen(JLD2Wrapper, args...; kwargs...) +InPartS.dfclose(df::JLD2.JLDFile) = dfclose(JLD2Wrapper(df)) +InPartS.dfrename(df::Type{<:JLD2.JLDFile}, args...; kwargs...) = InPartS.dfrename(JLD2Wrapper, args...; kwargs...) + # fully automatic wrapping +for fun ∈ [:readdict, :readdomain, :readobstacles, :readparams, :readrng, :readsim, :readsnap, :readstatic, :readtype, :numsnaps, :lastfullsnap] + @eval InPartS.$(fun)(d::JLD2Obj, args...; kwargs...) = InPartS.$(fun)(JLD2Wrapper(d), args...; kwargs...) +end + +InPartS.readsnap!(sim::Simulation, df::JLD2.JLDFile, args...; kwargs...) = InPartS.readsnap!(sim::Simulation, JLD2Wrapper(df), args...; kwargs...) -- GitLab From fcbfd204e702bb7293f6c0f11777968153f2b7d6 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 13:46:28 +0200 Subject: [PATCH 09/23] maybe resolve ambiguity --- src/export/generic.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/export/generic.jl b/src/export/generic.jl index dbad0df..4a3cded 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -30,9 +30,9 @@ Open the data file at `filename`. Mode specifications follow [`Base.open`](@ref). """ -dfopen(type::Type, filename::String, mode::String) = dfopen(type, filename; modetoflags(mode)...) -dfopen(@nospecialize(type::Type), filename::String; kwargs...) = error("Unknown export backend: $type") -dfopen(filename::String, mode::String="r") = dfopen(backend(filename), filename, mode) +dfopen(type::Type, filename, mode) = dfopen(type, filename; modetoflags(mode)...) +dfopen(@nospecialize(type::Type), filename; kwargs...) = error("Unknown export backend: $type") +dfopen(filename::String, mode="r") = dfopen(backend(filename), filename, mode) function dfopen(f::Function, args...; kwargs...) file = dfopen(args...; kwargs...) -- GitLab From 30fb3e0fc1b5c7f6f5509d03abf4f8114a0576d2 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 13:50:26 +0200 Subject: [PATCH 10/23] missing functions --- src/export/jld2wrapper.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/export/jld2wrapper.jl b/src/export/jld2wrapper.jl index a92f1a3..aca45e4 100644 --- a/src/export/jld2wrapper.jl +++ b/src/export/jld2wrapper.jl @@ -21,6 +21,8 @@ Base.setindex!(j2w::JLD2Wrapper, value::SArray, key::String) = setindex!(j2w, co # use writedict for recursive dicts Base.setindex!(j2w::JLD2Wrapper, value::Dict{String, Any}, key::String) = InPartS.writedict(j2w, value; name = key) +Base.keys(j2w::JLD2Wrapper) = keys(j2w._handle) +Base.haskey(j2w::JLD2Wrapper, k) = haskey(j2w._handle, k) # InPartS IO Backend API InPartS.dfopen(::Type{<:JLD2Wrapper}, filename::String, mode::String = "r") = JLD2Wrapper(JLD2.jldopen(filename, mode)) -- GitLab From f4f80e8f82aa7e8f4f06b38476001327a0f5e9e7 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 15:26:42 +0200 Subject: [PATCH 11/23] more fixes --- src/export/jld2wrapper.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/export/jld2wrapper.jl b/src/export/jld2wrapper.jl index aca45e4..4f3f183 100644 --- a/src/export/jld2wrapper.jl +++ b/src/export/jld2wrapper.jl @@ -13,7 +13,7 @@ function Base.getindex(j2w::JLD2Wrapper, key) return item isa JLD2Obj ? JLD2Wrapper(item) : item end -Base.get(j2w::JLD2Wrapper, key, default) = get(j2w._handle, key, default) +Base.get(j2w::JLD2Wrapper, key, default) = haskey(j2w, key) ? j2w[key] : default Base.setindex!(j2w::JLD2Wrapper, val, key) = setindex!(j2w._handle, val, key) # For backwards compatibility with HDF5 write all SArrays as Arrays -- GitLab From 08a428f308f61144087867365d4437f49426c54e Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 16:08:04 +0200 Subject: [PATCH 12/23] more fixes --- src/export/jld2wrapper.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/export/jld2wrapper.jl b/src/export/jld2wrapper.jl index 4f3f183..00ec0ae 100644 --- a/src/export/jld2wrapper.jl +++ b/src/export/jld2wrapper.jl @@ -26,15 +26,15 @@ Base.haskey(j2w::JLD2Wrapper, k) = haskey(j2w._handle, k) # InPartS IO Backend API InPartS.dfopen(::Type{<:JLD2Wrapper}, filename::String, mode::String = "r") = JLD2Wrapper(JLD2.jldopen(filename, mode)) - InPartS.dfclose(j2w::JLD2Wrapper) = close(j2w._handle) +Base.close(j2w::JLD2Wrapper{<:JLD2.JLDFile}) = close(j2w._handle) # for lazy people function InPartS.dfrename(::Type{JLD2Wrapper}, oldname, newname; force = false) cp(oldname, newname; force = force) rm(oldname) end -InPartS.gcreate(g::JLD2Wrapper, name::String; kwargs...) = JLD2.Group(g._handle, name; kwargs...) +InPartS.gcreate(g::JLD2Wrapper, name::String; kwargs...) = JLD2Wrapper(JLD2.Group(g._handle, name; kwargs...)) InPartS.isgroup(g::JLD2Wrapper, key) = (g[key] isa JLD2Wrapper{<:JLD2Group}) Base.length(g::JLD2Wrapper) = length(keys(g._handle)) -- GitLab From d7686640344b25c0ea62dd256ed9604be1594858 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 16:51:53 +0200 Subject: [PATCH 13/23] default snap for readsim --- src/export/generic.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/export/generic.jl b/src/export/generic.jl index 4a3cded..1ed2337 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -496,13 +496,14 @@ end ## Convenience function for reading things """ - readsim(filename; snap, [warn = IOWARN[]], [kwargs...]) - readsim(f; snap, [warn = IOWARN[]], [kwargs...]) -Read a simulation from file and load the specified snapshot. +readsim(f; snap = InPartS.lastfullsnap(f), [warn = IOWARN[]], [kwargs...]) +readsim(filename; snap, [warn = IOWARN[]], [kwargs...]) +Read a simulation from file and load the specified snapshot. Defaults to the last full snapshot +(see [`lastfullsnap`](@ref)). Additional keyword arguments are passed through to [`readstatic!`](@ref) and [`readsnap`](@ref). """ -function readsim(f; snap, warn = IOWARN[], kwargs...) +function readsim(f; snap = lastfullsnap(f), warn = IOWARN[], kwargs...) sim = readstatic(f; warn, kwargs...) try readsnap!(sim, f, snap; warn, kwargs...) -- GitLab From d43f303c877a4ba79f1e8211a0ad925978aed84e Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 16:52:18 +0200 Subject: [PATCH 14/23] =?UTF-8?q?backends=20=E2=86=92=20extensions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project.toml | 5 +++++ src/export/h5wrapper.jl => ext/HDF5Ext.jl | 21 +++++++++++++-------- src/export/jld2wrapper.jl => ext/JLD2Ext.jl | 13 +++++++++---- src/InPartS.jl | 20 ++++++++++++-------- src/export/generic.jl | 12 +++++------- 5 files changed, 44 insertions(+), 27 deletions(-) rename src/export/h5wrapper.jl => ext/HDF5Ext.jl (78%) rename src/export/jld2wrapper.jl => ext/JLD2Ext.jl (90%) diff --git a/Project.toml b/Project.toml index 7055263..b101ed1 100644 --- a/Project.toml +++ b/Project.toml @@ -16,6 +16,11 @@ Requires = "ae029012-a4dd-5104-9daa-d747884805df" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +[weakdeps] +JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" + +[extensions] +JLD2Ext = "JLD2" [compat] julia = "1.7" diff --git a/src/export/h5wrapper.jl b/ext/HDF5Ext.jl similarity index 78% rename from src/export/h5wrapper.jl rename to ext/HDF5Ext.jl index f5fffd6..716bfcf 100644 --- a/src/export/h5wrapper.jl +++ b/ext/HDF5Ext.jl @@ -1,12 +1,15 @@ -export H5Wrapper +module HDF5Ext -using .HDF5 +using InPartS, HDF5 struct H5Wrapper{T<:Union{HDF5.File, HDF5.Group}} handle::T end +InPartS._backend(::Val{:HDF5}) = H5Wrapper + + """ H5Wrapper(filename; [kwargs...]) → H5Wrapper{HDF5.File} H5Wrapper(filename, mode::String) → H5Wrapper{HDF5.File} @@ -48,21 +51,23 @@ Base.setindex!(h5w::H5Wrapper, value::SArray, key::String) = setindex!(h5w.handl Base.get(h5w::H5Wrapper, key::String, default) = exists(h5w.handle, key) ? h5w[key] : default # InPartS IO Backend API -dfopen(::Type{H5Wrapper}, name::String; flags...) = H5Wrapper(name; flags...) -dfclose(h5w::H5Wrapper{HDF5.File}) = close(h5w.handle) -function dfrename(::Type{H5Wrapper}, oldname, newname; force = false) +InPartS.dfopen(::Type{H5Wrapper}, name::String; flags...) = H5Wrapper(name; flags...) +InPartS.dfclose(h5w::H5Wrapper{HDF5.File}) = close(h5w.handle) +function InPartS.dfrename(::Type{H5Wrapper}, oldname, newname; force = false) cp(oldname, newname; force = force) rm(oldname) end -gcreate(h5w::H5Wrapper, name::String; kwargs...) = H5Wrapper(HDF5.create_group(h5w.handle, name)) +InPartS.gcreate(h5w::H5Wrapper, name::String; kwargs...) = H5Wrapper(HDF5.create_group(h5w.handle, name)) Base.keys(h5w::H5Wrapper) = keys(h5w.handle) Base.haskey(h5w::H5Wrapper, k::AbstractString) = haskey(h5w.handle, k) -isgroup(h5w::H5Wrapper, key) = !(h5w.handle[key] isa HDF5.Dataset) +InPartS.isgroup(h5w::H5Wrapper, key) = !(h5w.handle[key] isa HDF5.Dataset) Base.length(h5w::H5Wrapper) = length(h5w.handle) # more efficient readdict -readdict(h5w::H5Wrapper; kwargs...) = read(h5w.handle) +InPartS.readdict(h5w::H5Wrapper; kwargs...) = read(h5w.handle) + +end \ No newline at end of file diff --git a/src/export/jld2wrapper.jl b/ext/JLD2Ext.jl similarity index 90% rename from src/export/jld2wrapper.jl rename to ext/JLD2Ext.jl index 00ec0ae..1e8a21a 100644 --- a/src/export/jld2wrapper.jl +++ b/ext/JLD2Ext.jl @@ -1,5 +1,6 @@ -export JLD2Wrapper -using .JLD2 +module JLD2Ext + +using InPartS, JLD2 const JLD2Obj = Union{JLD2.JLDFile, JLD2.Group} @@ -25,6 +26,8 @@ Base.keys(j2w::JLD2Wrapper) = keys(j2w._handle) Base.haskey(j2w::JLD2Wrapper, k) = haskey(j2w._handle, k) # InPartS IO Backend API +InPartS._backend(::Val{:JLD2}) = JLD2Wrapper + InPartS.dfopen(::Type{<:JLD2Wrapper}, filename::String, mode::String = "r") = JLD2Wrapper(JLD2.jldopen(filename, mode)) InPartS.dfclose(j2w::JLD2Wrapper) = close(j2w._handle) Base.close(j2w::JLD2Wrapper{<:JLD2.JLDFile}) = close(j2w._handle) # for lazy people @@ -65,8 +68,8 @@ InPartS.readdict(d::Dict) = d # legacy stuff -InPartS.dfopen(::Type{<:JLD2.JLDFile}, args...; kwargs...) = dfopen(JLD2Wrapper, args...; kwargs...) -InPartS.dfclose(df::JLD2.JLDFile) = dfclose(JLD2Wrapper(df)) +InPartS.dfopen(::Type{<:JLD2.JLDFile}, args...; kwargs...) = InPartS.dfopen(JLD2Wrapper, args...; kwargs...) +InPartS.dfclose(df::JLD2.JLDFile) = InPartS.dfclose(JLD2Wrapper(df)) InPartS.dfrename(df::Type{<:JLD2.JLDFile}, args...; kwargs...) = InPartS.dfrename(JLD2Wrapper, args...; kwargs...) # fully automatic wrapping for fun ∈ [:readdict, :readdomain, :readobstacles, :readparams, :readrng, :readsim, :readsnap, :readstatic, :readtype, :numsnaps, :lastfullsnap] @@ -74,3 +77,5 @@ for fun ∈ [:readdict, :readdomain, :readobstacles, :readparams, :readrng, :rea end InPartS.readsnap!(sim::Simulation, df::JLD2.JLDFile, args...; kwargs...) = InPartS.readsnap!(sim::Simulation, JLD2Wrapper(df), args...; kwargs...) + +end \ No newline at end of file diff --git a/src/InPartS.jl b/src/InPartS.jl index 0518a43..e874704 100644 --- a/src/InPartS.jl +++ b/src/InPartS.jl @@ -189,16 +189,20 @@ function __init__() # EXPORT BACKENDS - - @require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" begin - include("export/h5wrapper.jl") - @info "InPartS: HDF5 export enabled" - end - @require JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" begin - include("export/jld2wrapper.jl") - @info "InPartS: JLD2 export enabled" + # fallback if no extensions available + @static if !isdefined(Base, :get_extension) + @require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" begin + include("../ext/HDF5Ext.jl") + @info "InPartS: HDF5 export enabled" + end + + @require JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" begin + include("../ext/JLD2Ext.jl") + @info "InPartS: JLD2 export enabled" + end end + # Use Requires infrastructure to warn if legacy version is ever loaded simultaneously legacyInPartS = Base.PkgId(Base.UUID("385f2a1c-27df-41b8-9918-2c5d735168af"), "InPartS") Requires.listenpkg(legacyInPartS) do diff --git a/src/export/generic.jl b/src/export/generic.jl index 1ed2337..10e0485 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -14,15 +14,13 @@ function backend(filename::String) if !isdefined(Main, modulename) error("File appears to be a $modulename file but library is not loaded.") end - if modulename == :JLD2 - return JLD2Wrapper - elseif modulename == :HDF5 - return H5Wrapper - else - error("This backend is not implemented") - end + # this is a hack to get around the fact that extensions cannot export copied + # so we can't use the JLD2Wrapper/H5Wrapper types from here + return _backend(Val(modulename)) end +_backend(v) = error("The backend $v is not implemented") + """ dfopen(backend, filename; kwargs...) dfopen(backend, filename, mode::String) -- GitLab From 24ff064acfa1d3c82f8ec0a1040ec64b50fff1e5 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 16:56:02 +0200 Subject: [PATCH 15/23] fix --- src/export/generic.jl | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/export/generic.jl b/src/export/generic.jl index 10e0485..a20ff6f 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -920,18 +920,7 @@ A function for whenever you just need to save the ~~world~~ simulation. """ function write_sim(filename, sim::Simulation; dumprng = true) - s = FileIO.query(filename; checkfile=true) - modulename = typeof(s).parameters[1].parameters[1] - if !isdefined(InPartS, modulename) - Base.require(Main, modulename) - return Base.invokelatest(write_sim, filename, sim; dumprng) - elseif modulename == :JLD2 - FT = JLD2Wrapper - elseif modulename == :HDF5 - FT = H5Wrapper - else - error("This backend is not implemented") - end + FT = backend(filename) dfopen(FT, filename, "w") do f writestatic(f, sim) writesnap(f[SNAPGROUPNAME], sim; name = "0", dumprng) -- GitLab From ef400ca49cd65be41ccfabd60a9bfc900b25bb88 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 17:29:42 +0200 Subject: [PATCH 16/23] forgot to register extension --- Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Project.toml b/Project.toml index b101ed1..1c477a6 100644 --- a/Project.toml +++ b/Project.toml @@ -18,9 +18,11 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [weakdeps] JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" [extensions] JLD2Ext = "JLD2" +HDF5Ext = "HDF5 [compat] julia = "1.7" -- GitLab From b45d801e75284a50dc4af09ce65d40044bd2acca Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 17:49:13 +0200 Subject: [PATCH 17/23] typo --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1c477a6..95180cd 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,7 @@ HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" [extensions] JLD2Ext = "JLD2" -HDF5Ext = "HDF5 +HDF5Ext = "HDF5" [compat] julia = "1.7" -- GitLab From 4d48ab683ed205ab296f74190598e01eb644ed28 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 18:01:14 +0200 Subject: [PATCH 18/23] ugb --- ext/HDF5Ext.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/HDF5Ext.jl b/ext/HDF5Ext.jl index 716bfcf..57524a3 100644 --- a/ext/HDF5Ext.jl +++ b/ext/HDF5Ext.jl @@ -41,7 +41,7 @@ end # dictionary-like access function Base.getindex(h5w::H5Wrapper, key::String) item = h5w.handle[key] - return isgroup(h5w, key) ? H5Wrapper(item) : read(item) + return InPartS.isgroup(h5w, key) ? H5Wrapper(item) : read(item) end Base.setindex!(h5w::H5Wrapper, value, key::String) = setindex!(h5w.handle, value, key) Base.setindex!(h5w::H5Wrapper, value::Dict{String, Any}, key::String) = InPartS.writedict(h5w, value; name = key) -- GitLab From 9e4120142c5de5555bd1e49535329c476842c89a Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Thu, 27 Jul 2023 18:09:32 +0200 Subject: [PATCH 19/23] aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaA --- ext/JLD2Ext.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/JLD2Ext.jl b/ext/JLD2Ext.jl index 1e8a21a..da0a3a0 100644 --- a/ext/JLD2Ext.jl +++ b/ext/JLD2Ext.jl @@ -55,7 +55,7 @@ function InPartS.readdict(j2w::JLD2Wrapper; kwargs...) for k in keys(g) v = g[k] if v isa JLD2.Group - d[k] = readdict(v) + d[k] = InPartS.readdict(v) else d[k] = v end -- GitLab From 8f143477cb1067d9c253612421f15b74d10cf904 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Fri, 28 Jul 2023 10:11:28 +0000 Subject: [PATCH 20/23] changelog & version --- CHANGELOG.md | 10 ++++++++++ Project.toml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc88c3..6c5f1be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## v0.4.2 + - *deprecation*: `forcetuple` is deprecated in favour of the new improved `forcecontribution` + - *bugfix*: automatic ParamType now can deal with UnionAll particle types as long as `(::ParticleType).params` is concrete + - various IO improvements + - HDF5 and JLD2 IO backends have been moved to extensions on Julia versions that support them + - improvements to type reconstruction, speeding up simulation loading + - `readsim` now loads the last full snapshot when no snapshot is explicitely specified + - utility functions `current_time`, `current_step`, `particletype`, `hasobstacles` are now exported + + ## v0.4.1 - *bugfix*: PyPlot plotting has been repaired - *new feature*: new convenience syntax for reading data files with the `Snapshots` accessor diff --git a/Project.toml b/Project.toml index 95180cd..e1eae75 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "InPartS" uuid = "f768f48f-0d8a-415b-80aa-7de5ff9b8474" authors = ["Lukas Hupe <lukas.hupe@ds.mpg.de>", "Jonas Isensee <jonas.isensee@ds.mpg.de>", "Philip Bittihn <philip.bittihn@ds.mpg.de>"] -version = "0.4.1" +version = "0.4.2" [deps] FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -- GitLab From 51627056c559a06f19e84a5fbbaad504fd187275 Mon Sep 17 00:00:00 2001 From: Jonas Isensee <jonas.isensee@ds.mpg.de> Date: Fri, 28 Jul 2023 11:09:34 +0000 Subject: [PATCH 21/23] Noninteractive Termination --- src/callbacks.jl | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/callbacks.jl b/src/callbacks.jl index d7f89ab..75b182a 100644 --- a/src/callbacks.jl +++ b/src/callbacks.jl @@ -2,6 +2,7 @@ using Printf import Base.∘ export EveryStepCallback, TimedCallback, PeriodicCallback, RealTimeCallback, BurstModeCallback, CallbackSet +export SleeperCallback abstract type AbstractCallback end @@ -33,6 +34,14 @@ simulation. function finalize(::AbstractCallback, sim; kwargs...) end +""" + defrost!(cb <: AbstractCallback) +Define for custom callback if it needs modification after a simulation was interrupted. +The primary intended use case is to update timestamps in e.g. RealTimeCallback. +""" +function defrost!(cb::AbstractCallback) end + + """ NoCallback() <: AbstractCallback @@ -166,6 +175,7 @@ function (cb::RealTimeCallback)(sim) end finalize(cb::RealTimeCallback, sim; kwargs...) = cb.finalize(sim; kwargs...) +defrost!(cb::RealTimeCallback) = (cb.offset = time(); return nothing); @@ -302,3 +312,65 @@ function (cb::BurstModeCallback)(sim) end InPartS.finalize(cb::BurstModeCallback, sim; kwargs...) = cb.finalize(sim; kwargs...) + + +""" + SleeperCallback(trigger_file, interval=30.0) <: AbstractCallback +Returns a callback that checks for the existence of `trigger_file` every `interval` seconds. +If the file exists, it is read and the following commands are supported: +- `terminate`: terminates the simulation +- `simtime=...`: terminates the simulation at the specified simulation time +- `simtime+...`: terminates the simulation at the next multiple of the specified simulation time + +The callback copies the received commands to `trigger_file*"_parsed"` and deletes `trigger_file`. +""" +mutable struct SleeperCallback <: AbstractCallback + trigger_file::String + interval::Float64 + last_executiontime::Float64 + terminate_simtime::Float64 + SleeperCallback(trigger_file::String, interval::Real=30.0) = + new(trigger_file, interval, time(), Inf) +end + +function prepropagate(cb::SleeperCallback, sim) + triggertime = time() + + if sim.t ≥ cb.terminate_simtime + # terminate simulation + println("# SleeperCallback sim-time condition was triggered at t=$(sim.t) > $(cb.terminate_simtime). Terminating simulation.") + return false + end + if (triggertime - cb.last_executiontime) < cb.interval + return true + end + cb.last_executiontime = triggertime + retcode = true + if !isfile(cb.trigger_file) + # trigger file doesn't exist. Create it. + write(cb.trigger_file, "") + else + command = readline(cb.trigger_file) + if command == "terminate" + println("# SleeperCallback: Received terminate command. Terminating simulation.") + retcode = false + elseif startswith(command, "simtime=") + cb.terminate_simtime = parse(Float64, split(command, "=")[2]) + println("# SleeperCallback: Received terminate command. Terminating simulation at t=$(cb.terminate_simtime).") + elseif startswith(command, "simtime+") + extrasimtime = parse(Float64, split(command, "+")[2]) + cb.terminate_simtime = sim.t - sim.t%extrasimtime + extrasimtime + println("# SleeperCallback: Received terminate command. Terminating simulation at t=$(cb.terminate_simtime).") + elseif !isempty(command) + println("# SleeperCallback: Received gibberish: \"$(command)\". Ignoring.") + end + if !isempty(command) + open(cb.trigger_file*"_parsed", "a") do f + write(f, command) + write(f, "\n") + end + write(cb.trigger_file, "") # empty the file + end + end + return retcode +end -- GitLab From 83ffacb1a251e5622c74d134306610496232ccb5 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Fri, 28 Jul 2023 11:10:12 +0000 Subject: [PATCH 22/23] Update file CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5f1be..aa5eddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - improvements to type reconstruction, speeding up simulation loading - `readsim` now loads the last full snapshot when no snapshot is explicitely specified - utility functions `current_time`, `current_step`, `particletype`, `hasobstacles` are now exported - + - added SleeperCallback for external simulation control ## v0.4.1 - *bugfix*: PyPlot plotting has been repaired -- GitLab From 3ddee8455fc7d01e09f0a1c24d227c7300dbac16 Mon Sep 17 00:00:00 2001 From: Lukas Hupe <lukas.hupe@ds.mpg.de> Date: Fri, 28 Jul 2023 16:59:30 +0200 Subject: [PATCH 23/23] con structors --- src/export/generic.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/export/generic.jl b/src/export/generic.jl index a20ff6f..a58c3f4 100644 --- a/src/export/generic.jl +++ b/src/export/generic.jl @@ -167,6 +167,8 @@ mutable struct SaveCallback{FT, CB<:AbstractCallback, FN<:Function} <: AbstractC new{FT, CB, FN}(trigger, filename, 0, false, fn, warn) end +SaveCallback(trigger, filename, fn, warn) = SaveCallback{backend(filename)}(trigger, filename, fn, warn) + """ SaveCallback([backend], filename; [interval = 1.0], [offset = 0.0], [dumprng = (s, sim) -> (s.nextsnap % 10 == 0)], [warn = IOWARN[]]) Create a [`SaveCallback`](@ref) that periodically saves simulation snapshots to `filename`. @@ -174,10 +176,11 @@ Create a [`SaveCallback`](@ref) that periodically saves simulation snapshots to Uses a [`PeriodicCallback`](@ref) with the given `interval` and `offset`. """ SaveCallback(backend, filename; interval = 1.0, offset = 0.0, dumprng = (s, sim) -> _defaultdumprng(sim.rng, s), warn = IOWARN[]) = - SaveCallback{backend}(PeriodicCallback(sim -> true, interval; offset = offset), filename, dumprng, warn) + SaveCallback(PeriodicCallback(sim -> true, interval; offset = offset), filename, dumprng, warn) SaveCallback(filename::String; kwargs...) = SaveCallback(backend(filename), filename; kwargs...) + function (s::SaveCallback)(sim::Simulation) if !s.initialized _init(s, sim) @@ -257,6 +260,8 @@ struct BackupCallback{FT, CB} <: AbstractCallback BackupCallback{FT}(trigger::CB, filename, warn) where {FT, CB} = new{FT, CB}(trigger, filename, warn) end +BackupCallback(trigger, filename, warn) = SaveCallback{backend(filename)}(trigger, filename, warn) + """ BackupCallback(backend, filename; [interval = 300.0], [offset = 0.0], [warn = IOWARN[]]) Creates a [`BackupCallback`](@ref) that periodically dumps the entire simulation state to `filename`. @@ -264,7 +269,7 @@ Creates a [`BackupCallback`](@ref) that periodically dumps the entire simulation Uses a [`RealTimeCallback`](@ref) with the given `interval` and `offset`. """ BackupCallback(backend, filename; interval = 300.0, offset = 0.0, warn = IOWARN[]) = - BackupCallback{backend}(RealTimeCallback(interval, sim -> true; offset = offset), filename, warn) + BackupCallback(RealTimeCallback(interval, sim -> true; offset = offset), filename, warn) BackupCallback(filename::String; kwargs...) = BackupCallback(backend(filename), filename; kwargs...) -- GitLab