Skip to content

Sea ice sensitivity to GMST - Calculate from observational datasets#4220

Open
NParsonsMO wants to merge 75 commits into
mainfrom
4095-add-observations-to-sea-ice-sensitivity-recipe-retry
Open

Sea ice sensitivity to GMST - Calculate from observational datasets#4220
NParsonsMO wants to merge 75 commits into
mainfrom
4095-add-observations-to-sea-ice-sensitivity-recipe-retry

Conversation

@NParsonsMO
Copy link
Copy Markdown
Contributor

@NParsonsMO NParsonsMO commented Oct 13, 2025

Added capacity to calculate trends from observational datasets (though also keeping currently provided values, as Notz-style includes a "plausible" range that is beyond scope).

Observations are shown as squares.

Annual trend in siconc for NOAA dataset is slightly different between this and Roach's figures, 0.01677... vs 0.01692....

Annual trends for GMST were taken as one average in Roach's plots, so will not be identical.

The published plot used data up to 2018, but we only use up to 2014.

The hatching criterion for the Roach-style plot is now consistent with the published plot.

Added ability to plot two different time periods in Notz-style plot, ready for use with CMIP7 datasets.


Before you get started

Checklist

It is the responsibility of the author to make sure the pull request is ready to review. The icons indicate whether the item will be subject to the 🛠 Technical or 🧪 Scientific review.

New or updated recipe/diagnostic

New or updated data reformatting script


To help with the number of pull requests:

 - Removed obs from Roach-style graphic (left description for reference).
 - Fed in model datasets from recipe differently to allow for obs having a different variable.

 To do:
  - Calculate and plot the obs.
  - Print the obs trend values.
@NParsonsMO NParsonsMO self-assigned this Oct 13, 2025
@NParsonsMO NParsonsMO linked an issue Oct 13, 2025 that may be closed by this pull request
@valeriupredoi
Copy link
Copy Markdown
Contributor

please give the PR a suitable title, when you have a minute (even if it's a draft) 😁

@NParsonsMO NParsonsMO changed the title In progress: Sea ice sensitivity to GMST - Calculate from observational datasets Nov 24, 2025
@NParsonsMO NParsonsMO requested a review from alistairsellar May 19, 2026 14:02

# Read the time constraint from the period
start_year, end_year = time_range.split("-")
time_constraint = iris.Constraint(
Copy link
Copy Markdown
Contributor Author

@NParsonsMO NParsonsMO May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearly this can't add data, so if the observational dates fall outside the main dates specified in the pre-processor the date ranges won't be right (so when I accidentally only extracted data until 2007, it looked like the data for 1979-2007 was identical to the data for 1979-2014).

It's here because the obs values we have are valid until 2014 but data will be available until 2021, and that way round is fine, but it bothers me that if people change the dates in other ways it might be misleading...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend adding a comment to the code to explain this, to help future readers of the code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e8e399a

Comment thread esmvaltool/diag_scripts/seaice/seaice_sensitivity.py
start_day: 1
start_month: 1
start_year: 1979
start_year: &data_start 1979
Copy link
Copy Markdown
Contributor Author

@NParsonsMO NParsonsMO May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I should query the data period from the pre-processed netcdfs instead, then the titles wouldn't be at so much risk of being wrong if the cube didn't span the whole period (such as those passed through from CMEW). But I'd either have to assume all cubes were the same, or read them all...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to hard-wire here, but have a warning/error in the diagnostic for cases where the data doesn't span the period. (Or perhaps the preprocessor throws a warning/error in those cases?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did not that I could see.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did not that I could see.

OK. In which case (and reflecting an offline comment from Ed) it could be more obvious to specify the dates in the dataset specifications. (Still sticking with the hard-wiring approach.

Comment thread esmvaltool/recipes/recipe_seaice_sensitivity.yml
@alistairsellar alistairsellar marked this pull request as ready for review May 27, 2026 11:06
Copy link
Copy Markdown
Contributor

@alistairsellar alistairsellar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates @NParsonsMO - these will make it easier to adapt for CMIP7 for the REF. Approving the science review.

@schlunma since you reviewed the first implementation of this, would you be OK to / have time to review this PR?

Comment thread doc/sphinx/source/recipes/recipe_seaice_sensitivity.rst Outdated

# Read the time constraint from the period
start_year, end_year = time_range.split("-")
time_constraint = iris.Constraint(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend adding a comment to the code to explain this, to help future readers of the code.

Comment thread esmvaltool/diag_scripts/seaice/seaice_sensitivity.py
start_day: 1
start_month: 1
start_year: 1979
start_year: &data_start 1979
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to hard-wire here, but have a warning/error in the diagnostic for cases where the data doesn't span the period. (Or perhaps the preprocessor throws a warning/error in those cases?)

Co-authored-by: Alistair Sellar <16133375+alistairsellar@users.noreply.github.com>
start_day: 1
start_month: 1
start_year: 1979
start_year: &data_start 1979
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did not that I could see.

OK. In which case (and reflecting an offline comment from Ed) it could be more obvious to specify the dates in the dataset specifications. (Still sticking with the hard-wiring approach.

@NParsonsMO
Copy link
Copy Markdown
Contributor Author

NParsonsMO commented May 30, 2026

OK. In which case (and reflecting an offline comment from Ed) it could be more obvious to specify the dates in the dataset specifications. (Still sticking with the hard-wiring approach.

I've changed where the years are set in 2bff0ef.

I've added a warning if not all periods are the same in 2971258.

If nobody specifies any years anywhere then the script will fail because ESMValTool will just pre-process all available data, and then when it tries to calculate the regression of OSI-SAF data over, say, HadCRUT5 data the dimensions don't match, so I've added a note to the documentation in 26b7d2f.

@NParsonsMO NParsonsMO requested a review from alistairsellar May 30, 2026 21:27
@schlunma schlunma added this to the v2.15.0 milestone Jun 1, 2026
@schlunma schlunma added the REF Important for the CMIP Rapid Evaluation Framework (REF) label Jun 1, 2026
Copy link
Copy Markdown
Contributor

@schlunma schlunma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @NParsonsMO, code looks good to me! I only have a couple of minor comments.

Please run pre-commit run --all on your code, this should also fix most of the Codacy issues.

Thanks!


# List the SIA obs
elif group == "siconc_obs":
siconc_obs.append(dataset)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no else here, so this will silently ignore all other variables apart from the ones you specified. Is this on purpose?

Comment on lines +210 to +232
regression = [
r
for _ in range(num_periods)
for r in regressions
for _ in range(num_stats)
]

# Produces slope then r_value then p_value then std_err_slope, six times
statistic = [
s for _ in range(num_periods * num_regressions) for s in statistics
]

# Create main column headers
data_columns = pd.MultiIndex.from_arrays(
[period, regression, statistic],
names=["period", "regression", "statistic"],
)

return dictionary
# Add equivalent of single-level columns for dataset info
first_columns = pd.MultiIndex.from_arrays(
[["", ""], ["", ""], ["label", "type"]],
names=["period", "regression", "statistic"],
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be easier to set this up via pandas.MultiIndex.from_product.

Comment on lines +301 to +304
if "year" in cube.coords():
no_years = list(range(len(cube.coord("years").points)))
else:
no_years = list(range(len(cube.coord("time").points)))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the time coordinate will always be available, so this could potentially be simplified to

Suggested change
if "year" in cube.coords():
no_years = list(range(len(cube.coord("years").points)))
else:
no_years = list(range(len(cube.coord("time").points)))
no_years = np.arange(cube.coord("time").shape[0])

Comment on lines +494 to +503
if first_variable["diagnostic"] == "arctic":
title = (
"Trends in Annual Mean Temperature And September Arctic Sea Ice"
)
save_as = "September Arctic sea ice trends"
elif first_variable["diagnostic"] == "antarctic":
title = (
"Trends in Annual Mean Temperature And Annual Antarctic Sea Ice"
)
save_as = "Annual Antarctic sea ice trends"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be an else case with a proper error; otherwise your follow-up code will run into undefined variables errors for title and save_as.

save_as = "September Arctic sea ice sensitivity"
elif first_variable["diagnostic"] == "antarctic":
title = "Annual Antarctic sea-ice area sensitivity\ndSIA/dGMST"
save_as = "Annual Antarctic sea ice sensitivity"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: add else case to make sure that all variables are defined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

REF Important for the CMIP Rapid Evaluation Framework (REF)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add observations to sea ice sensitivity recipe

4 participants