clm5/tools/contrib/ssp_anomaly_forcing_smooth
2024-05-09 15:14:01 +08:00

708 lines
25 KiB
Python

#! /usr/bin/env python3
"""
ssp_anomaly_forcing_smooth
Create anomaly forcing datasets for SSP scenarios that can be used by CESM datm model
load proper modules first, i.e.
../../py_env_create
conda activate ctsm_pylib
"""
import sys
import os
import subprocess
import datetime
import argparse
from getpass import getuser
import numpy as np
import netCDF4 as netcdf4
# Adds global attributes, returning hdir and fdir
def add_global_attributes(ds, historydate, histdir, sspdir, num_ens, climo_year, climo_base_nyrs, dpath, dfile, hist_yrstart, hist_yrend, ssp_yrstart, ssp_yrend, timetag):
ds.Created_on = timetag
ds.title = "anomaly forcing data"
ds.note1 = (
"Anomaly/scale factors calculated relative to "
+ str(climo_year - (climo_base_nyrs - 1) / 2)
+ "-"
+ str(climo_year + (climo_base_nyrs - 1) / 2)
)
ds.history = historydate + ": created by " + sys.argv[0]
stdout = os.popen("git describe")
ds.gitdescribe = stdout.read().rstrip()
ds.Source = "CMIP6 CESM simulations"
ds.Conventions = "CF-1.0"
ds.comment = (
"Monthly scale factors for given SSP scenario compared to a climatology based on"
+ " data centered on "
+ str(climo_year)
+ " over the range given in note1"
)
ds.number_of_ensemble_members = str(num_ens)
ds.Created_by = getuser()
for nens in range(num_ens):
hdir = dpath + histdir[nens] + dfile
fdir = dpath + sspdir[nens] + dfile
if nens == 0:
ds.Created_from_historical_dirs = hdir
ds.Created_from_scenario_dirs = fdir
else:
ds.Created_from_historical_dirs += ", " + hdir
ds.Created_from_scenario_dirs += ", " + fdir
ds.History_years = str(hist_yrstart) + "," + str(hist_yrend)
ds.Scenario_years = str(ssp_yrstart) + "," + str(ssp_yrend)
ds.institution = "National Center for Atmospheric Research"
return hdir,fdir
def create_fill_latlon(ds, data, var_name):
ds.createDimension(var_name, int(data.size))
wl = ds.createVariable(var_name, np.float64, (var_name,))
if var_name == "lat":
wl.units = "degrees_north"
wl.long_name = "Latitude"
elif var_name == "lon":
wl.units = "degrees_east"
wl.long_name = "Longitude"
wl.mode = "time-invariant"
wl[:] = data
return ds
def create_fill_time(ds, time, ntime, ssp_time_units=None, ssp_time_longname=None, adj_time=False):
if ntime is not None:
ntime = int(ntime)
ds.createDimension("time", ntime)
wtime = ds.createVariable("time", np.float64, ("time",))
if ssp_time_units is not None:
wtime.units = ssp_time_units
if ssp_time_longname is not None:
wtime.long_name = ssp_time_longname
wtime.calendar = "noleap"
# adjust time to middle of month
if adj_time:
wtime_offset = 15 - time[0]
wtime[:] = time + wtime_offset
else:
wtime[:] = time
return ds
def create_fill_ancillary_vars(ds, landfrac, landmask, area):
wmask = ds.createVariable("landmask", np.int32, ("lat", "lon"))
warea = ds.createVariable("area", np.float64, ("lat", "lon"))
wfrac = ds.createVariable("landfrac", np.float64, ("lat", "lon"))
warea.units = "km2"
wfrac.units = "unitless"
wmask.units = "unitless"
warea.long_name = "Grid cell area"
wfrac.long_name = "Grid cell land fraction"
wmask.long_name = "Grid cell land mask"
warea.mode = "time-invariant"
wfrac.mode = "time-invariant"
wmask.mode = "time-invariant"
# write to file --------------------------------------------
wmask[:, :] = landmask
wfrac[:, :] = landfrac
warea[:, :] = area
return ds
def add_to_dataset(ds, var_name, data, units=None, mode=None, historical_source_files=None, scenario_source_files=None, long_name=None, cell_methods=None):
dims = ("time", "lat", "lon")
data_type = np.float64
wvar = ds.createVariable(
var_name,
data_type,
dims,
fill_value=data_type(1.0e36),
)
wvar[:, :, :] = data
if units is not None:
wvar.units = units
if mode is not None:
wvar.mode = mode
if historical_source_files is not None:
wvar.historical_source_files = historical_source_files
if scenario_source_files is not None:
wvar.scenario_source_files = scenario_source_files
if long_name is not None:
wvar.long_name = long_name
if cell_methods is not None:
wvar.cell_methods = cell_methods
return ds
def create_fill_forcing(ds, field_out, units, anomsf, field_out_wind, f, hdir, fdir, histfiles, sspfiles, long_name, anom_fld):
historical_source_files = "".join(histfiles).replace(hdir, "")
scenario_source_files = "".join(sspfiles).replace(fdir, "")
mode = "time-dependent"
if field_out[f] == "sfcWind":
long_name = str(long_name) + " U component " + anomsf[f]
var_name = field_out_wind[0]
data = anom_fld / np.sqrt(2)
else:
long_name = str(long_name) + " " + anomsf[f]
var_name = field_out[f]
data = anom_fld
# Was missing cell_methods attribute in original
ds = add_to_dataset(ds, var_name, data, units=units[f], mode=mode, historical_source_files=historical_source_files, scenario_source_files=scenario_source_files, long_name=long_name)
if field_out[f] == "sfcWind":
long_name = long_name.replace("U component", "V component")
var_name = field_out_wind[1]
# Was missing mode attribute in original
ds = add_to_dataset(ds, var_name, data, units=units[f], historical_source_files=historical_source_files, scenario_source_files=scenario_source_files, long_name=long_name, cell_methods="time: mean")
return ds
parser = argparse.ArgumentParser(description="Create anomaly forcing")
parser.add_argument(
"sspnum",
help="scenario number (1=SSP1-2.6, 2=SSP2-4.5, 3=SSP3-7.0, 4=SSP5-8.5)",
nargs="?",
type=int,
default=0,
)
parser.add_argument(
"--write_climo",
"--write-climo",
help="write out climatology files and exit",
action="store_true",
default=False,
)
parser.add_argument(
"--print_ssps",
"--print-ssps",
help="Just print out directory names and exit",
action="store_true",
default=False,
)
parser.add_argument(
"--output_dir", "--output-dir",
help="Top-level output directory (default: ./anomaly_forcing/). Sub-directory will be created for the selected scenario.",
type=str,
default=os.path.join(".", "anomaly_forcing"),
)
args = parser.parse_args()
if args.sspnum == 0:
sys.exit("choose valid ssp number")
# -------------------------------------------------------
print("Create anomaly forcing data that can be used by CTSM in CESM")
# Input and output directories make sure they exist
datapath = "/glade/campaign/collections/cmip/CMIP6/timeseries-cmip6/" # Path on casper
"""
The corrected SSP simulations:
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.101
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.102 (MOAR)
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.103
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.101
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.102 (MOAR)
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.103
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.101
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.102 (MOAR)
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.103
b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.101
b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.102 (MOAR)
b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.103
historical runs used to initialize SSPs:
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.001/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.010_v2.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.002/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.002-old/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.001/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.010_v2.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.002/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.003/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.004.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.003.oldTag/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.004.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP245.f09_g17.CMIP6-SSP2-4.5.001.BAD/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.010_v2.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.001/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.010_v2.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.002/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.003/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.004/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.005/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.006/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.001/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.010.clm2.r.2015-01-01-00000.nc'
b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.002/CaseDocs/lnd_in: finidat = \
'b.e21.BHIST.f09_g17.CMIP6-historical.011.clm2.r.2015-01-01-00000.nc'
_v2 is just used for restart files that have been spatially interpolated
"""
if os.path.exists(datapath):
print("Input data directory:" + datapath)
else:
sys.exit("Could not find input directory: " + datapath)
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
print("Output data directory:" + args.output_dir)
# Settings to run with
today = datetime.date.today()
creationdate = "_c" + today.strftime("%Y%m%d")
historydate = today.strftime("%a %b %d %Y")
sspdir_full = [
"b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.101/",
"b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.102/",
"b.e21.BSSP126cmip6.f09_g17.CMIP6-SSP1-2.6.103/",
"b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.101/",
"b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.102/",
"b.e21.BSSP245cmip6.f09_g17.CMIP6-SSP2-4.5.103/",
"b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.101/",
"b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.102/",
"b.e21.BSSP370cmip6.f09_g17.CMIP6-SSP3-7.0.103/",
"b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.101/",
"b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.102/",
"b.e21.BSSP585cmip6.f09_g17.CMIP6-SSP5-8.5.103/",
]
histdir_full = [
"b.e21.BHIST.f09_g17.CMIP6-historical.010/",
"b.e21.BHIST.f09_g17.CMIP6-historical.011/",
"b.e21.BHIST.f09_g17.CMIP6-historical.004/",
"b.e21.BHIST.f09_g17.CMIP6-historical.010/",
"b.e21.BHIST.f09_g17.CMIP6-historical.011/",
"b.e21.BHIST.f09_g17.CMIP6-historical.004/",
"b.e21.BHIST.f09_g17.CMIP6-historical.010/",
"b.e21.BHIST.f09_g17.CMIP6-historical.011/",
"b.e21.BHIST.f09_g17.CMIP6-historical.004/",
"b.e21.BHIST.f09_g17.CMIP6-historical.010/",
"b.e21.BHIST.f09_g17.CMIP6-historical.011/",
"b.e21.BHIST.f09_g17.CMIP6-historical.004/",
]
sim_pairs = zip(sspdir_full, histdir_full)
# print simulation pairs and exit
if args.print_ssps:
print(datapath, "\n")
for sim in sim_pairs:
print("SSP: ", sim[0])
print("historical: ", sim[1], "\n")
sys.exit()
sspnum = args.sspnum
if sspnum == 1:
# SSP1-26
ssptag = "SSP1-2.6"
histdir = histdir_full[0:3]
sspdir = sspdir_full[0:3]
elif sspnum == 2:
# SSP2-45
ssptag = "SSP2-4.5"
histdir = histdir_full[3:6]
sspdir = sspdir_full[3:6]
elif sspnum == 3:
# SSP3-70
ssptag = "SSP3-7.0"
histdir = histdir_full[6:9]
sspdir = sspdir_full[6:9]
elif sspnum == 4:
# SSP5-85
ssptag = "SSP5-8.5"
histdir = histdir_full[9:12]
sspdir = sspdir_full[9:12]
else:
sys.exit("sspnum is out of range: " + sspnum)
num_ens = len(sspdir)
if num_ens != len(histdir):
print("number of ensemble members not the same")
sys.exit("number of members different")
# Setup output directory
sspoutdir = "CMIP6-" + ssptag
outdir = os.path.join(args.output_dir, sspoutdir)
if not os.path.exists(outdir):
os.makedirs(outdir)
print("Output specific data directory :" + outdir)
climo_year = 2015
# ten years on either side (21 years total)
climo_base_nyrs = 21
write_climo = args.write_climo
nmo = 12
print("\n\n\n")
# needed to use QBOT and U10, not using V and U(for sfcwind)
field_in = ["TBOT", "RAIN", "FSDS", "FLDS", "QBOT", "PBOT", "WIND"]
field_out = ["tas", "pr", "rsds", "rlds", "huss", "ps", "sfcWind"]
units = ["K", "mm/s", "W m!U-2!N", "W m!U-2!N", "kg/kg", "Pa", "m/s"]
units_disp = ["K", "mm/s", "W m!U-2!N", "W m!U-2!N", "kg/kg", "Pa", "m/s"]
anomsf = [
"anomaly",
"scale factor",
"scale factor",
"scale factor",
"anomaly",
"anomaly",
"anomaly",
]
field_out_wind = ["uas", "vas"]
nfields = len(field_in)
output_format = "NETCDF3_64BIT_DATA"
# -- Loop over forcing fields ------------------------------------
for f in range(nfields):
# -- Loop over ensemble members ------------------------------
for nens in range(num_ens):
print("Beginning ensemble number ", nens + 1)
hist_case = histdir[nens]
fut_case = sspdir[nens]
dpath = datapath
dfile = "/lnd/proc/tseries/month_1/"
hdir = dpath + hist_case + dfile
fdir = dpath + fut_case + dfile
# Check that directories exist
if not os.path.exists(hdir):
sys.exit("Could not find directory: " + hdir)
if not os.path.exists(fdir):
sys.exit("Could not find directory: " + fdir)
# -- Get historical and SSP filenames --------------------
command = "ls " + hdir + "*." + field_in[f] + ".*.nc"
x2 = subprocess.Popen(command, stdout=subprocess.PIPE, shell="True")
x = x2.communicate()
histfiles = x[0].decode("utf-8").split("\n")
histfiles.remove("")
command = "ls " + fdir + "*." + field_in[f] + ".*.nc"
x2 = subprocess.Popen(command, stdout=subprocess.PIPE, shell="True")
x = x2.communicate()
sspfiles = x[0].decode("utf-8").split("\n")
sspfiles.remove("")
for hfile in histfiles:
print(hfile.split("month_1/")[-1])
if not os.path.exists(hfile):
sys.exit(hfile + " does not exist")
for sfile in sspfiles:
print(sfile.split("month_1/")[-1])
if not os.path.exists(sfile):
sys.exit(sfile + " does not exist")
# -- Read in historical data -----------
f1 = netcdf4.MFDataset(histfiles, "r")
if nens == 0:
# read in coordinates
lon = np.asfarray(f1.variables["lon"][:], np.float64)
lat = np.asfarray(f1.variables["lat"][:], np.float64)
hist_time = np.asfarray(f1.variables["time"][:], np.float64)
time_units = f1.variables["time"].units
# read landfrac, landmask, and area
landfrac = np.asfarray(f1.variables["landfrac"][:, :], np.float64)
landmask = np.asfarray(f1.variables["landmask"][:, :], np.float64)
area = np.asfarray(f1.variables["area"][:, :], np.float64)
ind = np.where(landfrac > 1.0e10)
landfrac[ind] = 0
x = time_units.split()[2]
ref_year = float(x.split("-")[0])
hist_yrstart = np.min(hist_time / 365.0 + ref_year).astype(int)
# overwrite hist_yrstart to select just 20 years prior to climo_year
hist_yrstart = climo_year - (climo_base_nyrs - 1)
hist_yrend = (np.max(hist_time / 365.0 + ref_year) - 1).astype(int)
hist_nyrs = hist_yrend - hist_yrstart + 1
if f == 0:
print("hist years: ", hist_yrstart, hist_yrend, hist_nyrs)
hist_ind = np.where(
np.logical_and(
hist_time / 365.0 + ref_year > hist_yrstart,
hist_time / 365.0 + ref_year <= (hist_yrend + 1),
)
)[0]
hist_time = hist_time[hist_ind]
nlat = lat.size
nlon = lon.size
ntime = hist_time.size
hist_fld = np.zeros((ntime, nlat, nlon))
hist_fld += np.asfarray(f1.variables[field_in[f]][hist_ind, :, :], np.float64)
f1.close()
# add SNOW to RAIN
if field_in[f] == "RAIN":
histfiles2 = [file.replace("RAIN", "SNOW") for file in histfiles]
f1 = netcdf4.MFDataset(histfiles2, "r")
hist_fld += np.asfarray(f1.variables["SNOW"][hist_ind, :, :], np.float64)
f1.close()
if f == 0:
print(
"hist_time: ",
hist_time[0] / 365.0 + ref_year,
hist_time[-1] / 365.0 + ref_year,
)
# read in future data ---------------------
f1 = netcdf4.MFDataset(sspfiles, "r")
if nens == 0:
ssp_time = np.asfarray(f1.variables["time"][:], np.float64)
ssp_time_units = f1.variables["time"].units
ssp_time_longname = f1.variables["time"].long_name
x = ssp_time_units.split()[2]
ssp_ref_year = float(x.split("-")[0])
# adjust ssp_time to reference time of hist_time
# ssp_time += 365*(ssp_ref_year - ref_year)
# ssp_ref_year = ref_year
# adjust hist_time to reference time of ssp_time
hist_time += 365 * (ref_year - ssp_ref_year)
ref_year = ssp_ref_year
# ssp_ind could be modified to subset data if needed...
ssp_ind = np.arange(ssp_time.size, dtype=int)
ssp_time = ssp_time[ssp_ind]
long_name = f1.variables[field_in[f]].long_name
ntime_ssp = ssp_time.size
ssp_fld = np.zeros((ntime_ssp, nlat, nlon))
ssp_yrstart = np.min(ssp_time / 365.0 + ref_year).astype(int)
ssp_yrend = (np.max(ssp_time / 365.0 + ref_year) - 1).astype(int)
ssp_nyrs = ssp_yrend - ssp_yrstart + 1
if f == 0:
print("SSP years: ", ssp_yrstart, ssp_yrend, ssp_nyrs)
tot_nyrs = hist_nyrs + ssp_nyrs
outfile_suffix = (
".CESM."
+ ssptag
+ "."
+ str(ssp_yrstart)
+ "-"
+ str(ssp_yrend)
+ creationdate
+ ".nc"
)
ssp_fld += np.asfarray(f1.variables[field_in[f]][ssp_ind, :, :], np.float64)
f1.close()
# add SNOW to RAIN
if field_in[f] == "RAIN":
sspfiles2 = [file.replace("RAIN", "SNOW") for file in sspfiles]
f1 = netcdf4.MFDataset(sspfiles2, "r")
ssp_fld += np.asfarray(f1.variables["SNOW"][ssp_ind, :, :], np.float64)
f1.close()
if f == 0:
print(
"ssp_time: ",
ssp_time[0] / 365.0 + ssp_ref_year,
ssp_time[-1] / 365.0 + ssp_ref_year,
ssp_time.size,
)
# -- end Loop over ensemble members ------------------------------
# normalize summed fields by number of ensemble members
hist_fld = hist_fld / float(num_ens)
ssp_fld = ssp_fld / float(num_ens)
# concatenate arrays to form contiguous time series
temp_fld = np.concatenate((hist_fld, ssp_fld), axis=0)
time = np.concatenate((hist_time, ssp_time), axis=0)
tm = time.size
# smooth data by applying boxcar averaging to sequence of months
stemp_fld = np.copy(temp_fld)
for n in range(tm):
# 21 years of jan, feb, etc. centered on each month in data
ind = nmo * (np.arange(climo_base_nyrs) - (climo_base_nyrs - 1) / 2) + n
# account for edges
m = np.where(np.logical_and(ind >= 0, ind < tm))[0]
ind2 = ind[m].astype(int)
stemp_fld[n, :, :] = np.sum(temp_fld[ind2, :, :], axis=0) / float(ind2.size)
if f == 0:
print(
"full time: ",
time[0] / 365.0 + ref_year,
time[-1] / 365.0 + ref_year,
time.size,
)
# create climatology of smoothed data
climo = np.zeros((nmo, nlat, nlon))
t_climo_year = np.argmin(np.abs((time / 365.0 + ref_year) - climo_year))
# shift to january of climo_year
t_climo_year += 1
if f == 0:
print((time[t_climo_year] / 365.0 + ref_year))
for n in range(nmo):
ind = (
nmo * (np.arange(climo_base_nyrs) - (climo_base_nyrs - 1) / 2)
+ t_climo_year
+ n
).astype(int)
climo[n, :, :] = np.sum(stemp_fld[ind, :, :], axis=0) / float(ind.size)
if f == 0:
print("climo calculated")
# extract smoothed SSP data
t_ssp_start = (ssp_yrstart - hist_yrstart) * nmo
if f == 0:
print((time[t_ssp_start] / 365.0 + ref_year))
ssp_fld_smoothed = stemp_fld[t_ssp_start:, :, :]
# calculate anomaly relative to climatology
anom_fld = np.zeros((ssp_fld_smoothed.shape))
for y in range(ssp_nyrs):
ind = (np.arange(nmo) + y * nmo).astype(int)
if anomsf[f] == "anomaly":
anom_fld[ind, :, :] = ssp_fld_smoothed[ind, :, :] - climo
if anomsf[f] == "scale factor":
tmp = ssp_fld_smoothed[ind, :, :]
ind2 = np.where(climo != 0.0)
# initialize scalar anomaly to 1
tmp2 = np.ones(tmp.shape)
# calculate scalar anomaly
tmp2[ind2] = tmp[ind2] / climo[ind2]
# place upper limit on scalar anomalies
max_scale_factor = 5.0
if field_in[f] == "FSDS":
max_scale_factor = 2.0
ind2 = np.where(tmp2 > max_scale_factor)
tmp2[ind2] = max_scale_factor
anom_fld[ind, :, :] = tmp2
# ----- end of year loop -------
# write out climo to check field -------------------------
if write_climo:
# Use NetCDF4 format, because using older NetCDF formats are too slow
w = netcdf4.Dataset(
os.path.join(outdir, field_out[f] + "_climo" + creationdate + ".nc"),
"w",
format=output_format,
)
w = create_fill_latlon(w, lat, "lat")
w = create_fill_latlon(w, lon, "lon")
w = create_fill_time(w, time[0:12], nmo)
add_to_dataset(w, field_out[f], climo)
w.close()
# Use NetCDF4 format, because using older NetCDF formats are too slow
w = netcdf4.Dataset(
os.path.join(outdir, field_out[f] + "_smooth" + creationdate + ".nc"),
"w",
format=output_format,
)
w = create_fill_latlon(w, lat, "lat")
w = create_fill_latlon(w, lon, "lon")
w = create_fill_time(w, time, tm)
add_to_dataset(w, field_out[f], temp_fld)
add_to_dataset(w, "smooth_" + field_out[f], stemp_fld)
w.close()
print("Exit early after writing out climatology\n\n")
sys.exit()
# create netcdf file ---------------------------------
if f == 0:
# Use NetCDF4 format, because using older NetCDF formats are too slow
# Will need to convert to CDF5 format at the end, as we can't seem to
# output in CDF5 format using netCDF4 python interfaces
outfilename = os.path.join(outdir, "af.allvars" + outfile_suffix)
print("Creating: " + outfilename)
outfile = netcdf4.Dataset(outfilename, "w", format=output_format)
# creation date on the file
command = 'date "+%Y/%m/%d"'
x2 = subprocess.Popen(command, stdout=subprocess.PIPE, shell="True")
x = x2.communicate()
timetag = x[0].decode("utf-8").strip()
# Add global attributes and get hdir/fdir
hdir, fdir = add_global_attributes(outfile, historydate, histdir, sspdir, num_ens, climo_year, climo_base_nyrs, dpath, dfile, hist_yrstart, hist_yrend, ssp_yrstart, ssp_yrend, timetag)
# Create dimensions
outfile = create_fill_latlon(outfile, lat, "lat")
outfile = create_fill_latlon(outfile, lon, "lon")
outfile = create_fill_time(outfile, ssp_time, None, ssp_time_units=ssp_time_units, ssp_time_longname=ssp_time_longname, adj_time=True)
# Create and fill ancillary variables
outfile = create_fill_ancillary_vars(outfile, landfrac, landmask, area)
# -- End if on open file
outfile = create_fill_forcing(outfile, field_out, units, anomsf, field_out_wind, f, hdir, fdir, histfiles, sspfiles, long_name, anom_fld)
# -- End Loop over forcing fields ------------------------------------
outfile.close()
print("\n\nSuccessfully made anomaly forcing datasets\n")