Skip to content

Commit 7bc1f02

Browse files
committed
Add run_initial_ensemble
1 parent 3c73712 commit 7bc1f02

File tree

1 file changed

+87
-1
lines changed

1 file changed

+87
-1
lines changed

src/ekp_interface.jl

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import EnsembleKalmanProcesses.TOMLInterface as TI
88

99
export get_prior, initialize, update_ensemble, save_G_ensemble
1010
export path_to_ensemble_member,
11-
path_to_model_log, path_to_iteration, parameter_path, load_latest_ekp
11+
path_to_model_log,
12+
path_to_iteration,
13+
parameter_path,
14+
load_latest_ekp,
15+
run_initial_ensemble
1216

1317
"""
1418
load_ekp_struct(output_dir, iteration)
@@ -308,6 +312,88 @@ function save_eki_and_parameters(eki, output_dir, iteration, prior)
308312
JLD2.save_object(ekp_path(output_dir, iteration), eki)
309313
end
310314

315+
"""
316+
run_initial_ensemble(
317+
backend::AbstractBackend,
318+
process::EKP.Process,
319+
prior::PD.ParameterDistribution,
320+
output_dir;
321+
ensemble_size = 0,
322+
rng = MersenneTwister(1234),
323+
run_forward_models_kwargs = (),
324+
)
325+
326+
Run the initial ensemble without needing the `EKP.EnsembleKalmanProcess`
327+
object and return the unconstrained parameters of the ensemble.
328+
329+
This is useful if the observations depend on the grid resolution of the
330+
simulation data as the first ensemble can be ran, the observations can be
331+
generated, and the `EKP.EnsembleKalmanProcess` object can be created afterward.
332+
333+
Depending on the `process`, the ensemble size will automatically be determined
334+
(e.g. `EKP.Unscented` or `EKP.TransformUnscented`).
335+
336+
!!! warning "Passing initial ensemble to EKP constructor"
337+
If the process is `EKP.Unscented` or `EKP.TransformUnscented`, you should
338+
not pass it to the `EnsembleKalmanProcess` constructor. Otherwise, you
339+
should pass the initial ensemble to the `EnsembleKalmanProcess` constructor.
340+
If you do not, the calibration results may be incorrect.
341+
"""
342+
function run_initial_ensemble( # TODO: Maybe move this to backends?
343+
backend,
344+
process::EKP.Process,
345+
prior::PD.ParameterDistribution,
346+
output_dir;
347+
ensemble_size = 0,
348+
rng = Random.MersenneTwister(1234),
349+
run_forward_models_kwargs = (),
350+
)
351+
# Cannot use last_completed_iteration because the JLD2 files storing the
352+
# G ensemble object and the ekp objects will never exist when calling this
353+
# function
354+
iter0_path = joinpath(output_dir, "iteration_000")
355+
if isdir(iter0_path)
356+
@info "Detected first iteration. Not running the initial ensemble. If this is not the case, then delete the directory $iter0_path."
357+
end
358+
359+
initial_ensemble = _construct_intial_unconstrained_ensemble(
360+
process,
361+
prior,
362+
ensemble_size = ensemble_size,
363+
rng = rng,
364+
)
365+
366+
# The size of initial_ensemble is (number of parameters, ensemble members)
367+
# For some processes, the ensemble_size is ignored
368+
ensemble_size = last(size(initial_ensemble))
369+
370+
param_dict = get_param_dict(prior)
371+
TI.save_parameter_ensemble(
372+
initial_ensemble,
373+
prior,
374+
param_dict,
375+
output_dir,
376+
DEFAULT_PARAMETER_FILE,
377+
0,
378+
)
379+
380+
# Cannot save eki object because the goal of this function is to be able
381+
# to run the first ensemble without the eki object
382+
JLD2.save_object(
383+
joinpath(path_to_iteration(output_dir, 0), "prior.jld2"),
384+
prior,
385+
)
386+
387+
run_forward_models(
388+
backend,
389+
0,
390+
ensemble_size;
391+
output_dir = output_dir,
392+
run_forward_models_kwargs...,
393+
)
394+
return initial_ensemble
395+
end
396+
311397
"""
312398
_construct_intial_unconstrained_ensemble(
313399
process::EKP.Process,

0 commit comments

Comments
 (0)