Skip to content

Commit 0d54873

Browse files
authored
Merge pull request #3 from UMD-AOSC/day2-dev
update nudging, hybrid, compute analysis LEs
2 parents 41b094d + 20f7c14 commit 0d54873

13 files changed

Lines changed: 214 additions & 30 deletions
59 Bytes
Binary file not shown.
118 Bytes
Binary file not shown.

analysis_init.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
#-----------------------------------------------------------------------
7474
# Initialize the ensemble
7575
#-----------------------------------------------------------------------
76-
das.edim = 3 #np.int(1*xdim)
76+
das.edim = 2 #np.int(1*xdim)
7777
das.ens_bias_init = 0
7878
das.ens_sigma_init = 0.1
7979

@@ -85,18 +85,18 @@
8585
I = np.identity(xdim)
8686

8787
# Set background error covariance
88-
sigma_b = 1.0
88+
sigma_b = 1.0 #1.0
8989
B = I * sigma_b**2
9090

9191
# Set observation error covariance
92-
sigma_r = 1.0
92+
sigma_r = 0.1
9393
R = I * sigma_r**2
9494

9595
# Set the linear observation operator matrix as the identity by default
9696
H = I
9797

9898
# Set constant matrix for nudging
99-
const = 0.5
99+
const = 1.0
100100
C = I * const
101101

102102
das.setB(B)

class_da_system.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def nudging(self,xb,yo):
222222
Ht = np.matrix(self.Ht)
223223

224224
C = self.C
225-
xa = xb + C*(yo - Hl*xb)
225+
xa = xb + np.dot(C,Ht)*(yo - Hl*xb)
226226

227227
if verbose:
228228
print('xb = ')
@@ -238,7 +238,7 @@ def nudging(self,xb,yo):
238238
print('Ht = ')
239239
print(Ht)
240240

241-
KH = Ht*C*Hl
241+
KH = np.dot(np.dot(Hl,C),Ht)
242242

243243
return xa.A1,KH
244244

@@ -577,6 +577,8 @@ def HybridGain(self,Xb,yo):
577577
#---------------------------------------------------------------------------------------------------
578578
# Use a hybrid method to compute the analysis
579579

580+
verbose = False
581+
580582
# Make sure inputs are matrix and column vector types
581583
Xb = np.matrix(Xb)
582584
yo = np.matrix(yo).flatten().T
@@ -590,16 +592,22 @@ def HybridGain(self,Xb,yo):
590592

591593
# Compute ETKF analysis
592594
Xa_ETKF, KH_ETKF = self.ETKF(Xb,yo)
593-
print('Xa_ETKF = ')
594-
print(Xa_ETKF)
595+
if verbose:
596+
print('Xa_ETKF = ')
597+
print(Xa_ETKF)
595598

596599
# Compute 3DVar analysis
597600
xa_ETKF = np.mean(Xa_ETKF,axis=1)
598-
print('xa_ETKF = ')
599-
print(xa_ETKF)
601+
602+
if verbose:
603+
print('xa_ETKF = ')
604+
print(xa_ETKF)
605+
600606
xa_3DVar, KH_3DVar = self._3DVar(xa_ETKF,yo)
601-
print('xa_3DVar = ')
602-
print(xa_3DVar)
607+
608+
if verbose:
609+
print('xa_3DVar = ')
610+
print(xa_3DVar)
603611

604612
xa_ETKF = np.matrix(xa_ETKF).flatten().T
605613
xa_3DVar = np.matrix(xa_3DVar).flatten().T
@@ -611,8 +619,9 @@ def HybridGain(self,Xb,yo):
611619
xa_hybrid = (1-alpha)*xa_ETKF + alpha*xa_3DVar
612620
Xa = Xa_ETKF + np.matlib.repmat(xa_hybrid,1,edim)
613621

614-
print('xa_hybrid = ')
615-
print(xa_hybrid)
622+
if verbose:
623+
print('xa_hybrid = ')
624+
print(xa_hybrid)
616625

617626
KH = (1-alpha)*KH_ETKF + alpha*KH_3DVar
618627

class_obs_data.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import pickle
33

44
class obs_data:
5-
def __init__(self,t=[0],pos=[0],val=[0],err=[0],bias=[],xt=[0],name='uninitialized'):
5+
def __init__(self,t=[0],pos=[0],val=[0],err=[0],bias=[0],xt=[0],name='uninitialized',mu_init=[],sigma_init=[]):
66
tdim = 0
77
xdim = 0
88
odim = 0
@@ -16,9 +16,13 @@ def __init__(self,t=[0],pos=[0],val=[0],err=[0],bias=[],xt=[0],name='uninitializ
1616
self.xt = np.array(xt)
1717
self.dep = []
1818
self.climvar = []
19+
self.mu_init = mu_init
20+
self.sigma_init = sigma_init
1921

2022
def __str__(self):
2123
print(self.name)
24+
print('mu_init = ', self.mu_init)
25+
print('sigma_init = ', self.sigma_init)
2226
print('Obs Error:')
2327
print(self.err)
2428
print('Positions:')

generate_analysis_3dDet.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,13 @@
106106

107107
# Archive the KH matrix
108108
KH_history.append(deepcopy(KH))
109-
KH_idx.append(i)
109+
KH_idx.append(i+acyc_step)
110110

111111
das.setKH(KH_history,KH_idx)
112112

113+
print('Last background error covariance matrix B = ')
114+
print(das.getB())
115+
113116
sv.setTrajectory(xa_history)
114117
sv.setName(name)
115118
das.setStateVector(sv)

generate_analysis_3dEns.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130

131131
# Archive the KH matrix
132132
KH_history.append(deepcopy(KH))
133-
KH_idx.append(i)
133+
KH_idx.append(i+acyc_step)
134134

135135
das.setKH(KH_history,KH_idx)
136136

generate_analysis_LEs.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Tutorial: "A tour of Data Assimilation methods"
2+
# Model: Lorenz-63
3+
# DA Methods: Nudging, 3D-Var, 4D-Var, Particle Filter, EnKF, Hybrid
4+
import numpy as np
5+
from class_lorenz63 import lorenz63
6+
from class_state_vector import state_vector
7+
from class_obs_data import obs_data
8+
from class_da_system import da_system
9+
from sys import argv
10+
from copy import deepcopy
11+
12+
#-----------------------------------------------------------------------
13+
# Usage:
14+
# python generate_analysis_LEs.py <method>
15+
#-----------------------------------------------------------------------
16+
17+
#-----------------------------------------------------------------------
18+
# Read the L63 nature run
19+
#-----------------------------------------------------------------------
20+
name1 = 'x_nature'
21+
infile1 = name1+'_Mhist.pkl'
22+
sv1 = state_vector()
23+
sv1 = sv1.load(infile1)
24+
x_nature = sv1.getTrajectory()
25+
t_nature = sv1.getTimes()
26+
27+
#-----------------------------------------------------------------------
28+
# Read the analysis
29+
#-----------------------------------------------------------------------
30+
name = 'x_analysis'
31+
method = argv[1]
32+
infile2 = name+'_'+method+'.pkl'
33+
das = da_system()
34+
das = das.load(infile2)
35+
sv = das.getStateVector()
36+
KH, khidx = das.getKH()
37+
print(sv)
38+
39+
#print('KH = ')
40+
#print(KH)
41+
#print('khidx = ')
42+
#print(khidx)
43+
44+
#-----------------------------------------------------------------------
45+
# Initialize the timesteps
46+
#-----------------------------------------------------------------------
47+
t_nature = sv1.getTimes()
48+
dt = (t_nature[1] - t_nature[0])
49+
_,xdim = np.shape(x_nature)
50+
acyc_step = das.acyc_step
51+
dtau = das.dtau
52+
53+
#------------------------------------------------------------------
54+
# Propagate the TLM in time and intermittently
55+
# performing a QR decomposition to rescale as necessary.
56+
#------------------------------------------------------------------
57+
rescale_interval = 2*acyc_step
58+
I = np.identity(xdim)
59+
Mhist = sv1.getMhist()
60+
M2hist = []
61+
Qhist = []
62+
Rhist = []
63+
hist_idx = []
64+
M2 = I
65+
maxit = das.maxit
66+
ki=0
67+
for i in range(maxit - acyc_step):
68+
69+
# In order to find the eigenvalues of the final matrix describing
70+
# the evolution of the entire trajectory, we would like to
71+
# compute the product of the M matrices to identify the
72+
# stretching and contracting that results.
73+
#
74+
# Iterate the linear propagator (evolution matrix)
75+
# to fit the specified rescaling time interval
76+
77+
M2 = np.dot(Mhist[i],M2)
78+
79+
# If there was an analysis at this time index, then
80+
# apply the contraction (I-KH) applied by the DA
81+
if khidx[ki] == i:
82+
M2 = np.dot(I-KH[ki],M2)
83+
ki = ki+1
84+
85+
# Rescale via QR decomposition
86+
if (np.mod(i+1,rescale_interval)==0):
87+
Q,R = np.linalg.qr(M2)
88+
89+
# Store the Q and R matrices
90+
M2hist.append(deepcopy(M2))
91+
Qhist.append(deepcopy(Q))
92+
Rhist.append(deepcopy(R))
93+
hist_idx.append(i)
94+
M2 = Q
95+
96+
print('Last Q = ')
97+
print(Q)
98+
99+
print('Last R = ')
100+
print(R)
101+
102+
sv.rescale_interval = rescale_interval
103+
sv.hist_ainc = acyc_step
104+
sv.hist_dtau = dtau
105+
sv.hist_idx = hist_idx
106+
sv.setM2hist(M2hist)
107+
sv.setQhist(Qhist)
108+
sv.setRhist(Rhist)
109+
110+
#-----------------------------------------------------------------------
111+
# Compute the Lyapunov Exponents (LEs) of the data assimilation system
112+
#-----------------------------------------------------------------------
113+
lyap_sum = np.zeros(xdim)
114+
rescale_cnt = 0
115+
for i in range(len(Rhist)):
116+
R = Rhist[i]
117+
rdiag = abs(np.diag(R))
118+
lyap_sum = lyap_sum + np.log(rdiag) / (dtau)
119+
rescale_cnt = rescale_cnt + 1
120+
121+
lyap_avg = lyap_sum / rescale_cnt
122+
sv.setLEs(lyap_avg)
123+
124+
print(sv)
125+
print('Lyapunov Exponents:')
126+
print(lyap_avg)
127+
128+
#------------------------------------------------------------------
129+
# Store the nature run data
130+
#------------------------------------------------------------------
131+
outfile = name+'_LEs.pkl'
132+
sv.save(outfile)

generate_nature_QR.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
# Initialize the timesteps
2323
#-----------------------------------------------------------------------
2424
t_nature = sv.getTimes()
25-
ainc_step = 10 # (how frequently to perform an analysis)
26-
dtau = (t_nature[ainc_step] - t_nature[0])
25+
inc_step = 10 # (how frequently to perform an analysis)
26+
dtau = (t_nature[inc_step] - t_nature[0])
2727
maxit,xdim = np.shape(x_nature)
2828

2929
#------------------------------------------------------------------
@@ -34,7 +34,7 @@
3434
Mhist = sv.getMhist()
3535
#print('Mhist = ')
3636
#print(Mhist)
37-
rescale_interval = ainc_step
37+
rescale_interval = inc_step
3838

3939
M2hist = []
4040
Qhist = []
@@ -77,7 +77,7 @@
7777
print(R)
7878

7979
sv.rescale_interval = rescale_interval
80-
sv.hist_ainc = ainc_step
80+
sv.hist_ainc = inc_step
8181
sv.hist_dtau = dtau
8282
sv.hist_idx = hist_idx
8383
sv.setM2hist(M2hist)

generate_observations.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#--------------------------------------------------------------------------------
1616
# Create observation object
1717
#--------------------------------------------------------------------------------
18-
obs = obs_data(name='observe_full_state',err=[],bias=mu,val=[],pos=[])
18+
obs = obs_data(name='observe_full_state', mu_init=mu, sigma_init=sigma)
1919

2020
#--------------------------------------------------------------------------------
2121
# Read the nature run
@@ -59,6 +59,8 @@
5959
obs.setDep(yo-hx)
6060
obs.setPos(pos)
6161

62+
print(obs)
63+
6264
#--------------------------------------------------------------------------------
6365
# Store the true and noisy observations
6466
#--------------------------------------------------------------------------------

0 commit comments

Comments
 (0)