|
| 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) |
0 commit comments