-
Notifications
You must be signed in to change notification settings - Fork 2
Enhance robust pcmdi #49
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
base: main
Are you sure you want to change the base?
Changes from all commits
b9124fe
fa84fac
27c1487
da6c90b
a135d3b
c5db2e7
910fcf0
1012e37
201bcd2
49e1452
6e20772
d6a7f04
40c535f
4ef16c2
6fe4dba
ec525a1
e408782
20df79f
dfc7fef
aba7f34
ac6aa3d
2799173
97b6ce8
dfefdf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import pandas as pd | ||
|
|
||
| from zppy_interfaces.pcmdi_diags.synthetic_plots import synthetic_metrics_plotter | ||
| from zppy_interfaces.pcmdi_diags.synthetic_plots.synthetic_metrics_plotter import ( | ||
| drop_vars, | ||
| mean_climate_plot_driver, | ||
| ) | ||
|
|
||
|
|
||
| def _mean_climate_frame(**values): | ||
| data = { | ||
| "model": ["CMIP", "E3SM"], | ||
| "run": ["r1", "r1"], | ||
| "model_run": ["CMIP_r1", "E3SM_r1"], | ||
| } | ||
| data.update(values) | ||
| return pd.DataFrame(data) | ||
|
|
||
|
|
||
| def test_drop_vars_removes_requested_variables_missing_from_dataframe(): | ||
| data_dict, var_names, var_units = drop_vars( | ||
| _mean_climate_frame(pr=[1.0, 2.0]), | ||
| ["pr", "prw"], | ||
| ["mm/day", "kg/m2"], | ||
| ) | ||
|
zhangshixuan1987 marked this conversation as resolved.
|
||
|
|
||
| assert "prw" not in data_dict.columns | ||
| assert var_names == ["pr"] | ||
| assert var_units == ["mm/day"] | ||
|
|
||
|
|
||
| def test_mean_climate_portrait_skips_region_missing_variables(monkeypatch, tmp_path): | ||
| captured = {} | ||
|
|
||
| def fake_portrait_metric_plot( | ||
| region, | ||
| stat, | ||
| group, | ||
| data_dict, | ||
| stat_name, | ||
| model_name, | ||
| var_list, | ||
| model_list, | ||
| out_path, | ||
| fig_format, | ||
| ): | ||
| captured["region"] = region | ||
| captured["var_list"] = var_list | ||
| captured["data_dict"] = data_dict | ||
|
|
||
| monkeypatch.setattr( | ||
| synthetic_metrics_plotter, | ||
| "portrait_metric_plot", | ||
| fake_portrait_metric_plot, | ||
| ) | ||
|
|
||
| metric_dict = { | ||
| "type": ["portrait"], | ||
| "region": ["ocean"], | ||
| "season": ["djf", "mam", "jja", "son"], | ||
| "name": "Mean Bias", | ||
| } | ||
| df_dict = { | ||
| "djf": {"ocean": _mean_climate_frame(pr=[1.0, 2.0], prw=[3.0, 4.0])}, | ||
| "mam": {"ocean": _mean_climate_frame(pr=[1.0, 2.0])}, | ||
| "jja": {"ocean": _mean_climate_frame(pr=[1.0, 2.0], prw=[3.0, 4.0])}, | ||
| "son": {"ocean": _mean_climate_frame(pr=[1.0, 2.0], prw=[3.0, 4.0])}, | ||
| } | ||
|
|
||
| mean_climate_plot_driver( | ||
| metric="mean_climate", | ||
| stat="mae_xy", | ||
| regions=["ocean"], | ||
| model_name=["E3SM"], | ||
| metric_dict=metric_dict, | ||
| df_dict=df_dict, | ||
| var_list=["pr", "prw"], | ||
| var_unit_list=["mm/day", "kg/m2"], | ||
| save_data=False, | ||
| out_path=str(tmp_path), | ||
| fig_format="png", | ||
| ) | ||
|
|
||
| assert captured["region"] == "ocean" | ||
| assert captured["var_list"] == ["pr"] | ||
| assert all(values.shape == (1, 2) for values in captured["data_dict"].values()) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From Claude: The shape
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is also a smoke test, so maybe it is not a problem? |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,126 @@ | ||
| from zppy_interfaces.pcmdi_diags.viewer import safe_join | ||
| from zppy_interfaces.pcmdi_diags.viewer import ( | ||
| CMVARGroupBuilder, | ||
| generate_cmvar_table, | ||
| generate_data_html, | ||
| generate_emovs_table, | ||
| safe_join, | ||
| ) | ||
|
|
||
|
|
||
| def test_safe_join(): | ||
| assert safe_join("a", "b") == "a/b" | ||
| assert safe_join("a/", "b") == "a/b" | ||
|
|
||
|
|
||
| def test_coupled_mov_compose_eof_uses_mode_eof(tmp_path): | ||
| fig_dir = tmp_path / "figures" | ||
| diag_dir = tmp_path / "viewer" | ||
| yearly_dir = fig_dir / "MOV_compose" / "yearly" | ||
| yearly_dir.mkdir(parents=True) | ||
| diag_dir.mkdir() | ||
|
|
||
| (yearly_dir / "MOV_compose_AMO_yearly_eof1.png").touch() | ||
| (yearly_dir / "MOV_compose_PDO_yearly_eof1.png").touch() | ||
| (yearly_dir / "MOV_compose_NPGO_yearly_eof2.png").touch() | ||
|
|
||
| builder = CMVARGroupBuilder() | ||
|
|
||
| amo_row = builder.generate_mcpl_row("AMO", str(diag_dir), str(fig_dir)) | ||
| amo_compose_cell = amo_row[2]["content"] | ||
| assert "AMO (SST)" in amo_row[0]["content"] | ||
| assert "MOV_compose_AMO_yearly_eof1.png" in amo_compose_cell | ||
|
|
||
| pdo_row = builder.generate_mcpl_row("PDO", str(diag_dir), str(fig_dir)) | ||
| pdo_compose_cell = pdo_row[2]["content"] | ||
| assert "PDO (SST)" in pdo_row[0]["content"] | ||
| assert "MOV_compose_PDO_yearly_eof1.png" in pdo_compose_cell | ||
| assert "MOV_compose_PDO_yearly_cbf.png" not in pdo_compose_cell | ||
|
|
||
| npgo_row = builder.generate_mcpl_row("NPGO", str(diag_dir), str(fig_dir)) | ||
| npgo_compose_cell = npgo_row[2]["content"] | ||
| assert "NPGO (SST)" in npgo_row[0]["content"] | ||
| assert "MOV_compose_NPGO_yearly_eof2.png" in npgo_compose_cell | ||
| assert "MOV_compose_NPGO_yearly_eof1.png" not in npgo_compose_cell | ||
|
|
||
|
|
||
| def test_coupled_modes_are_normalized_from_config(tmp_path): | ||
| fig_dir = tmp_path / "figures" | ||
| diag_dir = tmp_path / "viewer" | ||
| yearly_dir = fig_dir / "MOV_metric" / "MOV_compose" / "yearly" | ||
| yearly_dir.mkdir(parents=True) | ||
| diag_dir.mkdir() | ||
|
|
||
| (yearly_dir / "MOV_compose_NPGO_yearly_eof2.png").touch() | ||
|
|
||
| table = generate_cmvar_table( | ||
| str(diag_dir), | ||
| str(fig_dir), | ||
| enso_show=False, | ||
| movc_show=True, | ||
| movc_modes=" npgo ", | ||
| ) | ||
|
|
||
| assert len(table) == 1 | ||
| assert "NPGO (SST)" in table[0][0]["content"] | ||
| assert "MOV_compose_NPGO_yearly_eof2.png" in table[0][2]["content"] | ||
| assert "MOV_compose_NPGO_yearly_eof1.png" not in table[0][2]["content"] | ||
|
|
||
|
|
||
| def test_emovs_table_defaults_to_atmospheric_modes(tmp_path): | ||
| table = generate_emovs_table(str(tmp_path / "viewer"), str(tmp_path / "figures")) | ||
|
|
||
| first_cells = [row[0]["content"] for row in table if row and "rowspan" in row[0]] | ||
| assert first_cells == [ | ||
| "<b>NAM (PSL)</b>", | ||
| "<b>PNA (PSL)</b>", | ||
| "<b>NPO (PSL)</b>", | ||
| "<b>NAO (PSL)</b>", | ||
| "<b>SAM (PSL)</b>", | ||
| "<b>PSA1 (PSL)</b>", | ||
| "<b>PSA2 (PSL)</b>", | ||
| ] | ||
|
|
||
|
|
||
| def test_emovs_modes_are_normalized_from_config(tmp_path): | ||
| fig_dir = tmp_path / "figures" | ||
| compose_dir = fig_dir / "MOV_metric" / "MOV_compose" / "DJF" | ||
| compose_dir.mkdir(parents=True) | ||
| (compose_dir / "MOV_compose_NPO_DJF_eof2.png").touch() | ||
| (compose_dir / "MOV_compose_PSA2_DJF_eof3.png").touch() | ||
|
|
||
| table = generate_emovs_table( | ||
| str(tmp_path / "viewer"), | ||
| str(fig_dir), | ||
| modes=" npo, psa2 ", | ||
| ) | ||
|
|
||
| first_cells = [row[0]["content"] for row in table if row and "rowspan" in row[0]] | ||
| assert first_cells == ["<b>NPO (PSL)</b>", "<b>PSA2 (PSL)</b>"] | ||
| assert "MOV_compose_NPO_DJF_eof2.png" in table[1][1]["content"] | ||
| assert "MOV_compose_PSA2_DJF_eof3.png" in table[10][1]["content"] | ||
|
|
||
|
|
||
| def test_generate_data_html_creates_out_dir_and_keeps_string_lists(tmp_path): | ||
| template_dir = tmp_path / "templates" | ||
| out_dir = tmp_path / "viewer" | ||
| template_dir.mkdir(parents=True) | ||
| (template_dir / "data_template.html").write_text( | ||
| "{% for section in sections %}" | ||
| "{% for row in section.rows %}{{ row.description }}\n{% endfor %}" | ||
| "{% endfor %}" | ||
| ) | ||
|
|
||
| out_path = generate_data_html( | ||
| { | ||
| "template_dir": str(template_dir), | ||
| "out_dir": str(out_dir), | ||
| "clim_viewer": True, | ||
| "clim_vars": "pr,tas", | ||
| } | ||
| ) | ||
|
|
||
| html = (out_dir / "diag_data.html").read_text() | ||
| assert out_path == str(out_dir / "diag_data.html") | ||
| assert out_dir.is_dir() | ||
| assert "pr,tas" in html | ||
| assert "p, r" not in html |
Uh oh!
There was an error while loading. Please reload this page.