diff --git a/CHANGELOG.md b/CHANGELOG.md
index 509ad8a93b98fa2f8f958afe2f7ebea813cde81c..7866172342322b2fc9b53f16d4493a3e6f5bccf4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## v0.3.12
+ - bugfix: particletype for POLOContainers fixed
+ - bugfix: ParticleContainers can now safely return distinct objects on succesive calls of alldynamicobjects (as originally intended)
+ - bugfix: a default full serialization condition interval for the global RNG has been added (the condition is `false`)
+ - added convenience functions for data file handling
+
 ## v0.3.11
  - bugfix: boxing for 3D inverted DiskObstacles has been fixed
  - fallback constructors for obstacles with `inverted` field now work without having to specify type parameters
diff --git a/Project.toml b/Project.toml
index b3b97c01d1156629452aa4b370a71c5eadf7315c..507e18aa3b461c1ac55b36b718006ac3eaad02c0 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.3.11"
+version = "0.3.12"
 
 [deps]
 FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
diff --git a/src/evolve.jl b/src/evolve.jl
index 3b6c385c89b3bcfb21f7d2cc7762c23836aacedd..69090c589d255d8a1322d886b5e78b577273fce3 100644
--- a/src/evolve.jl
+++ b/src/evolve.jl
@@ -113,13 +113,13 @@ function propagate!(sim::Simulation, dt)
     iter2 = alldynamicobjects(sim)
     if isthreaded(sim)
         Threads.@threads for n in eachindex(iter2)
-            particle = iter[n]
+            particle = iter2[n]
             fold_back!(particle, sim.domain)
             reset!(particle, sim)
         end
     else
         for n in eachindex(iter2)
-            particle = iter[n]
+            particle = iter2[n]
             fold_back!(particle, sim.domain)
             reset!(particle, sim)
         end
diff --git a/src/export/generic.jl b/src/export/generic.jl
index 176cb88c25cf8c3ad9722c4a39bab8a7bc8031bc..20da7757358f487d6896531d14e328e1783b966b 100644
--- a/src/export/generic.jl
+++ b/src/export/generic.jl
@@ -1,31 +1,37 @@
 export SaveCallback, BackupCallback, readsim
 
 ## Datafile interface definitions
-"""
-    dfopen(backend, filename; kwargs...)
-    dfopen(backend, filename, mode::String)
-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")
-
-function dfopen(filename::String, mode::String="r")
+    exportbackend(filename)
+A bit of sugar around `FileIO.query`.
+"""
+function backend(filename::String)
     s = FileIO.query(filename)
     modulename = typeof(s).parameters[1].parameters[1]
     if !isdefined(Main, modulename)
         error("File appears to be a $modulename file but library is not loaded.")
     end
     if modulename == :JLD2
-        return dfopen(JLD2.JLDFile, filename, mode)
+        return JLD2.JLDFile
     elseif modulename == :HDF5
-        return dfopen(H5Wrapper, filename, mode)
+        return H5Wrapper
     else
         error("This backend is not implemented")
     end
 end
 
+"""
+    dfopen(backend, filename; kwargs...)
+    dfopen(backend, filename, mode::String)
+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)
+
 function dfopen(f::Function, args...; kwargs...)
     file = dfopen(args...; kwargs...)
     try
@@ -88,7 +94,7 @@ 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 = true])
 Create a [`SaveCallback`](@ref) that periodically saves simulation snapshots to `filename`.
 
 Uses a [`PeriodicCallback`](@ref) with the given `interval` and `offset`.
@@ -96,6 +102,7 @@ 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}(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
@@ -111,6 +118,8 @@ function (s::SaveCallback)(sim::Simulation)
 end
 
 # Xoshiros are small and can be dumped every snapshot, MersenneTwisters are huge and shouldn't be written as often
+# the GLOBAL_RNG can't be properly serialized anyway so we just give up
+_defaultdumprng(::Random._GLOBAL_RNG) = false 
 _defaultdumprng(::Random.Xoshiro, _) = true
 _defaultdumprng(::Random.MersenneTwister, s) = (s.nextsnap % 10 == 0)
 
@@ -182,6 +191,8 @@ Uses a [`RealTimeCallback`](@ref) with the given `interval` and `offset`.
 """
 BackupCallback(backend, filename; interval = 300.0, offset = 0.0, warn = true) =
     BackupCallback{backend}(RealTimeCallback(interval, sim -> true; offset = offset), filename, warn)
+BackupCallback(filename::String; kwargs...) =
+    BackupCallback(backend(filename), filename; kwargs...)
 
 
 function(cb::BackupCallback{FT})(sim::Simulation) where FT
@@ -403,7 +414,7 @@ end
 ## Convenience function for reading things
 
 """
-    readsim(backend, filename; snap, [warn = true], [kwargs...])
+    readsim(filename; snap, [warn = true], [kwargs...])
     readsim(f; snap, [warn = true], [kwargs...])
 Read a simulation from file and load the specified snapshot.
 
@@ -420,8 +431,8 @@ function readsim(f; snap, warn = true, kwargs...)
     return sim
 end
 
-function readsim(backend, filename; kwargs...)
-    f = dfopen(backend, filename, "r")
+function readsim(filename::AbstractString; kwargs...)
+    f = dfopen(filename, "r")
     sim = missing
     try
         sim = readsim(f; kwargs...)
@@ -649,6 +660,12 @@ function readsnap!(sim::Simulation, snapgroup; warn::Bool = true)
     return sim
 end
 
+function readsnap!(sim::Simulation, filename::String, snap; kwargs...)
+    dfopen(filename) do f
+        readsnap!(sim, f, snap; kwargs...)
+    end
+end
+
 function readsnap!(sim::Simulation, f, snap; warn::Bool=true)
     if length(keys(f)) == 1
         legacy_readsnap!(sim, f, snap; warn=warn)
@@ -665,6 +682,7 @@ function numsnaps(f)
         length(keys(f[SNAPGROUPNAME]))
     end
 end
+numsnaps(filename::String) = dfopen(numsnaps, filename)
 
 ## Stuff
 
@@ -676,8 +694,11 @@ function nextsnapname(g)
     return snaps[end] + 1
 end
 
-
-function latest_full_snap(f)
+"""
+    lastfullsnap(f)
+Returns the index of the last full snapshot (including RNG) in the file `f`.
+"""
+function lastfullsnap(f)
     for n = (numsnaps(f)-1):-1:0
         if haskey(f[SNAPGROUPNAME][string(n)], "rng")
             return n
@@ -685,6 +706,13 @@ function latest_full_snap(f)
     end
     return -1
 end
+lastfullsnap(filename::String) = dfopen(lastfullsnap, filename)
+
+function latest_full_snap(f)
+    Base.depwarn("`$(@__MODULE__).latest_full_snap` is deprecated. \
+    Use `$(@__MODULE__).lastfullsnap` instead", :latest_full_snap)
+    return lastfullsnap(f)
+end
 
 ## RNG stuff
 
diff --git a/src/particlecontainers/polocontainer.jl b/src/particlecontainers/polocontainer.jl
index a91d84059f6d83aedc45f9764f5b5d25351f6763..44202a2c775f3283c568d5cd2f361aef4cd5aeef 100644
--- a/src/particlecontainers/polocontainer.jl
+++ b/src/particlecontainers/polocontainer.jl
@@ -50,7 +50,7 @@ Base.append!(pc::POLOContainer, xs) = append!(pc.pv, xs)
 
 particles(sim::Simulation{<:POLOContainer}) = sim.particles.pv
 particles(pc::POLOContainer) = pc.pv
-particletype(::POLOContainer{PT}) where PT = PT
+particletype(::POLOContainer{<:Any, PT}) where PT = PT
 flushadditions!(pc::POLOContainer, ab::Vector) = flushadditions!(pc.pv, ab)
 flushdeletions!(pc::POLOContainer, db) = flushdeletions!(pc.pv, db)
 findbyid(pc::POLOContainer, id) = findbyid(pc.pv, id)