-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MESMER-M integration tests #501
Open
veni-vidi-vici-dormivi
wants to merge
78
commits into
MESMER-group:main
Choose a base branch
from
veni-vidi-vici-dormivi:m_integrationtests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
78 commits
Select commit
Hold shift + click to select a range
4edd069
add mesmer_m calibration test
veni-vidi-vici-dormivi 339f07b
add time to calibration results
veni-vidi-vici-dormivi f52cae8
update calibrate test
veni-vidi-vici-dormivi a42f690
add test for drawing
veni-vidi-vici-dormivi 9a2bf0d
Merge branch 'main' into m_integrationtests
veni-vidi-vici-dormivi 7d5de93
update emulations
veni-vidi-vici-dormivi 1bd7c74
linting
veni-vidi-vici-dormivi 3c13240
sneakily add nits to other tests
veni-vidi-vici-dormivi 53688e2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] d1f97a8
switch do allclose for drawing
veni-vidi-vici-dormivi d7bacda
switch to allclose for calibrating
veni-vidi-vici-dormivi 801086c
Merge branch 'main' into m_integrationtests
veni-vidi-vici-dormivi 384624b
nit in example
veni-vidi-vici-dormivi 49caa64
save params as netcdf
veni-vidi-vici-dormivi bdd0dee
fixes
veni-vidi-vici-dormivi 347e7fe
relax atol some more
veni-vidi-vici-dormivi 620c300
test whats wrong
veni-vidi-vici-dormivi 44e4717
nits
veni-vidi-vici-dormivi f97ea6e
tr_solver exact
veni-vidi-vici-dormivi ec37c10
no
veni-vidi-vici-dormivi 5cd11f6
try with more exact jacobian
veni-vidi-vici-dormivi ad029eb
try with even more exact jacobian
veni-vidi-vici-dormivi f0c622b
relax tol again
veni-vidi-vici-dormivi 929c80b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 079de67
tighten convergence criteria
veni-vidi-vici-dormivi e9ca890
check hm coeffs again
veni-vidi-vici-dormivi 74c61ad
tighten some more
veni-vidi-vici-dormivi 1be4c6e
updated data from ch4
d6fcf6e
untighten
a641c0b
ch4 and mindeps
012f4f6
tr_solver = exact
veni-vidi-vici-dormivi 3716db5
tr_solver = lsmr
veni-vidi-vici-dormivi c16182c
switch to curve_fit
veni-vidi-vici-dormivi 5cb8efe
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] ab9f0b1
forgot false
veni-vidi-vici-dormivi 78b6a3b
try trm method and three point jac
veni-vidi-vici-dormivi 1b378bf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 91c2f95
relax tol
veni-vidi-vici-dormivi 5e74e47
numpy close
veni-vidi-vici-dormivi 718ffd4
back to original method
veni-vidi-vici-dormivi 4f9969a
use lm as method
veni-vidi-vici-dormivi 5f6882d
Merge branch 'main' into m_integrationtests
veni-vidi-vici-dormivi 1615d17
leastsq
veni-vidi-vici-dormivi cc9be16
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 6f58608
fix
veni-vidi-vici-dormivi 8275ba7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 30df937
check if the rest is fine
veni-vidi-vici-dormivi 5c9d4ff
linting
veni-vidi-vici-dormivi bc89474
check xi0
veni-vidi-vici-dormivi 388b46e
least_squares and lm
veni-vidi-vici-dormivi 0f0f66c
try different pt method
veni-vidi-vici-dormivi 0df1b71
powell
veni-vidi-vici-dormivi 930dc28
rtol
veni-vidi-vici-dormivi f4e6a75
relax rtol
veni-vidi-vici-dormivi f86055b
relax rtol more
veni-vidi-vici-dormivi 66394ae
nits
veni-vidi-vici-dormivi 3d1a738
Merge branch 'main' into m_integrationtests
veni-vidi-vici-dormivi ac1cb05
remove dev leftovers
veni-vidi-vici-dormivi 81df5a1
try exact again
veni-vidi-vici-dormivi 03558de
assign coords to coeff dims
veni-vidi-vici-dormivi 9036df2
update tests
veni-vidi-vici-dormivi 8d18649
update test
veni-vidi-vici-dormivi 6484592
relax atol
veni-vidi-vici-dormivi 4cdb715
try with rtol
veni-vidi-vici-dormivi 70c2ec0
unique naming
veni-vidi-vici-dormivi bb88e50
naming and rtol
veni-vidi-vici-dormivi ec87532
linting
veni-vidi-vici-dormivi 1f5e816
test all params individually
veni-vidi-vici-dormivi abf634b
fix tests
veni-vidi-vici-dormivi f5d6423
adjust tol hm_coeffs
veni-vidi-vici-dormivi 4a4ea70
adjust tol for lambda_coeffs
veni-vidi-vici-dormivi 99a2519
linitng
veni-vidi-vici-dormivi 79a70c3
adjust tol ar1_slope
veni-vidi-vici-dormivi 8354e9f
adjust tol ar1_intercept
veni-vidi-vici-dormivi 3b862a0
adjust tol localized cov
veni-vidi-vici-dormivi 991f09c
adjusting rtol for lambda coeffs agian windows
veni-vidi-vici-dormivi b813543
comment
veni-vidi-vici-dormivi 57a94be
Changelog
veni-vidi-vici-dormivi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import importlib | ||
|
||
import pytest | ||
import xarray as xr | ||
|
||
import mesmer | ||
|
||
|
||
def test_calibrate_mesmer_m(update_expected_files=False): | ||
# define config values | ||
THRESHOLD_LAND = 1 / 3 | ||
|
||
REFERENCE_PERIOD = slice("1850", "1900") | ||
|
||
LOCALISATION_RADII = list(range(1250, 6251, 250)) + list(range(6500, 8501, 500)) | ||
|
||
esm = "IPSL-CM6A-LR" | ||
|
||
# define paths and load data | ||
TEST_DATA_PATH = importlib.resources.files("mesmer").parent / "tests" / "test-data" | ||
TEST_PATH = TEST_DATA_PATH / "output" / "tas" / "mon" | ||
cmip6_data_path = TEST_DATA_PATH / "calibrate-coarse-grid" / "cmip6-ng" | ||
|
||
path_tas_ann = cmip6_data_path / "tas" / "ann" / "g025" | ||
fN_hist_ann = path_tas_ann / f"tas_ann_{esm}_historical_r1i1p1f1_g025.nc" | ||
fN_proj_ann = path_tas_ann / f"tas_ann_{esm}_ssp585_r1i1p1f1_g025.nc" | ||
|
||
tas_y = xr.open_mfdataset( | ||
[fN_hist_ann, fN_proj_ann], | ||
combine="by_coords", | ||
use_cftime=True, | ||
combine_attrs="override", | ||
data_vars="minimal", | ||
compat="override", | ||
coords="minimal", | ||
drop_variables=["height", "file_qf"], | ||
).load() | ||
|
||
path_tas_mon = cmip6_data_path / "tas" / "mon" / "g025" | ||
fN_hist_mon = path_tas_mon / f"tas_mon_{esm}_historical_r1i1p1f1_g025.nc" | ||
fN_proj_mon = path_tas_mon / f"tas_mon_{esm}_ssp585_r1i1p1f1_g025.nc" | ||
tas_m = xr.open_mfdataset( | ||
[fN_hist_mon, fN_proj_mon], | ||
combine="by_coords", | ||
use_cftime=True, | ||
combine_attrs="override", | ||
data_vars="minimal", | ||
compat="override", | ||
coords="minimal", | ||
drop_variables=["height", "file_qf"], | ||
).load() | ||
|
||
# data preprocessing | ||
ref_y = tas_y.sel(time=REFERENCE_PERIOD).mean("time", keep_attrs=True) | ||
ref_m = tas_m.sel(time=REFERENCE_PERIOD).mean("time", keep_attrs=True) | ||
|
||
tas_y = tas_y - ref_y | ||
tas_m = tas_m - ref_m | ||
|
||
# create local gridded tas data | ||
def mask_and_stack(ds, threshold_land): | ||
ds = mesmer.mask.mask_ocean_fraction(ds, threshold_land) | ||
ds = mesmer.mask.mask_antarctica(ds) | ||
ds = mesmer.grid.stack_lat_lon(ds) | ||
return ds | ||
|
||
tas_stacked_y = mask_and_stack(tas_y, threshold_land=THRESHOLD_LAND) | ||
tas_stacked_m = mask_and_stack(tas_m, threshold_land=THRESHOLD_LAND) | ||
|
||
# we need to get the original time coordinate to be able to validate our results | ||
m_time = tas_stacked_m.time | ||
|
||
# fit harmonic model | ||
harmonic_model_fit = mesmer.stats.fit_harmonic_model( | ||
tas_stacked_y.tas, tas_stacked_m.tas | ||
) | ||
|
||
# train power transformer | ||
resids_after_hm = tas_stacked_m - harmonic_model_fit.hm_predictions | ||
pt_coefficients = mesmer.stats.fit_yeo_johnson_transform( | ||
resids_after_hm.tas, tas_stacked_y.tas | ||
) | ||
transformed_hm_resids = mesmer.stats.yeo_johnson_transform( | ||
resids_after_hm.tas, pt_coefficients.lambda_coeffs, tas_stacked_y.tas | ||
) | ||
|
||
# fit cyclo-stationary AR(1) process | ||
AR1_fit = mesmer.stats.fit_auto_regression_monthly( | ||
transformed_hm_resids.transformed, time_dim="time" | ||
) | ||
|
||
# work out covariance matrix | ||
geodist = mesmer.geospatial.geodist_exact(tas_stacked_y.lon, tas_stacked_y.lat) | ||
|
||
phi_gc_localizer = mesmer.stats.gaspari_cohn_correlation_matrices( | ||
geodist, localisation_radii=LOCALISATION_RADII | ||
) | ||
|
||
weights = xr.ones_like(AR1_fit.residuals.isel(gridcell=0)) | ||
weights.name = "weights" | ||
|
||
localized_ecov = mesmer.stats.find_localized_empirical_covariance_monthly( | ||
AR1_fit.residuals, weights, phi_gc_localizer, "time", 30 | ||
) | ||
|
||
# merge into one dataset | ||
harmonic_model_fit = harmonic_model_fit.drop_vars("hm_predictions") | ||
AR1_fit = AR1_fit.drop_vars("residuals") | ||
localized_ecov = localized_ecov.drop_vars("covariance") | ||
m_time = m_time.rename("monthly_time") | ||
calibrated_params = xr.merge( | ||
[harmonic_model_fit, pt_coefficients, AR1_fit, localized_ecov, m_time] | ||
) | ||
|
||
# save params | ||
if update_expected_files: | ||
calibrated_params.to_netcdf(TEST_PATH / "test-mesmer_m-params.nc") | ||
pytest.skip("Updated test-mesmer_m-params.nc") | ||
|
||
# testing | ||
else: | ||
# load expected values | ||
expected_params = xr.open_dataset( | ||
TEST_PATH / "test-mesmer_m-params.nc", use_cftime=True | ||
) | ||
|
||
# the following parameters should be exactly the same | ||
exact_exp_params = xr.merge( | ||
[ | ||
expected_params.hm_selected_order, | ||
expected_params.localization_radius, | ||
expected_params.monthly_time, | ||
] | ||
) | ||
exact_cal_params = xr.merge( | ||
[ | ||
calibrated_params.hm_selected_order, | ||
calibrated_params.localization_radius, | ||
calibrated_params.monthly_time, | ||
] | ||
) | ||
|
||
xr.testing.assert_equal(exact_exp_params, exact_cal_params) | ||
|
||
# compare the rest | ||
# using numpy because it outputs the differences and how many values are off | ||
import numpy as np | ||
|
||
# the tols are set to the best we can do | ||
# NOTE: it is always rather few values that are off | ||
np.testing.assert_allclose( | ||
expected_params.hm_coeffs, | ||
calibrated_params.hm_coeffs, | ||
atol=1e-5, | ||
rtol=1 / 3, | ||
) | ||
# NOTE: would have to be atol is 1e12 here - not doing that | ||
np.testing.assert_allclose( | ||
expected_params.lambda_coeffs, calibrated_params.lambda_coeffs, rtol=0.6 | ||
) | ||
np.testing.assert_allclose( | ||
expected_params.ar1_slope, calibrated_params.ar1_slope, atol=1e-5, rtol=1e-4 | ||
) | ||
np.testing.assert_allclose( | ||
expected_params.ar1_intercept, | ||
calibrated_params.ar1_intercept, | ||
atol=1e-4, | ||
rtol=1e-2 / 3, | ||
) | ||
np.testing.assert_allclose( | ||
expected_params.localized_covariance, | ||
calibrated_params.localized_covariance, | ||
atol=1e-4, | ||
rtol=1e-2, | ||
) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a huge fan of this there and back renaming and merging. Would it be bad to save each Dataset as individual netCDF? Or merge into a datatree? (But maybe later).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of having one dataset with all the calibrated parameters per ESM. I did some renaming in the modules such that everything can be merged without problems. What do you think about that?