From 4a245c654ac9decfe08b6f3a981f2198cfcb3f76 Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Tue, 6 Jun 2023 16:58:18 +0200
Subject: [PATCH 1/9] first draft for snapshot accessor

---
 src/export/generic.jl | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index e5bc34c..b794455 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -1,4 +1,4 @@
-export SaveCallback, BackupCallback, readsim
+export SaveCallback, BackupCallback, Snapshots, readsim, 
 
 ## Datafile interface definitions
 
@@ -67,6 +67,33 @@ function modetoflags(mode::String)
     end
 end
 
+## Lazy snapshot accessor
+
+struct Snapshots
+    path::String
+    _lastsnap::Ref{Int}
+    Snapshots(path::String; lastsnap = -1) = new(path, Ref(lastsnap))
+end
+
+# indexed access
+Base.getindex(sn::Snapshots, i::Int) = InPartS.dfopen(sn.path, "r") do df
+    InPartS.readsim(df, snap = i)
+end
+
+Base.firstindex(sn::Snapshots) = 0
+
+function Base.lastindex(sn::Snapshots)
+    if sn._lastsnap[] < 0
+        nsnaps = dfopen(sn.path, "r") do df
+            numsnaps(df) 
+        end
+        sn._lastsnap[] = nsnaps - 1
+    end
+    return sn._lastsnap[]
+end
+        
+Base.show(io::IO, sn::Snapshots) = print(io, "Snapshots($(sn.path)")
+
 ## Callbacks
 
 
-- 
GitLab


From b8b906b55720863d13c6cf38d4a002203d26e2e8 Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Tue, 6 Jun 2023 17:12:40 +0200
Subject: [PATCH 2/9] trailing comma,

---
 src/export/generic.jl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index b794455..d72b8f9 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -1,4 +1,4 @@
-export SaveCallback, BackupCallback, Snapshots, readsim, 
+export SaveCallback, BackupCallback, Snapshots, readsim
 
 ## Datafile interface definitions
 
-- 
GitLab


From 8af492c57e744a1c99ce0664f4806a540335ef17 Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Wed, 7 Jun 2023 15:03:12 +0200
Subject: [PATCH 3/9] iteration

---
 src/export/generic.jl | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index d72b8f9..de89471 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -85,13 +85,20 @@ Base.firstindex(sn::Snapshots) = 0
 function Base.lastindex(sn::Snapshots)
     if sn._lastsnap[] < 0
         nsnaps = dfopen(sn.path, "r") do df
-            numsnaps(df) 
+            numsnaps(df)
         end
         sn._lastsnap[] = nsnaps - 1
     end
     return sn._lastsnap[]
 end
-        
+
+# iteration
+Base.iterate(sn::Snapshots, state::Int = 0) = state > lastindex(sn) ? nothing : (sn[state],  state + 1)
+Base.length(sn::Snapshots) = lastindex(sn) + 1
+Base.size(sn::Snapshots) = (length(sn), )
+Base.eltype(::Snapshots) = InPartS.Simulation
+
+
 Base.show(io::IO, sn::Snapshots) = print(io, "Snapshots($(sn.path)")
 
 ## Callbacks
-- 
GitLab


From 46affecd3ce718bce02a4c14f1a7cbdef2387d80 Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Wed, 7 Jun 2023 17:45:09 +0200
Subject: [PATCH 4/9] smaller magic number

---
 src/export/generic.jl | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index de89471..3772ef8 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -72,7 +72,8 @@ end
 struct Snapshots
     path::String
     _lastsnap::Ref{Int}
-    Snapshots(path::String; lastsnap = -1) = new(path, Ref(lastsnap))
+    # use -2 as magic value to distinguish empty files (numsnaps = 0)
+    Snapshots(path::String; lastsnap = -2) = new(path, Ref(lastsnap))
 end
 
 # indexed access
@@ -83,7 +84,7 @@ end
 Base.firstindex(sn::Snapshots) = 0
 
 function Base.lastindex(sn::Snapshots)
-    if sn._lastsnap[] < 0
+    if sn._lastsnap[] < -1
         nsnaps = dfopen(sn.path, "r") do df
             numsnaps(df)
         end
-- 
GitLab


From e2da01a8b820fb6095571f3821988eb6c9263eee Mon Sep 17 00:00:00 2001
From: Jonas Isensee <jonas.isensee@ds.mpg.de>
Date: Fri, 16 Jun 2023 12:27:20 +0000
Subject: [PATCH 5/9] make it simpler

---
 src/export/generic.jl | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index 3772ef8..cdf2ed3 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -77,17 +77,18 @@ struct Snapshots
 end
 
 # indexed access
-Base.getindex(sn::Snapshots, i::Int) = InPartS.dfopen(sn.path, "r") do df
-    InPartS.readsim(df, snap = i)
+function Base.getindex(sn::Snapshots, i::Int)
+    if !(firstindex(sn) ≤ i ≤ lastindex(sn))
+        throw(BoundsError("attempt to access snapshot at index [$i], valid snapshot indices are $(firstindex(sn)):$(lastindex(sn))")) 
+    end
+    readsim(sn.path, snap = i)
 end
 
 Base.firstindex(sn::Snapshots) = 0
 
 function Base.lastindex(sn::Snapshots)
     if sn._lastsnap[] < -1
-        nsnaps = dfopen(sn.path, "r") do df
-            numsnaps(df)
-        end
+        nsnaps = numsnaps(sn.path)
         sn._lastsnap[] = nsnaps - 1
     end
     return sn._lastsnap[]
-- 
GitLab


From 234ee12de03130d4e95589a0763c5f52daa0b0ac Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Fri, 16 Jun 2023 14:31:05 +0200
Subject: [PATCH 6/9] docstring

---
 src/export/generic.jl | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index cdf2ed3..4c8e596 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -69,6 +69,16 @@ end
 
 ## Lazy snapshot accessor
 
+"""
+    Snapshots(path::String)
+A lazy iterator to access snapshots in the InPartS data file at `path`. 
+
+Indexing syntax `Snapshots(path)[i]` will open the file and attempt to load the `i`th 
+snapshot using [`readsim`](@ref). 
+
+Iteration is also supported, but keep in mind that the data file will be opened and closed
+in every single iteration
+"""
 struct Snapshots
     path::String
     _lastsnap::Ref{Int}
-- 
GitLab


From c0cfcd7bd672883485572f311888afa4bf0de94d Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Mon, 19 Jun 2023 11:22:25 +0200
Subject: [PATCH 7/9] slicing?

---
 src/export/generic.jl | 48 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 38 insertions(+), 10 deletions(-)

diff --git a/src/export/generic.jl b/src/export/generic.jl
index 4c8e596..4f0d473 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -71,30 +71,56 @@ end
 
 """
     Snapshots(path::String)
-A lazy iterator to access snapshots in the InPartS data file at `path`. 
+A lazy iterator to access snapshots in the InPartS data file at `path`.
 
-Indexing syntax `Snapshots(path)[i]` will open the file and attempt to load the `i`th 
-snapshot using [`readsim`](@ref). 
+Indexing syntax `Snapshots(path)[i]` will open the file and attempt to load the `i`th
+snapshot using [`readsim`](@ref).
+Indexing with a unit range `Snapshots(path)[i:j]` will return a `Snapshots` struct with
+modified `firstindex` and `lastindex`. Note that bounds checking may not be performed here,
+as the file isn't opened and therefore the last snapshot index cannot be determined if it isn't
+already known from a previous file access. Therefore, the resulting `Snapshots` may be invalid.
 
 Iteration is also supported, but keep in mind that the data file will be opened and closed
-in every single iteration
+in every single iteration.
+
+Requires your data file to use zero indexed sequentially numbered snapshots (as is the case
+if you used a [`SaveCallback`](@ref)).
 """
 struct Snapshots
     path::String
+    _firstsnap::Int # this is not a ref as it must be set on construction (either its 0 or you know what you want)
     _lastsnap::Ref{Int}
     # use -2 as magic value to distinguish empty files (numsnaps = 0)
-    Snapshots(path::String; lastsnap = -2) = new(path, Ref(lastsnap))
+    Snapshots(path::String; firstsnap = 0, lastsnap = -2) = new(path, firstsnap, Ref(lastsnap))
 end
 
 # indexed access
-function Base.getindex(sn::Snapshots, i::Int)
-    if !(firstindex(sn) ≤ i ≤ lastindex(sn))
-        throw(BoundsError("attempt to access snapshot at index [$i], valid snapshot indices are $(firstindex(sn)):$(lastindex(sn))")) 
+function Base.getindex(sn::Snapshots, i::Integer)
+    @boundscheck if !(firstindex(sn) ≤ i ≤ lastindex(sn))
+        throw(BoundsError(sn, i))
     end
     readsim(sn.path, snap = i)
 end
 
-Base.firstindex(sn::Snapshots) = 0
+function Base.getindex(sn::Snapshots, is::UnitRange)
+    # don't do a full bounds check in case `lastindex` doesn't work
+    @boundscheck if !(_maybe_check_bounds(sn, first(is)) && _maybe_check_bounds(sn, last(is)))
+        throw(BoundsError(sn, is))
+    end
+    return Snapshots(sn.path; firstsnap = first(is), lastsnap = last(is))
+end
+
+function _maybe_check_bounds(sn, i)
+    if sn._lastsnap[] < -1
+        @warn "Index may be out of range for $sn (last snap not known)" maxlog=1
+        return firstindex(sn) ≤ i
+    else
+        return firstindex(sn) ≤ i ≤ lastindex(sn)
+    end
+end
+
+
+Base.firstindex(sn::Snapshots) = sn._firstsnap
 
 function Base.lastindex(sn::Snapshots)
     if sn._lastsnap[] < -1
@@ -104,6 +130,8 @@ function Base.lastindex(sn::Snapshots)
     return sn._lastsnap[]
 end
 
+Base.eachindex(sn) = firstindex(sn):lastindex(sn)
+
 # iteration
 Base.iterate(sn::Snapshots, state::Int = 0) = state > lastindex(sn) ? nothing : (sn[state],  state + 1)
 Base.length(sn::Snapshots) = lastindex(sn) + 1
@@ -111,7 +139,7 @@ Base.size(sn::Snapshots) = (length(sn), )
 Base.eltype(::Snapshots) = InPartS.Simulation
 
 
-Base.show(io::IO, sn::Snapshots) = print(io, "Snapshots($(sn.path)")
+Base.show(io::IO, sn::Snapshots) = print(io, "Snapshots($(sn.path))[$(firstindex(sn)):$(sn._lastsnap[] < -1 ? "???" : lastindex(sn))]")
 
 ## Callbacks
 
-- 
GitLab


From 2494f5a6ee11dfe68bf730d17129c73b03c490c4 Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Mon, 19 Jun 2023 11:30:28 +0200
Subject: [PATCH 8/9] add global IOWARN setting

---
 src/InPartS.jl        |   3 ++
 src/export/generic.jl | 118 +++++++++++++++++++++---------------------
 2 files changed, 63 insertions(+), 58 deletions(-)

diff --git a/src/InPartS.jl b/src/InPartS.jl
index 8af14a0..6cc9123 100644
--- a/src/InPartS.jl
+++ b/src/InPartS.jl
@@ -202,6 +202,9 @@ function __init__()
         @warn "The legacy version of InPartS was loaded alongside the current, public version. This will cause unexpected behavior."
     end
 
+    # set IOWARN from environment
+    InPartS.IOWARN[] = get(ENV, "INPARTS_IOWARN", true)
+
     return
 end
 
diff --git a/src/export/generic.jl b/src/export/generic.jl
index 4f0d473..b7db702 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -1,5 +1,7 @@
 export SaveCallback, BackupCallback, Snapshots, readsim
 
+# this may be set from ENV by the __init__ function
+const IOWARN = Ref(true)
 ## Datafile interface definitions
 
 """
@@ -145,7 +147,7 @@ Base.show(io::IO, sn::Snapshots) = print(io, "Snapshots($(sn.path))[$(firstindex
 
 
 """
-    SaveCallback{backend}(trigger::AbstractCallback, filename, dumprng; [warn = true])
+    SaveCallback{backend}(trigger::AbstractCallback, filename, dumprng; [warn = IOWARN[]])
 Create a callback that periodically saves simulation snapshots to `filename`.
 
 # Extended help
@@ -168,12 +170,12 @@ mutable struct SaveCallback{FT, CB<:AbstractCallback, FN<:Function} <: AbstractC
 end
 
 """
-    SaveCallback([backend], filename; [interval = 1.0], [offset = 0.0], [dumprng = (s, sim) -> (s.nextsnap % 10 == 0)], [warn = true])
+    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`.
 
 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 = true) =
+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(filename::String; kwargs...) = SaveCallback(backend(filename), filename; kwargs...)
@@ -241,7 +243,7 @@ end
 
 
 """
-    BackupCallback{backend}(trigger::AbstractCallback, filename; [warn = true])
+    BackupCallback{backend}(trigger::AbstractCallback, filename; [warn = IOWARN[]])
 Creates a callback that dumps the entire simulation state to `filename`.
 
 # Extended help
@@ -258,12 +260,12 @@ struct BackupCallback{FT, CB} <: AbstractCallback
 end
 
 """
-   BackupCallback(backend, filename; [interval = 300.0], [offset = 0.0], [warn = true])
+   BackupCallback(backend, filename; [interval = 300.0], [offset = 0.0], [warn = IOWARN[]])
 Creates a [`BackupCallback`](@ref) that periodically dumps the entire simulation state to `filename`.
 
 Uses a [`RealTimeCallback`](@ref) with the given `interval` and `offset`.
 """
-BackupCallback(backend, filename; interval = 300.0, offset = 0.0, warn = true) =
+BackupCallback(backend, filename; interval = 300.0, offset = 0.0, warn = IOWARN[]) =
     BackupCallback{backend}(RealTimeCallback(interval, sim -> true; offset = offset), filename, warn)
 BackupCallback(filename::String; kwargs...) =
     BackupCallback(backend(filename), filename; kwargs...)
@@ -297,13 +299,13 @@ finalize(cb::BackupCallback, sim; kwargs...) = cb(sim)
 ## Generic writing functions
 # writestatic
 """
-    writestatic(simgroup, sim; [warn = true]) → snapgroup, paramgroup
+    writestatic(simgroup, sim; [warn = IOWARN[]]) → snapgroup, paramgroup
 Writes the static bits of the simulation to the output group `simgroup`.
 
 This includes the particle and parameter types, the domain and some InPartS version
 information.
 """
-function writestatic(simgroup, sim::Simulation{PTC, PT, PM}; warn = true) where {PTC, PT, PM}
+function writestatic(simgroup, sim::Simulation{PTC, PT, PM}; warn = IOWARN[]) where {PTC, PT, PM}
     simgroup[TREEHASHNAME] = InPartS.TREE_HASH
     simgroup[VERSIONNAME] = string(InPartS._INPARTS_VERSION)
 
@@ -330,7 +332,7 @@ function writestatic(simgroup, sim::Simulation{PTC, PT, PM}; warn = true) where
 end
 
 
-function writerng(group, rng::T; complete::Bool = false, warn = true) where {T<:AbstractRNG}
+function writerng(group, rng::T; complete::Bool = false, warn = IOWARN[]) where {T<:AbstractRNG}
     group["_type"] = string(T)
     group["_rng_complete"] = (T == Xoshiro) || complete # Xoshiro is always completely serialized
     if complete
@@ -344,11 +346,11 @@ end
 
 
 """
-    writetype(group, PT::Type; name, [warn = true])
+    writetype(group, PT::Type; name, [warn = IOWARN[]])
 Creates a dataset of name `name` in `group` and saves the type name of `PT`.
 If `PT` is a `Union`, the union types´ names will be written instead as a vector.
 """
-function writetype(group, PT::Type; name, prefix = "", warn = true)
+function writetype(group, PT::Type; name, prefix = "", warn = IOWARN[])
     if PT isa Union
         typenames = string.(Base.uniontypes(PT))
         group[name] = prefix.*typenames
@@ -360,11 +362,11 @@ end
 
 
 """
-    writedomain(group, domain; [name = DOMAINGROUPNAME], [warn = true])
+    writedomain(group, domain; [name = DOMAINGROUPNAME], [warn = IOWARN[]])
 Creates a new subgroup called `name` in `DOMAINSIZENAME` and fills it withthe static properties
 of the domain (to wit, size & boundaries).
 """
-function writedomain(group, domain::Domain2D{BCX, BCY}; name = DOMAINGROUPNAME, warn = true) where {BCX, BCY}
+function writedomain(group, domain::Domain2D{BCX, BCY}; name = DOMAINGROUPNAME, warn = IOWARN[]) where {BCX, BCY}
     wgroup = gcreate(group, name)
     wgroup[DOMAINSIZENAME] = domain.size
     wgroup[DOMAINBCNAME] = [string(BCX), string(BCY)]
@@ -372,11 +374,11 @@ function writedomain(group, domain::Domain2D{BCX, BCY}; name = DOMAINGROUPNAME,
 end
 
 """
-    writedoamin(group, domain; [name = DOMAINGROUPNAME], [warn = true])
+    writedoamin(group, domain; [name = DOMAINGROUPNAME], [warn = IOWARN[]])
 Creates a new subgroup called `name` in `group` and fills it withthe static properties
 of the domain (to wit, size & boundaries).
 """
-function writedomain(group, domain::Domain3D{BCX, BCY, BCZ}; name = DOMAINGROUPNAME, warn = true) where {BCX, BCY, BCZ}
+function writedomain(group, domain::Domain3D{BCX, BCY, BCZ}; name = DOMAINGROUPNAME, warn = IOWARN[]) where {BCX, BCY, BCZ}
     dgroup = gcreate(group, name)
     dgroup[DOMAINSIZENAME] = domain.size
     dgroup[DOMAINBCNAME] = [string(BCX), string(BCY), string(BCZ)]
@@ -386,7 +388,7 @@ end
 # writesnap (snapshots)
 
 """
-    writesnap(group, sim; name = nextsnapname(group), [warn = true], kwargs...)
+    writesnap(group, sim; name = nextsnapname(group), [warn = IOWARN[]], kwargs...)
 Creates a new subgroup called `name` in `group` and saves a snapshot of the current
 state of the dynamical properties of `sim`.
 
@@ -409,7 +411,7 @@ function _writesnap_common(group, sim::Simulation{PTC, PT};
     name = nextsnapname(group),
     isunion = (PT isa Union),
     dumprng = !(sim.rng isa Random._GLOBAL_RNG),
-    warn = true
+    warn = IOWARN[]
   ) where {PTC, PT}
     snapgroup = gcreate(group, name)
 
@@ -434,12 +436,12 @@ function _writesnap_common(group, sim::Simulation{PTC, PT};
 end
 
 """
-    writepgroup(group, particles::Vector{PT}, sim; [name = string(PT)], [warn = true])
+    writepgroup(group, particles::Vector{PT}, sim; [name = string(PT)], [warn = IOWARN[]])
 Tries to serialize the `particles` into a new subgroup `name` of `group`.
 
 Do not expect this to work if `PT` is a Union.
 """
-function writepgroup(group, particles::AbstractVector{PT}, sim::Simulation; name = string(PT), warn = true) where {PT}
+function writepgroup(group, particles::AbstractVector{PT}, sim::Simulation; name = string(PT), warn = IOWARN[]) where {PT}
     pgroup = gcreate(group, name)
     ExportUtils.serialize!(pgroup, particles; sim = sim)
 end
@@ -447,26 +449,26 @@ end
 # writefinal (final things)
 
 """
-    writefinal(group, sim; [warn = true])
+    writefinal(group, sim; [warn = IOWARN[]])
 Write the information that is only available at the end of the simulation.
 
 At the moment, this includes the registrar and the parameter vector
 """
-function writefinal(group, sim::Simulation; warn = true)
+function writefinal(group, sim::Simulation; warn = IOWARN[])
     writedict(group, Dict(sim.registrar); name = REGNAME)
     writeparams(group[PARAMGROUPNAME], sim.params; warn)
 end
 
 
 """
-    writeparams(group, params; [warn = true])
+    writeparams(group, params; [warn = IOWARN[]])
 Tries to serialize the `params` into a new subgroup of `group` named i, the index
 of the param struct .
 
 In contrast to [`writepgroup`](@ref), this will actually work for abstract/union
 eltypes.
 """
-function writeparams(group, params::AbstractVector{PT}; warn = true) where {PT<:AbstractParams}
+function writeparams(group, params::AbstractVector{PT}; warn = IOWARN[]) where {PT<:AbstractParams}
     for (i, p) ∈ enumerate(params)
         if string(i) ∉ keys(group)
             paramgroup = gcreate(group, string(i))
@@ -477,7 +479,7 @@ function writeparams(group, params::AbstractVector{PT}; warn = true) where {PT<:
 end
 
 # TODO: circular references? what circular references?
-function writedict(group, dict::AbstractDict; name, warn = true)
+function writedict(group, dict::AbstractDict; name, warn = IOWARN[])
     dgroup = gcreate(group, name)
     for (k, v) ∈ pairs(dict)
         if v isa AbstractDict
@@ -494,13 +496,13 @@ end
 ## Convenience function for reading things
 
 """
-    readsim(filename; snap, [warn = true], [kwargs...])
-    readsim(f; snap, [warn = true], [kwargs...])
+    readsim(filename; snap, [warn = IOWARN[]], [kwargs...])
+    readsim(f; snap, [warn = IOWARN[]], [kwargs...])
 Read a simulation from file and load the specified snapshot.
 
 Additional keyword arguments are passed through to [`readstatic!`](@ref) and [`readsnap`](@ref).
 """
-function readsim(f; snap, warn = true, kwargs...)
+function readsim(f; snap, warn = IOWARN[], kwargs...)
     sim = readstatic(f; warn, kwargs...)
     try
         readsnap!(sim, f, snap; warn, kwargs...)
@@ -525,7 +527,7 @@ end
 # readstatic (simulation)
 
 """
-    readstatic(simgroup; [time], [warn = true]) → sim::Simulation
+    readstatic(simgroup; [time], [warn = IOWARN[]]) → sim::Simulation
 Reconstructs a [`Simulation`](@ref) from the information saved in `simgroup`.
 
 To load a specific snapshot into the `Simulation`, use [`readsnap!`](@ref).
@@ -535,7 +537,7 @@ To load a specific snapshot into the `Simulation`, use [`readsnap!`](@ref).
   * `warn` turns on various warning messages. Defaults to `true`
 """
 
-function readstatic(simgroup; time = (0.0, 0), warn::Bool = true, version = get_version(simgroup))
+function readstatic(simgroup; time = (0.0, 0), warn::Bool = IOWARN[], version = get_version(simgroup))
 
     if version < v"0.2.0-alpha"
         # legacy import for InPartS v1 files (probably only works for CG models)
@@ -584,10 +586,10 @@ end
 
 
 """
-    readdomain(group; [warn = true])
+    readdomain(group; [warn = IOWARN[]])
 Reconstructs a [`Domain2D`](@ref) or [`Domain3D`](@ref) from `group`.
 """
-function readdomain(group; warn = true)
+function readdomain(group; warn = IOWARN[])
     size = group[DOMAINSIZENAME]
     boundarystrings = group[DOMAINBCNAME]
     boundaries = InPartS.reconstruct_subtype.(boundarystrings, Boundary)
@@ -597,14 +599,14 @@ end
 
 
 """
-    readtype(pt; [super], [warn = true]) → T::Type
+    readtype(pt; [super], [warn = IOWARN[]]) → T::Type
 Reconstructs a type from a name as encoded with [`writetype`](@ref)
 
 ### Optional arguments
  * `super` limits the type reconstruction to children of a supertype. Defaults to `Any`
  * `mod` module in which to look for the type name. Defaults to `Main`
 """
-function readtype(pt; super = Any, mod = Main, warn = true)
+function readtype(pt; super = Any, mod = Main, warn = IOWARN[])
     #pt = simgroup[name]
     if pt isa Vector
         return Union{InPartS.reconstruct_subtype.(pt, super; mod)...}
@@ -615,14 +617,14 @@ end
 
 
 """
-    readdict(group; [maxdepth], [warn = true]) → d::Union{Missing, Dict{String, Any}}
+    readdict(group; [maxdepth], [warn = IOWARN[]]) → d::Union{Missing, Dict{String, Any}}
 Recursively reads `group` into a dictionary until depth `maxdepth`. If
 `maxdepth` is reached, all data is replaced by `missing`
 
 ## Optional arguments
   * `maxdepth` Sets the maximum recursion depth. Defaults to 32.
 """
-function readdict(group; maxdepth = 32, warn = true)
+function readdict(group; maxdepth = 32, warn = IOWARN[])
     if maxdepth == 0
         warn && @warn "readdict: Maximum recursion depth reached"
         return missing
@@ -644,8 +646,8 @@ end
 
 
 """
-    readrng(group; [complete], [warn = true]) → rng::AbstractRNG
-    readrng(group, T ; [complete], [warn = true]) → rng::T
+    readrng(group; [complete], [warn = IOWARN[]]) → rng::AbstractRNG
+    readrng(group, T ; [complete], [warn = IOWARN[]]) → rng::T
 Reconstructs the RNG from `group`. Optionally specifiy the RNG type.
 
 ## Optional Arguments
@@ -654,7 +656,7 @@ Reconstructs the RNG from `group`. Optionally specifiy the RNG type.
 """
 readrng(group; mod = InPartS.Random, kwargs...) = readrng(group, InPartS.reconstruct_subtype(group["_type"], AbstractRNG; mod); kwargs...)
 
-function readrng(group, T::Type{<:AbstractRNG}; complete::Bool = false, warn = true)
+function readrng(group, T::Type{<:AbstractRNG}; complete::Bool = false, warn = IOWARN[])
     if complete
         if group["_rng_complete"]
             return _deserialize_rng_full(group, T; warn)
@@ -669,10 +671,10 @@ end
 
 
 """
-    readparams(group; [warn = true]) → params
+    readparams(group; [warn = IOWARN[]]) → params
 Reconstructs the parameter structs from `group`
 """
-function readparams(group; warn = true)
+function readparams(group; warn = IOWARN[])
     indices = Vector{Int}()
     params = Vector{AbstractParams}()
     for n ∈ keys(group)
@@ -689,7 +691,7 @@ end
 @deprecate readsnap(snapgroup; sim::Simulation) readsnap(snapgroup, sim)
 
 """
-    readsnap(snapgroup, sim::Simulation; [warn = true]) → particles, time, step, next_id, rng[,...]
+    readsnap(snapgroup, sim::Simulation; [warn = IOWARN[]]) → particles, time, step, next_id, rng[,...]
 Loads the snapshot from `snapgroup`, using additional information from `sim`. Returns a tuple of
 state information that can be applied to the simulation using [`setstate`]((@ref)
 """
@@ -697,7 +699,7 @@ readsnap(snapgroup, sim::Simulation; kwargs...) = _readsnap_common(snapgroup, si
 
 # this function deals with particles, times, steps, registrars etc.
 # and can be used when overwriting readsnap for custom PTCs
-function _readsnap_common(snapgroup, sim::Simulation{PTC, PT}; warn = true, ignorelist = SNAPDATANAMES) where {PTC, PT}
+function _readsnap_common(snapgroup, sim::Simulation{PTC, PT}; warn = IOWARN[], ignorelist = SNAPDATANAMES) where {PTC, PT}
     time = snapgroup[SNAPTIMENAME]
     step = snapgroup[SNAPSTEPNAME]
     next_id = snapgroup[SNAPNEXTIDNAME]
@@ -733,7 +735,7 @@ setstate!(sim::Simulation, state; kwargs...) = _setstate_common!(sim, state; kwa
 
 # this function deals with particles, times, steps, registrars etc.
 # and can be used when overwriting setstate! for custom PTCs
-function _setstate_common!(sim::Simulation, state; warn = false)
+function _setstate_common!(sim::Simulation, state; warn = IOWARN[])
     particles, time, step, next_id, rng = state
     setparticles!(sim.particles, particles)
     sim.t = time
@@ -749,12 +751,12 @@ end
 
 
 """
-    readsnap!(sim::Simulation, f, snapname::String; [warn = true])
-    readsnap!(sim::Simulation, f, snapid::Int; [warn = true])
-    readsnap!(sim::Simulation, snapgroup; [warn = true])
+    readsnap!(sim::Simulation, f, snapname::String; [warn = IOWARN[]])
+    readsnap!(sim::Simulation, f, snapid::Int; [warn = IOWARN[]])
+    readsnap!(sim::Simulation, snapgroup; [warn = IOWARN[]])
 Loads the snapshot data from `snapgroup` into `sim`.
 """
-readsnap!(sim::Simulation, snapgroup; warn::Bool = true) = setstate!(sim, readsnap(snapgroup, sim; warn); warn)
+readsnap!(sim::Simulation, snapgroup; warn::Bool = IOWARN[]) = setstate!(sim, readsnap(snapgroup, sim; warn); warn)
 
 function readsnap!(sim::Simulation, filename::String, snap; kwargs...)
     dfopen(filename) do f
@@ -762,7 +764,7 @@ function readsnap!(sim::Simulation, filename::String, snap; kwargs...)
     end
 end
 
-function readsnap!(sim::Simulation, f, snap; warn::Bool=true)
+function readsnap!(sim::Simulation, f, snap; warn::Bool=IOWARN[])
     if length(keys(f)) == 1
         legacy_readsnap!(sim, f, snap; warn=warn)
     else
@@ -809,22 +811,22 @@ lastfullsnap(filename::String) = dfopen(lastfullsnap, filename)
 
 ## RNG stuff
 
-function _serialize_rng_partial!(group, @nospecialize rng::AbstractRNG; warn = true)
+function _serialize_rng_partial!(group, @nospecialize rng::AbstractRNG; warn = IOWARN[])
     warn && @warn "Partial serialization not implemented for $(typeof(rng))"
     return
 end
 
-function _serialize_rng_full!(group, @nospecialize rng::AbstractRNG; warn = true)
+function _serialize_rng_full!(group, @nospecialize rng::AbstractRNG; warn = IOWARN[])
     warn && @warn "Full serialization not implemented for $(typeof(rng)), falling back to partial serialization"
     return _serialize_rng_partial!(group, rng; warn)
 end
 
-function _serialize_rng_partial!(group, rng::MersenneTwister; warn = true)
+function _serialize_rng_partial!(group, rng::MersenneTwister; warn = IOWARN[])
     group["seed"] = rng.seed
     return
 end
 
-function _serialize_rng_full!(group, rng::MersenneTwister; warn = true)
+function _serialize_rng_full!(group, rng::MersenneTwister; warn = IOWARN[])
     _serialize_rng_partial!(group, rng)
     group["vals"] = rng.vals
     group["ints"] = string.(rng.ints)
@@ -835,20 +837,20 @@ function _serialize_rng_full!(group, rng::MersenneTwister; warn = true)
 end
 
 
-function _deserialize_rng_partial(group, @nospecialize T::Type{<:AbstractRNG}; warn = true)
+function _deserialize_rng_partial(group, @nospecialize T::Type{<:AbstractRNG}; warn = IOWARN[])
     warn && @warn "Partial deserialization not implemented for $(T), creating new instance"
     return T()
 end
 _deserialize_rng_partial(group, T::Type{Random._GLOBAL_RNG}) = Random.GLOBAL_RNG
 
-function _deserialize_rng_full(group, @nospecialize T::Type{<:AbstractRNG}; warn = true)
+function _deserialize_rng_full(group, @nospecialize T::Type{<:AbstractRNG}; warn = IOWARN[])
     warn && @warn "Full deserialization not implemented for $(T), falling back to partial deserialization"
     return _deserialize_rng_partial(group, T)
 end
 
-_deserialize_rng_partial(group, ::Type{MersenneTwister}; warn = true) = MersenneTwister(group["seed"])
+_deserialize_rng_partial(group, ::Type{MersenneTwister}; warn = IOWARN[]) = MersenneTwister(group["seed"])
 
-function _deserialize_rng_full(group, ::Type{MersenneTwister}; warn = true)
+function _deserialize_rng_full(group, ::Type{MersenneTwister}; warn = IOWARN[])
     rng = _deserialize_rng_partial(group, MersenneTwister)
 
     rng.vals = group["vals"]
@@ -886,7 +888,7 @@ _deserialize_rng_partial(group, T::Type{Xoshiro}; kwargs...) = _serialize_rng_fu
 ################################################################################
 
 
-function writeobstacles(group,  sim::Simulation; warn = true)
+function writeobstacles(group,  sim::Simulation; warn = IOWARN[])
     sov = obstacles(sim)
     obsgroup = gcreate(group, OBSTACLEGROUPNAME)
     for (i, so) ∈ enumerate(sov)
@@ -896,7 +898,7 @@ function writeobstacles(group,  sim::Simulation; warn = true)
     end
 end
 
-function readobstacles(group; warn = true)
+function readobstacles(group; warn = IOWARN[])
     indices = Vector{Int}()
     params = Vector{AbstractObstacle}()
     for n ∈ keys(group)
-- 
GitLab


From deb99ca036e01dc8a7bfb7a8e14c1742ec3ef1bb Mon Sep 17 00:00:00 2001
From: Lukas Hupe <lukas.hupe@ds.mpg.de>
Date: Mon, 19 Jun 2023 11:51:06 +0200
Subject: [PATCH 9/9] slightly more useful environment arg

---
 src/InPartS.jl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/InPartS.jl b/src/InPartS.jl
index 6cc9123..d42e5c9 100644
--- a/src/InPartS.jl
+++ b/src/InPartS.jl
@@ -203,7 +203,7 @@ function __init__()
     end
 
     # set IOWARN from environment
-    InPartS.IOWARN[] = get(ENV, "INPARTS_IOWARN", true)
+    InPartS.IOWARN[] = get(ENV, "INPARTS_IOWARN", "1") != "0"
 
     return
 end
-- 
GitLab