Skip to content

Commit

Permalink
Merge pull request #851 from PCMDI/741_ljw_cloud
Browse files Browse the repository at this point in the history
Cloud feedback metric
  • Loading branch information
lee1043 committed Feb 17, 2023
2 parents 2461053 + 3be658e commit 80c092e
Show file tree
Hide file tree
Showing 28 changed files with 108,775 additions and 6 deletions.
71 changes: 71 additions & 0 deletions pcmdi_metrics/cloud_feedback/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Cloud Feedback Metrics

The clould feedback metrics implemented to PMP is originally from [`assessed-cloud-fbks`](https://github.com/mzelinka/assessed-cloud-fbks)[![DOI](https://zenodo.org/badge/353136800.svg)](https://zenodo.org/badge/latestdoi/353136800), developed by [@mzelinka](https://mzelinka.github.io/) at LLNL. This code performs the analysis of [Zelinka et al. (2022)](https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2021JD035198). It computes GCM cloud feedback components and compares them to the expert-assessed values from [Sherwood et al. (2020)](https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2019RG000678).

## Instructions
To use, please consider follow these steps:

#### 1. Install PMP
Install PMP via conda following [these instructions](http://pcmdi.github.io/pcmdi_metrics/install.html). For example:
```
conda create -n [YOUR_CONDA_ENVIRONMENT] -c conda-forge pcmdi_metrics
```

#### 2. Activate PMP installed conda environment
Activate your environment (if PMP installed env is differnt than your current one):
```
conda activate [YOUR_CONDA_ENVIRONMENT]
```

#### 3. Clone PMP repo to your local
Clone PMP repo to your local for pre-calculated data:
```
git clone https://github.com/PCMDI/pcmdi_metrics
```

Once completed, go to `pcmdi_metrics/pcmdi_metrics/cloud_feedback` directory
```
cd [YOUR LOCAL CLONED PMP REPOSITORY]
cd pcmdi_metrics/pcmdi_metrics/cloud_feedback
```

#### 4. Edit parameter files
In [`param/my_param.py`](param/my_param.py), update the "User Input" section so it points to your model's amip and amip-p4K files.
```python

# User Input:
# ================================================================================================
model = "GFDL-CM4"
variant = "r1i1p1f1"

input_files_json = "./param/input_files.json"

# Flag to compute ECS
# True: compute ECS using abrupt-4xCO2 run
# False: do not compute, instead rely on ECS value present in the json file (if it exists)
# get_ecs = True
get_ecs = False

# Output directory path (directory will be generated if it does not exist yet.)
xml_path = "./xmls/"
figure_path = "./figures/"
output_path = "./output"
output_json_filename = "_".join(["cloud_feedback", model, variant]) + ".json"
# ================================================================================================
```

You will need to update [`param/input_files.json`](param/input_files.json) file as well to provide data path for your input files.

#### 5. Run the code and inspect the generated output files
Run calculation:
```
python cloud_feedback_driver.py -p param/my_param.py
```

Once code is completed, check `output` directory (`output_path` from the above parameter file) for JSON and `figures` directory (`figure_path` from the above parameter file) for figures and text tables.


## References
- Zelinka et al. (2022): [Evaluating climate models’ cloud feedbacks against expert judgement](https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2021JD035198), <em>J. Geophys. Res.</em>, 127, e2021JD035198, doi:10.1029/2021JD035198.

- Sherwood et al. (2020): [A combined assessment of Earth’s climate sensitivity](https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1029/2019RG000678), <em>Rev. Geophys.</em>, 58, e2019RG000678, doi:10.1029/2019RG000678.
Empty file.
197 changes: 197 additions & 0 deletions pcmdi_metrics/cloud_feedback/cloud_feedback_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/usr/bin/env python

import json
import os
from collections import OrderedDict

import pcmdi_metrics.cloud_feedback.lib.cld_fbks_ecs_assessment_v3 as dataviz
from pcmdi_metrics.cloud_feedback.lib import (
AddParserArgument,
CloudRadKernel,
cloud_feedback_metrics_to_json,
compute_ECS,
organize_ecs_jsons,
organize_err_jsons,
organize_fbk_jsons,
)

# collect user input
param = AddParserArgument()

model = param.model
institution = param.institution
variant = param.variant
grid_label = param.grid_label
version = param.version
input_files_json = param.input_files_json
path = param.path
xml_path = param.xml_path
data_path = param.data_path
figure_path = param.figure_path
output_path = param.output_path
output_json_filename = param.output_json_filename
get_ecs = param.get_ecs
debug = param.debug

cmec = False
if hasattr(param, "cmec"):
cmec = param.cmec # Generate CMEC compliant json
print("CMEC:" + str(cmec))

print("model:", model)
print("institution:", institution)
print("variant:", variant)
print("grid_label:", grid_label)
print("version:", version)
print("path:", path)
print("input_files_json:", input_files_json)
print("xml_path:", xml_path)
print("figure_path:", figure_path)
print("output_path:", output_path)
print("output_json_filename:", output_json_filename)
print("get_ecs:", get_ecs)
print("debug:", debug)

if get_ecs:
exps = ["amip", "amip-p4K", "piControl", "abrupt-4xCO2"]
else:
exps = ["amip", "amip-p4K"]

# generate xmls pointing to the cmorized netcdf files
os.makedirs(xml_path, exist_ok=True)

filenames = dict()

if input_files_json is not None:
with open(input_files_json) as f:
ncfiles = json.load(f)
else:
print('Warning: input files were not explicitly given. They will be searched from ', path)

for exp in exps:
filenames[exp] = dict()
if exp == "amip-p4K":
activity = "CFMIP"
else:
activity = "CMIP"
if "amip" in exp:
fields = [
"tas",
"rsdscs",
"rsuscs",
"wap",
"clisccp",
] # necessary for cloud feedback calcs
else:
fields = ["tas", "rlut", "rsut", "rsdt"] # needed for ECS calc
for field in fields:
if field == "clisccp":
table = "CFmon"
else:
table = "Amon"

if input_files_json is None: # PCMDI internal setup
searchstring = os.path.join(
path,
activity,
institution,
model,
exp,
variant,
table,
field,
grid_label,
version,
"*.nc",
)
else:
searchstring = os.path.join(
ncfiles[exp][field]["path"], ncfiles[exp][field]["file"]
)
xmlname = os.path.join(xml_path, ".".join([exp, model, variant, field, "xml"]))
os.system("cdscan -x " + xmlname + " " + searchstring)
filenames[exp][field] = xmlname

if debug:
with open(os.path.join(output_path, "filenames.json"), "w") as f:
json.dump(filenames, f, sort_keys=True, indent=4)

# calculate all feedback components and Klein et al (2013) error metrics:
fbk_dict, obsc_fbk_dict, err_dict = CloudRadKernel(filenames)

print("calc done")

# add this model's results to the pre-existing json file containing other models' results:
updated_fbk_dict, updated_obsc_fbk_dict = organize_fbk_jsons(
fbk_dict, obsc_fbk_dict, model, variant, datadir=data_path
)
updated_err_dict = organize_err_jsons(err_dict, model, variant, datadir=data_path)

ecs = None
if get_ecs:
# calculate ECS and add it to the pre-existing json file containing other models' results:
ecs = compute_ECS(filenames)
print("calc ECS done")
print("ecs: ", ecs)
updated_ecs_dict = organize_ecs_jsons(ecs, model, variant, datadir=data_path)

os.makedirs(output_path, exist_ok=True)
if debug:
with open(os.path.join(output_path, "fbk_dict.json"), "w") as f:
json.dump(fbk_dict, f, sort_keys=True, indent=4)
with open(os.path.join(output_path, "err_dict.json"), "w") as f:
json.dump(err_dict, f, sort_keys=True, indent=4)
with open(os.path.join(output_path, "updated_err_dict.json"), "w") as f:
json.dump(updated_err_dict, f, sort_keys=True, indent=4)
with open(os.path.join(output_path, "updated_fbk_dict.json"), "w") as f:
json.dump(updated_fbk_dict, f, sort_keys=True, indent=4)
with open(os.path.join(output_path, "updated_ecs_dict.json"), "w") as f:
json.dump(updated_ecs_dict, f, sort_keys=True, indent=4)

# generate plots and extract metrics from the plotting routines
os.makedirs(figure_path, exist_ok=True)
climo_cld_rmse, cld_fbk_rmse, assessed_cld_fbk, ecs = dataviz.make_all_figs(
updated_fbk_dict,
updated_obsc_fbk_dict,
updated_err_dict,
updated_ecs_dict,
model,
figdir=figure_path,
datadir=data_path,
debug=debug,
)
print("get metrics done")

# save final metrics and accompanying important statistics in JSON format
print("-- Metric result --")
print("model:", model)
print("variant:", variant)
print("clim_cloud_rmse:", climo_cld_rmse)
print("cloud_feedback_rmse:", cld_fbk_rmse)
print("assessed_cloud_feedback:", assessed_cld_fbk)
print("ecs:", ecs)

output_dict = OrderedDict()
output_dict["RESULTS"] = OrderedDict()
output_dict["RESULTS"][model] = OrderedDict()
output_dict["RESULTS"][model][variant] = OrderedDict()
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"] = OrderedDict()
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["high_cloud_altitude"] = assessed_cld_fbk[0]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["tropical_marine_low_cloud"] = assessed_cld_fbk[1]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["tropical_anvil_cloud_area"] = assessed_cld_fbk[2]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["land_cloud_amount"] = assessed_cld_fbk[3]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["middle_latitude_marine_low_cloud_amount"] = assessed_cld_fbk[4]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["high_latitude_low_cloud_optical_depth"] = assessed_cld_fbk[5]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["implied_unassessed"] = assessed_cld_fbk[6]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["sum_of_assessed"] = assessed_cld_fbk[7]
output_dict["RESULTS"][model][variant]["assessed_cloud_feedback"]["total_cloud_feedback"] = assessed_cld_fbk[8]
output_dict["RESULTS"][model][variant]["clim_cloud_rmse"] = climo_cld_rmse
output_dict["RESULTS"][model][variant]["cloud_feedback_rmse"] = cld_fbk_rmse
output_dict["RESULTS"][model][variant]["equilibrium_climate_sensitivity"] = ecs


cloud_feedback_metrics_to_json(
output_path, output_json_filename, output_dict, cmec_flag=cmec
)

print("Done!")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 80c092e

Please sign in to comment.