forked from modons/LMR
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LMR_prior.py
546 lines (436 loc) · 24.3 KB
/
LMR_prior.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
"""
Module: LMR_prior.py
Purpose: Contains definitions of classes defining the various sources
(i.e. model simulations and reanalyses) which may be used as to
populate the prior in the LMR. Also contains the code used to
randomly pick model states along the temporal domain to populate
the prior ensemble.
Originator: Robert Tardif | Dept. of Atmospheric Sciences, Univ. of Washington
| January 2015
Revisions:
- Added the ERA20CM (ECMWF 20th century ensemble simulation) as a
possible source of prior data to be used by the LMR.
[R. Tardif, U. of Washington, December 2015]
- Added the option of detrending the prior
[R. Tardif, U. of Washington, March 2016]
- Added the 'ccsm4_isotope_controlrun' as a possible prior source.
Contains simulated isotope (d18O) field.
[R. Tardif, U. of Washington, May 2016]
- Added 'ccsm3_trace21ka' (simulation of the transient climate of
the last 21k years i.e. LGM -> Holocene) as a possible prior source
[R. Tardif, U. of Washington, Nov 2016]
- Added the 'loveclim_goosse2005' class (Common Era simulation performed
with the LOVECLIM v1.0 model by Goosse et al. GRL 2005) as a
possible prior source.
[R. Tardif, U. of Washington, Jul 2017]
- Added the 'cgenie_petm' class (simulations of the PETM with the
cGENIE EMIC) as a possible prior source.
[R. Tardif, U. of Washington, Aug 2017]
- Added the 'ihadcm3_preindustrial_control' class for use of data from
the isotope-enabled HadCM3 model simulation of preindustrial climate
(the "0kyr" run, part of paleoclimate simulations performed by
Max Holloway of the British Antarctic Survey)
[R. Tardif, U. of Washington, Aug 2017]
- Added the 'icesm_last_millennium' and 'icesm_last_millennium_historical'
classes for isotope-enabled CESM model simulations of the
last millennium (0850 to 1850) and of the last millennium to which
a modern historical simulation (1850 to 2006) has been concatenated.
[R. Tardif, U. of Washington, Nov 2017]
"""
import numpy as np
from random import sample, seed
from copy import deepcopy
# -------------------------------------------------------------------------------
# *** Prior source assignment --------------------------------------------------
# -------------------------------------------------------------------------------
# All logic for prior object assignment
def prior_assignment(iprior):
if iprior == 'generic':
prior_object = prior_generic()
elif iprior == 'GISTEMP':
prior_object = prior_gistemp()
elif iprior == 'ccsm4_last_millenium':
prior_object = prior_ccsm4_last_millenium()
elif iprior == 'ccsm4_preindustrial_control':
prior_object = prior_ccsm4_preindustrial_control()
elif iprior == 'ccsm4_isotope_controlrun':
prior_object = prior_ccsm4_isotope_controlrun()
elif iprior == 'mpi-esm-p_last_millenium':
prior_object = prior_mpi_esm_p_last_millenium()
elif iprior == 'gfdl-cm3_preindustrial_control':
prior_object = prior_gfdl_cm3_preindustrial_control()
elif iprior == '20cr':
prior_object = prior_20cr()
elif iprior == 'era20c':
prior_object = prior_era20c()
elif iprior == 'era20cm':
prior_object = prior_era20cm()
elif iprior == 'loveclim_goosse2005':
prior_object = prior_loveclim_goosse2005()
elif iprior == 'icesm_last_millennium':
prior_object = prior_icesm_last_millennium()
elif iprior == 'icesm_last_millennium_historical':
prior_object = prior_icesm_last_millennium_historical()
elif iprior == 'ihadcm3_preindustrial_control':
prior_object = prior_ihadcm3_preindustrial_control()
elif iprior == 'ccsm3_trace21ka':
prior_object = prior_ccsm3_trace21ka()
elif iprior == 'cgenie_petm':
prior_object = prior_cgenie_petm()
return prior_object
# -------------------------------------------------------------------------------
# *** Master class for model data as prior --------------------------------------
# -------------------------------------------------------------------------------
class prior_master(object):
'''
This is the master class for the prior data. Inherent to create classes for each prior source.
'''
# Populate the prior ensemble from gridded model/analysis data
def populate_ensemble(self,prior_source, prior_cfg):
# Load prior data from file(s) - multiple state variables
self.read_prior()
Nens_max = len(self.prior_dict[list(self.prior_dict.keys())[0]]['years'])
if self.Nens and self.Nens > Nens_max:
raise SystemExit('ERROR in populate_ensemble! Specified ensemble size too large for available nb of states. '
'Max allowed with current configuration: %d' %Nens_max)
nbvars = len(self.statevars)
# Check consistency between specified state variables and uploaded dictionary
if len(list(self.prior_dict.keys())) != nbvars:
raise SystemExit('Problem with load of prior state variables. Exiting!')
# Defining content of state vector => dictionary: state_vect_content
# NOTE: now assumes that dims of state variables are (lat,lon) only !!!
state_vect_info = {}
# Loop over state variables
Nx = 0
timedim = []
for var in list(self.prior_dict.keys()):
vartype = self.prior_dict[var]['vartype']
dct = {}
timedim.append(len(self.prior_dict[var]['years']))
spacecoords = self.prior_dict[var]['spacecoords']
if '2D' in vartype:
dim1, dim2 = spacecoords
# How are these defined? Check dims of arrays
if len(self.prior_dict[var][dim1].shape) == 2 and len(self.prior_dict[var][dim2].shape) == 2:
# we have a field defined on an irregular lat/lon grid, requiring lat & lon
# each be defined with a 2d array
ndim1 = self.prior_dict[var][dim1].shape[0]
ndim2 = self.prior_dict[var][dim1].shape[1]
elif len(self.prior_dict[var][dim1].shape) == 1 and len(self.prior_dict[var][dim2].shape) == 1:
# regular lat/lon array : lat and lon can be defined with 1d arrays
ndim1 = len(self.prior_dict[var][dim1])
ndim2 = len(self.prior_dict[var][dim2])
else:
raise SystemExit('ERROR in populate_ensemble: Unrecognized info on spatial dimensions. Exiting!')
ndimtot = ndim1*ndim2
dct['pos'] = (Nx,Nx+(ndimtot)-1)
dct['spacecoords'] = spacecoords
dct['spacedims'] = (ndim1,ndim2)
if 'lat' in spacecoords and 'lon' in spacecoords:
dct['vartype'] = '2D:horizontal'
elif 'lat' in spacecoords and 'lev' in spacecoords:
dct['vartype'] = '2D:meridional_vertical'
else:
raise SystemExit('ERROR in populate_ensemble: Unrecognized info on spatial dimensions. Exiting!')
elif vartype == '1D:meridional':
dim1, = spacecoords
ndim1 = self.prior_dict[var][dim1].shape[0]
ndimtot = ndim1
dct['pos'] = (Nx,Nx+(ndimtot)-1)
dct['spacecoords'] = spacecoords
dct['spacedims'] = (ndim1,)
dct['vartype'] = vartype
else:
# variable is simple time series'
ndimtot = 1
dct['pos'] = (Nx,Nx+(ndimtot)-1)
dct['spacecoords'] = None
dct['spacedims'] = None
dct['vartype'] = '0D:time series'
# assign to master dictionary
state_vect_info[var] = dct
# determining length of state vector
Nx = Nx + (ndimtot)
# Looped through all state variables, now a summary:
print(' ')
print('State vector information:')
print('Nx =', Nx)
print('state_vect_info=', state_vect_info)
# time dimension consistent across variables?
if all(x == timedim[0] for x in timedim):
ntime = timedim[0]
else:
raise SystemExit('ERROR im populate_ensemble: time dimension not consistent across all state variables. Exiting!')
# If Nens is None, use all of prior with no random sampling
if self.Nens is None:
take_sample = False
self.Nens = ntime
else:
take_sample = True
# Array that will contain the prior ensemble (state vector)
Xb = np.zeros(shape=[Nx,self.Nens]) # no time dimension now...
# ***NOTE: Following code assumes that data for a given year are located at same array time index across all state variables
if take_sample:
print('Random selection of', str(self.Nens), 'ensemble members')
# Populate prior ensemble from randomly sampled states
seed(prior_cfg.seed)
ind_ens = sample(list(range(ntime)), self.Nens)
else:
print('Using entire consecutive years in prior dataset.')
ind_ens = list(range(ntime))
self.prior_sample_indices = ind_ens
# To keep spatial coords of gridpoints (needed geo. information)
Xb_coords = np.empty(shape=[Nx,2]) # 2 is max nb of spatial dim a variable can take
Xb_coords[:,:] = np.NAN # initialize with Nan's
for var in list(self.prior_dict.keys()):
vartype = self.prior_dict[var]['vartype']
indstart = state_vect_info[var]['pos'][0]
indend = state_vect_info[var]['pos'][1]
if '2D' in vartype:
# Loop over ensemble members
for i in range(0,self.Nens):
Xb[indstart:indend+1,i] = self.prior_dict[var]['value'][ind_ens[i],:,:].flatten()
# get the name of the spatial coordinates for state variable 'var'
coordname1, coordname2 = state_vect_info[var]['spacecoords']
# load in the coord values from data dictionary
coord1 = self.prior_dict[var][coordname1]
coord2 = self.prior_dict[var][coordname2]
# check how coords are defined:
# 1d (regular lat/lon grid) or 2d (irregular lat/lon grid)
if len(coord1.shape) == 1 and len(coord2.shape) == 1:
ndim1 = coord1.shape[0]
ndim2 = coord2.shape[0]
X_coord1 = np.array([coord1,]*ndim2).transpose()
X_coord2 = np.array([coord2,]*ndim1)
elif len(coord1.shape) == 2 and len(coord2.shape) == 2:
ndim1, ndim2 = coord1.shape
X_coord1 = coord1
X_coord2 = coord2
Xb_coords[indstart:indend+1,0] = X_coord1.flatten()
Xb_coords[indstart:indend+1,1] = X_coord2.flatten()
# Some cleanup
del coord1
del coord2
del X_coord1
del X_coord2
elif vartype == '1D:meridional':
# Loop over ensemble members
for i in range(0,self.Nens):
Xb[indstart:indend+1,i] = self.prior_dict[var]['value'][ind_ens[i],:].flatten()
# get the name of the spatial coordinate for state variable 'var'
coordname1, = state_vect_info[var]['spacecoords']
# load in the coord values from data dictionary
X_coord1 = self.prior_dict[var][coordname1]
Xb_coords[indstart:indend+1,0] = X_coord1.flatten()
# Some cleanup
del X_coord1
elif vartype == '0D:time series':
# Loop over ensemble members
for i in range(0,self.Nens):
Xb[indstart:indend+1,i] = self.prior_dict[var]['value'][ind_ens[i]].flatten()
else:
raise SystemExit('ERROR im populate_ensemble: variable of unrecognized spatial dimensions. Exiting!')
"""
# RT dev ... ... ...
# if anom_reference period is defined, re-centering sample around a mean of zero ? ...
print self.prior_dict[var]['climo'].shape
print self.prior_dict[var]['value'][ind_ens[0]].shape
print np.mean(Xb[indstart:indend+1,:], axis=1)
sample_mean = np.mean(Xb[indstart:indend+1,:], axis=1)
Xb[indstart:indend+1,:] = Xb[indstart:indend+1,:] - sample_mean[:,np.newaxis]
print np.mean(Xb[indstart:indend+1,:], axis=1)
exit(1)
# RT dev ... ... ...
"""
# Returning state vector Xb as masked array, if it contains
# at least one invalid value
if np.any(np.isnan(Xb)):
# Returning state vector Xb as masked array
Xb_res = np.ma.masked_invalid(Xb)
# Set fill_value to np.nan
np.ma.set_fill_value(Xb_res, np.nan)
# array indices of masked & valid elements
inds_mask = np.nonzero(Xb_res.mask)
inds_valid = np.nonzero(~Xb_res.mask)
else:
Xb_res = Xb
# Assign return variables
self.ens = Xb_res
self.coords = Xb_coords
self.full_state_info = state_vect_info
return
# -------------------------------------------------------------------------------
# Classes for specific model/simulation -----------------------------------------
# -------------------------------------------------------------------------------
# class for generic object
class prior_generic(prior_master):
pass
# class for GISTEMP gridded surface temperature dataset
class prior_gistemp(prior_master):
pass
# class for BerkeleyEarth gridded surface temperature dataset
class prior_BerkeleyEarth(prior_master):
pass
# class for the CCSM4 Last Millennium simulation
class prior_ccsm4_last_millenium(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the CCSM4 Pre-Industrial Control simulation
class prior_ccsm4_preindustrial_control(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the CCSM4 isotope-enabled control simulation (from D. Noone)
class prior_ccsm4_isotope_controlrun(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the MPI-ESM-P Last Millenniun simulation
class prior_mpi_esm_p_last_millenium(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the GFDL-CM3 Pre-Industrial Control simulation
class prior_gfdl_cm3_preindustrial_control(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for NOAA's 20th century reanalysis (20CR)
class prior_20cr(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for ECMWF's 20th century reanalysis (ERA20C)
class prior_era20c(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for ECMWF's 20th century model ensemble (ERA20CM)
class prior_era20cm(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model_ensemble
self.prior_dict = read_gridded_data_CMIP5_model_ensemble(self.prior_datadir,
self.prior_datafile,
self.statevars)
return
# class for the LOVECLIM 1.0 Common Era simulation of Goosse et al. (GRL 2005)
class prior_loveclim_goosse2005(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the iCESM last millennium simulation
class prior_icesm_last_millennium(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the concatenated iCESM last millennium and historical simulations
class prior_icesm_last_millennium_historical(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the the isotope-enabled HadCM3 model simulation of preindustrial climate
# (the "0kyr" time slice, part of a series of equilibrium paleoclimate simulations
class prior_ihadcm3_preindustrial_control(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_CMIP5_model
self.prior_dict = read_gridded_data_CMIP5_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference,
self.statevars_info)
return
# class for the simulation of the transient climate of the last 21k years (TraCE21ka)
class prior_ccsm3_trace21ka(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_TraCE21ka
self.prior_dict = read_gridded_data_TraCE21ka(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference)
return
# class for the cGENIE simulation if the PETM
class prior_cgenie_petm(prior_master):
def read_prior(self):
from load_gridded_data import read_gridded_data_cGENIE_model
self.prior_dict = read_gridded_data_cGENIE_model(self.prior_datadir,
self.prior_datafile,
self.statevars,
self.avgInterval,
self.detrend,
self.anom_reference)
return