Skip to content

Commit 54e7beb

Browse files
authored
Merge pull request #163 from CliMA/kp/postprocess
Add `analyze_iteration` and `postprocess_g_ensemble`
2 parents 06f3c30 + 322226e commit 54e7beb

File tree

6 files changed

+83
-3
lines changed

6 files changed

+83
-3
lines changed

docs/src/api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
```@docs
66
ClimaCalibrate.forward_model
77
ClimaCalibrate.observation_map
8+
ClimaCalibrate.analyze_iteration
9+
ClimaCalibrate.postprocess_g_ensemble
810
```
911

1012
## Worker Interface

docs/src/quickstart.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,41 @@ function observation_map(iteration)
4646
return G_ensemble
4747
end
4848
```
49+
50+
### Optional postprocessing
51+
52+
It may be the case that `observation_map` is insufficient as you need to more information,
53+
such as information from the `ekp` object to compute `G_ensemble`. Further postprocessing of the
54+
`G_ensemble` object can be done by implementing the `postprocess_g_ensemble` as shown
55+
below.
56+
57+
```julia
58+
function postprocess_g_ensemble(ekp, g_ensemble, prior, output_dir, iteration)
59+
return g_ensemble
60+
end
61+
```
62+
63+
After each evaluation of the observation map and before updating the ensemble, it may be
64+
helpful to print the errors from the `ekp` object or plot `G_ensemble`. This can be done
65+
by implementing the `analyze_iteration` as shown below.
66+
67+
```julia
68+
function ClimaCalibrate.analyze_iteration(
69+
ekp,
70+
g_ensemble,
71+
prior,
72+
output_dir,
73+
iteration,
74+
)
75+
@info "Analyzing iteration"
76+
@info "Iteration $iteration"
77+
@info "Current mean parameter: $(EnsembleKalmanProcesses.get_ϕ_mean_final(prior, ekp))"
78+
@info "g_ensemble: $g_ensemble"
79+
@info "output_dir: $output_dir"
80+
return nothing
81+
end
82+
```
83+
4984
### Parameters
5085

5186
Every parameter that is being calibrated requires a prior distribution to sample from.

src/ekp_interface.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,11 @@ Compute the observation map and update the given EKP object.
377377
"""
378378
function observation_map_and_update!(ekp, output_dir, iteration, prior)
379379
g_ensemble = observation_map(iteration)
380+
g_ensemble =
381+
postprocess_g_ensemble(ekp, g_ensemble, prior, output_dir, iteration)
380382
save_G_ensemble(output_dir, iteration, g_ensemble)
381383
terminate = update_ensemble!(ekp, g_ensemble, output_dir, iteration, prior)
384+
analyze_iteration(ekp, g_ensemble, prior, output_dir, iteration)
382385
return terminate
383386
end
384387

src/model_interface.jl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import EnsembleKalmanProcesses as EKP
22
import YAML
33

4-
export forward_model, observation_map
4+
export forward_model, observation_map, analyze_iteration
55

66
"""
77
forward_model(iteration, member)
@@ -24,3 +24,30 @@ This function must be implemented for each calibration experiment.
2424
function observation_map(iteration)
2525
error("observation_map not implemented")
2626
end
27+
28+
"""
29+
analyze_iteration(ekp, g_ensemble, prior, output_dir, iteration)
30+
31+
After each evaluation of the observation map and before updating the ensemble,
32+
`analyze_iteration` is evaluated.
33+
34+
This function is optional to implement.
35+
36+
For example, one may want to print information from the `eki` object or plot
37+
`g_ensemble`.
38+
"""
39+
function analyze_iteration(ekp, g_ensemble, prior, output_dir, iteration)
40+
@info "Mean constrained parameter(s): $(EKP.get_ϕ_mean_final(prior, ekp))"
41+
@info "Covariance-weighted error: $(last(EKP.get_error(ekp)))"
42+
return nothing
43+
end
44+
45+
"""
46+
postprocess_g_ensemble(ekp, g_ensemble, prior, output_dir, iteration)
47+
48+
Postprocess `g_ensemble` after evaluating the observation map and before
49+
updating the ensemble.
50+
"""
51+
function postprocess_g_ensemble(ekp, g_ensemble, prior, output_dir, iteration)
52+
return g_ensemble
53+
end

test/julia_backend.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ function CAL.observation_map(iteration)
5050
return G_ensemble
5151
end
5252

53+
function CAL.analyze_iteration(ekp, g_ensemble, prior, output_dir, iteration)
54+
@info "Analyzing iteration"
55+
@info "Iteration $iteration"
56+
@info "Current mean constrained parameter: $(EKP.get_ϕ_mean_final(prior, ekp))"
57+
@info "g_ensemble: $g_ensemble"
58+
@info "output_dir: $output_dir"
59+
return nothing
60+
end
61+
5362
# Test!
5463
ekp = CAL.calibrate(CAL.JuliaBackend, experiment_config)
5564

test/model_interface.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import ClimaCalibrate
33
using EnsembleKalmanProcesses.ParameterDistributions
44
using Test
55

6-
# Tests for ensuring ClimaCalibrate has protected interfaces. The API tested below must be defined for each model,
7-
# otherwise ClimaCalibrate will throw an error.
6+
# Tests for ensuring ClimaCalibrate has protected interfaces. The API tested
7+
# below must be defined for each model, otherwise ClimaCalibrate will throw an
8+
# error. However, `analyze_iteration` and `postprocess_g_ensemble` are
9+
# optional to implement.
810

911
@testset "Model Interface stubs" begin
1012
@test_throws ErrorException("forward_model not implemented") ClimaCalibrate.forward_model(
@@ -14,4 +16,6 @@ using Test
1416
@test_throws ErrorException("observation_map not implemented") ClimaCalibrate.observation_map(
1517
1,
1618
)
19+
@test isnothing(ClimaCalibrate.analyze_iteration(1, 1, 1, 1, 1))
20+
@test 2 == ClimaCalibrate.postprocess_g_ensemble(1, 2, 3, 4, 5)
1721
end

0 commit comments

Comments
 (0)