Skip to content

Commit a60f6c6

Browse files
Merge pull request #1082 from CliMA/ar/calibrate_land
Calibration of the land model
2 parents e4e4d7a + 629cc71 commit a60f6c6

15 files changed

+1561
-150
lines changed

.buildkite/Manifest-v1.11.toml

Lines changed: 221 additions & 49 deletions
Large diffs are not rendered by default.

.buildkite/Project.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
66
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
77
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
88
ClimaAnalysis = "29b5916a-a76c-4e73-9657-3c8fd22e65e6"
9+
ClimaCalibrate = "4347a170-ebd6-470c-89d3-5c705c0cacc2"
910
ClimaComms = "3a4d1b5c-c61d-41fd-a00a-5873ba7a1b0d"
1011
ClimaCore = "d414da3d-4745-48bb-8d80-42e94e092884"
1112
ClimaCoreMakie = "908f55d8-4145-4867-9c14-5dad1a479e4d"
@@ -17,6 +18,7 @@ ClimaUtilities = "b3f4f4ca-9299-4f7f-bd9b-81e1242a7513"
1718
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
1819
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1920
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
21+
EnsembleKalmanProcesses = "aa8a2aa5-91d8-4396-bcef-d4f2ec43552d"
2022
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
2123
Format = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
2224
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
@@ -38,11 +40,15 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
3840
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
3941
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
4042
SurfaceFluxes = "49b00bb7-8bd4-4f2b-b78c-51cd0450215f"
43+
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
4144
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4245
Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"
4346

4447
[compat]
45-
ClimaAnalysis = "0.5.16"
48+
ClimaAnalysis = "0.5.17"
4649
ClimaTimeSteppers = "0.7, 0.8"
50+
ClimaDiagnostics = "0.2.13"
51+
ClimaCalibrate = "0.0.15"
52+
EnsembleKalmanProcesses = "2.4.1"
4753
Flux = "0.15"
4854
Statistics = "1"

Artifacts.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ git-tree-sha1 = "cbbc6b3752d9cb9b667ec33cfbeb46819f8db418"
1818
[[landsea_mask_1deg.download]]
1919
sha256 = "3722b553c2fdf28a6574aea2e0b167d16ab050f34e5969ada45625b3a3ecb6da"
2020
url = "https://caltech.box.com/shared/static/b3u4dv0dsoswvqgp8y63bzj7awbhwztd.gz"
21-
21+
2222
[soil_ic_2008_50m]
2323
git-tree-sha1 = "fd9cda235e203b4235136b6dcdcd07af788c00f6"
2424

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
#PBS -N derecho_calibration
3+
#PBS -o output.txt
4+
#PBS -e error.txt
5+
#PBS -l walltime=12:00:00
6+
#PBS -l select=1:ncpus=4:ngpus=1
7+
8+
## Account number for CliMA
9+
#PBS -A UCIT0011
10+
#PBS -q main
11+
12+
export PBS_ACCOUNT="UCIT0011"
13+
export MODULEPATH="/glade/campaign/univ/ucit0011/ClimaModules-Derecho:$MODULEPATH"
14+
module load climacommon
15+
16+
export CLIMACOMMS_DEVICE="CUDA"
17+
export CLIMACOMMS_CONTEXT="SINGLETON"
18+
julia --project=.buildkite -e 'using Pkg; Pkg.instantiate(;verbose=true)'
19+
20+
julia --project=.buildkite/ experiments/calibration/calibrate_land.jl
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
######## CONFIGS ########
2+
# Most of the time users will just need to modify the settings below
3+
# Our goal is to make it flexible and easy to run different calibration setups
4+
# And to potentially iterate fast (e.g., low resolution, specific regions, few params, short simulation time...)
5+
#########################
6+
7+
ENV["JULIA_WORKER_TIMEOUT"] = "1000.0" # This is a ClimaCalibrate setting. Wait time for workers.
8+
9+
using Dates
10+
using Distributed
11+
import EnsembleKalmanProcesses as EKP
12+
import Random
13+
using ClimaLand
14+
import ClimaCalibrate: forward_model, parameter_path, path_to_ensemble_member
15+
import ClimaCalibrate as CAL
16+
rng_seed = 2
17+
rng = Random.MersenneTwister(rng_seed)
18+
FT = Float64
19+
20+
# Calibrate the land model with:
21+
const variable_list = ["swu"] # variables you want to capture by adjusting your priors
22+
const n_iterations = 10 # 1 iterations takes ~ 1.5 hour with current settings (resolution, 2 year simulation)
23+
const spinup_period = Year(1)
24+
# potentially we could add time_for_calibration (currently 1 year)
25+
26+
# Using the following priors:
27+
include(joinpath(@__DIR__, "priors.jl"))
28+
# potentially we could add loss-parameters pairing https://clima.github.io/EnsembleKalmanProcesses.jl/dev/update_groups/
29+
30+
# With the forward model on GPUS. Note: the forward model needs to be adjusted to read priors!
31+
ekp_process = EKP.Unscented(prior)
32+
ensemble_size = ekp_process.N_ens
33+
# Config for workers
34+
addprocs(
35+
CAL.PBSManager(ensemble_size), # simulations in parallel. User may change this to use less GPUs.
36+
q = "main",
37+
A = "UCIT0011",
38+
l_select = "1:ngpus=1:ncpus=4",
39+
l_walltime = "11:30:00",
40+
l_job_priority = "premium",
41+
)
42+
# ClimaLand (forward model) - needs to read priors!
43+
@everywhere begin
44+
using Dates # needs to be called again for workers
45+
const nelements = (50, 10) # resolution - (horizontal elements (lon,lat), vertical elements (soil discretization))
46+
const start_date = DateTime(2008, 12, 01) # this is the start of the forward model spinup
47+
const caldir = "calibration_output_utki"
48+
using ClimaLand
49+
dir = pkgdir(ClimaLand)
50+
include(joinpath(dir, "experiments/calibration/forward_model.jl"))
51+
end
52+
@assert month(start_date + spinup_period) == 12 "The start of your calibration period should be December."
53+
54+
# And using those locations (currently all coordinates on land):
55+
include(joinpath(@__DIR__, "make_training_locations.jl"))
56+
training_locations = make_training_locations(nelements)
57+
# potentially we can add regional runs or specific lon lat bands or filter (e.g., regions with snow)
58+
59+
# NOTE1: Don't forget to modify your forward model (land or bucket) to read and use your priors correctly.
60+
# NOTE2: The noise is set in observationseries_era5.jl - adjust if needed.
61+
# ^ current noise options: era5 inter-annual variance, era5 seasonal mean * factor, flat noise, weigh by lats.
62+
# NOTE3: Currently everything is set to use seasonal averages. We could add option to use e.g., monthly or other.
63+
# NOTE4: We could add option to calibrate single sites (change forward model, observationseries, observation_map).
64+
# ^ maybe Julia and Thanhthanh SURF?
65+
66+
67+
68+
##########################
69+
# Most of the time you won't need to change the code below.
70+
# Unless you change EKP configurations, or other specific settings.
71+
##########################
72+
73+
74+
# observationseries - era5 data and noise object to compare to model output in EKP (to minimize the loss)
75+
include(joinpath(@__DIR__, "observationseries_era5.jl"))
76+
77+
# l_obs is the length of Observation objects (observationseries for era5, observationmap for ClimaLand)
78+
n_locations = length(training_locations)
79+
n_variables = length(variable_list)
80+
n_time_points = 4 # 4 seasons (and not, for example, 12 months)
81+
l_obs = n_time_points * n_variables * n_locations
82+
83+
# build observation from ClimaLand outputs - for one member
84+
include(joinpath(@__DIR__, "observation_map.jl"))
85+
86+
# build observation from ClimaLand outputs - for all members
87+
function CAL.observation_map(iteration)
88+
single_member_dims = (l_obs,)
89+
G_ensemble = Array{Float64}(undef, single_member_dims..., ensemble_size)
90+
91+
for m in 1:ensemble_size
92+
member_path = path_to_ensemble_member(caldir, iteration, m)
93+
simdir_path =
94+
joinpath(member_path, "global_diagnostics", "output_active")
95+
simdir = SimDir(simdir_path)
96+
G_ensemble[:, m] .=
97+
process_member_data(simdir, training_locations, variable_list)
98+
end
99+
100+
return G_ensemble
101+
end
102+
103+
# Build the UTKI object - this is where you set EKP configurations
104+
utki = EKP.EnsembleKalmanProcess(
105+
observationseries,
106+
EKP.TransformUnscented(prior, impose_prior = true);
107+
verbose = true,
108+
rng,
109+
scheduler = EKP.DataMisfitController(terminate_at = 100),
110+
)
111+
112+
# Run the calibration via ClimaCalibrate using the arguments built above
113+
CAL.calibrate(CAL.WorkerBackend, utki, n_iterations, prior, caldir)

0 commit comments

Comments
 (0)