Skip to content

Commit 33cefaa

Browse files
authored
Merge pull request #197 from CliMA/kp/reconstruct
Add ObservationRecipe.reconstruct_g_mean_final
2 parents 6087116 + 8d21d61 commit 33cefaa

File tree

5 files changed

+404
-1
lines changed

5 files changed

+404
-1
lines changed

docs/src/api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ ClimaCalibrate.ObservationRecipe.SVDplusDCovariance
8080
ClimaCalibrate.ObservationRecipe.SVDplusDCovariance(sample_dates)
8181
ClimaCalibrate.ObservationRecipe.covariance
8282
ClimaCalibrate.ObservationRecipe.observation
83+
ClimaCalibrate.ObservationRecipe.short_names
84+
ClimaCalibrate.ObservationRecipe.get_metadata_for_nth_iteration
85+
ClimaCalibrate.ObservationRecipe.reconstruct_g_mean_final
8386
ClimaCalibrate.ObservationRecipe.seasonally_aligned_yearly_sample_date_ranges
8487
ClimaCalibrate.ObservationRecipe.change_data_type
8588
```

docs/src/observation_recipe.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,29 @@ obs = ObservationRecipe.observation(
8888
)
8989
```
9090

91+
## Metadata
92+
93+
!!! note
94+
Metadata in `EKP.observation` is only added with versions of
95+
EnsembleKalmanProcesses later than v2.4.2.
96+
"""
97+
98+
When creating an observation with [`ObservationRecipe.observation`](@ref
99+
ClimaCalibrate.ObservationRecipe.observation), metadata is extracted from the
100+
`OutputVar`s and attached to the observation. The metadata for each observation
101+
can be accessed with `EKP.get_metadata(obs::EKP.Observation)` and the metadata
102+
for each iteration can be accessed with
103+
[`ObservationRecipe.get_metadata_for_nth_iteration`](@ref
104+
ClimaCalibrate.ObservationRecipe.get_metadata_for_nth_iteration). The metadata
105+
can be used with `ClimaAnalysis.unflatten` to reconstruct the original
106+
`OutputVar` before flattening. See the ClimaAnalysis
107+
[documentation](https://clima.github.io/ClimaAnalysis.jl/dev/api/#FlatVar) about
108+
`ClimaAnalysis.FlatVar` for more information.
109+
110+
`ObservationRecipe` provides a helper function for reconstructing the mean
111+
forward map evaluation with [`ObservationRecipe.reconstruct_g_mean_final`](@ref
112+
ClimaCalibrate.ObservationRecipe.reconstruct_g_mean_final).
113+
91114
## Frequently asked questions
92115

93116
**Q: I need to compute `g_ensemble` and I do not know how the data of the `OutputVar`s is flattened.**

ext/ClimaAnalysisExt.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,4 +506,63 @@ function _lat_weights_var(var::OutputVar; min_cosd_lat = 0.1)
506506
return ClimaAnalysis.remake(var, data = lat_weights)
507507
end
508508

509+
"""
510+
get_metadata_for_nth_iteration(obs_series, N)
511+
512+
For the `N`th iteration, get the metadata of the observation(s) being processed.
513+
"""
514+
function ObservationRecipe.get_metadata_for_nth_iteration(obs_series, N)
515+
num_epoches = EKP.get_length_epoch(obs_series)
516+
# EKP.get_minibatch fails with N > num_epoches, so we use mod1 to go back to
517+
# the first epoch which seems consistent with what EKP does
518+
minibatch_indices = EKP.get_minibatch(obs_series, mod1(N, num_epoches))
519+
minibatch_obs = EKP.get_observations(obs_series)[minibatch_indices]
520+
metadata_vec = map(obs -> EKP.get_metadata(obs), minibatch_obs)
521+
return vcat(metadata_vec...)
522+
end
523+
524+
"""
525+
reconstruct_g_mean_final(ekp::EKP.EnsembleKalmanProcess,
526+
observation::EKP.Observation)
527+
528+
Reconstruct the mean forward model evaluation at the last iteration as a
529+
vector of `OutputVar`s.
530+
531+
This function assumes `observation` contains the necessary metadata to reconstruct
532+
the `OutputVar`s. Note that the metadata comes from the observations.
533+
"""
534+
function ObservationRecipe.reconstruct_g_mean_final(
535+
ekp::EKP.EnsembleKalmanProcess,
536+
)
537+
obs_series = EKP.get_observation_series(ekp)
538+
metadatas = ObservationRecipe.get_metadata_for_nth_iteration(
539+
obs_series,
540+
EKP.get_N_iterations(ekp),
541+
)
542+
eltype(metadatas) <: ClimaAnalysis.Var.Metadata || error(
543+
"Getting the short names from an observation is only supported with metadata from ClimaAnalysis",
544+
)
545+
g_mean = EKP.get_g_mean_final(ekp)
546+
547+
# Check if length of g ensemble is the same as the length of the data in the metadatas
548+
total_metadata_length =
549+
sum(ClimaAnalysis.Var._data_size(metadata) for metadata in metadatas)
550+
length(g_mean) != total_metadata_length && error(
551+
"Length of g_mean_final is not the same as the length of all the metadata",
552+
)
553+
554+
# Reconstruct each OutputVar from the metadata
555+
index = Ref(1)
556+
vars = map(metadatas) do metadata
557+
data_size = ClimaAnalysis.Var._data_size(metadata)
558+
start_idx = index[]
559+
index[] += data_size
560+
ClimaAnalysis.unflatten(
561+
metadata,
562+
g_mean[start_idx:(start_idx + data_size - 1)],
563+
)
564+
end
565+
return vars
566+
end
567+
509568
end

src/observation_recipe.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export ScalarCovariance,
77
observation,
88
short_names,
99
seasonally_aligned_yearly_sample_date_ranges,
10-
change_data_type
10+
change_data_type,
11+
get_metadata_for_nth_iteration,
12+
reconstruct_g_mean_final
1113

1214
import Dates
1315

@@ -282,4 +284,8 @@ function seasonally_aligned_yearly_sample_date_ranges end
282284

283285
function change_data_type end
284286

287+
function reconstruct_g_mean_final end
288+
289+
function get_metadata_for_nth_iteration end
290+
285291
end

0 commit comments

Comments
 (0)