def HMMs(stepper="Tay2", resolution="Low", R=1): """Define the various HMMs used.""" # Use small version of L96. Has 4 non-stable Lyapunov exponents. Nx = 10 # Time sequence # Grudzien'2020 uses the below chronology with Ko=25000, BurnIn=5000. t = modelling.Chronology(dt=0.005, dto=.1, T=30, Tplot=Tplot, BurnIn=10) if resolution == "High": t.dt = 0.001 elif stepper != "Tay2": t.dt = 0.01 # Dynamical operator Dyn = {'M': Nx, 'model': steppers(stepper)} # (Random) initial condition X0 = modelling.GaussRV(mu=x0(Nx), C=0.001) # Observation operator jj = range(Nx) # obs_inds Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = R return modelling.HiddenMarkovModel(Dyn, Obs, t, X0)
def X0(param_mean, param_var): # State x0 = np.zeros(Nx) C0 = .01*np.ones(Nx) # Append param params x0 = np.hstack([x0, param_mean*np.ones(Np)]) C0 = np.hstack([C0, param_var*np.ones(Np)]) return modelling.GaussRV(x0, C0)
import dapper.mods as modelling import dapper.tools.randvars as RVs from dapper.mods.Lorenz96 import step from dapper.tools.localization import nd_Id_localization t = modelling.Chronology(0.05, dko=2, T=4**5, BurnIn=20) Nx = 80 Dyn = { 'M': Nx, 'model': step, 'noise': 0, } X0 = modelling.GaussRV(M=Nx, C=0.001) jj = np.arange(0, Nx, 2) Obs = modelling.partial_Id_Obs(Nx, jj) Obs['localizer'] = nd_Id_localization((Nx,), (1,), jj) # Obs['noise'] = RVs.LaplaceRV(C=1,M=len(jj)) Obs['noise'] = RVs.LaplaceParallelRV(C=1, M=len(jj)) HMM = modelling.HiddenMarkovModel(Dyn, Obs, t, X0) #################### # Suggested tuning #################### # rmse.a # xps += LETKF(N=20,rot=True,infl=1.04 ,loc_rad=5) # 0.44
try: # Load pre-generated L = np.load(sample_filename)['Left'] except FileNotFoundError: # First-time use print('Did not find sample file', sample_filename, 'for experiment initialization. Generating...') NQ = 20000 # Must have NQ > (2*wnumQ+1) A = sinusoidal_sample(Nx, wnumQ, NQ) A = 1 / 10 * (A - A.mean(0)) / np.sqrt(NQ) Q = A.T @ A U, s, _ = tsvd(Q) L = U * np.sqrt(s) np.savez(sample_filename, Left=L) X0 = modelling.GaussRV(C=modelling.CovMat(np.sqrt(5) * L, 'Left')) ################### # Forward model # ################### damp = 0.98 Fm = Fmat(Nx, -1, 1, tseq.dt) def step(x, t, dt): assert dt == tseq.dt return x @ Fm.T Dyn = { 'M': Nx,
################ # Full ################ # tseq = modelling.Chronology(dt=0.001,dto=0.05,T=4**3,BurnIn=6) # allows using rk2 tseq = modelling.Chronology(dt=0.005, dto=0.05, T=4**3, BurnIn=6) # requires rk4 Dyn = { 'M': LUV.M, 'model': modelling.with_rk4(LUV.dxdt, autonom=True), 'noise': 0, 'linear': LUV.dstep_dx, } X0 = modelling.GaussRV(mu=LUV.x0, C=0.01) R = 0.1 jj = np.arange(nU) Obs = modelling.partial_Id_Obs(LUV.M, jj) Obs['noise'] = R other = {'name': rel2mods(__file__) + '_full'} HMM_full = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0, LP=LUV.LPs(jj), **other) ################
Nx = len(x0) Ny = Nx day = 0.05 / 6 * 24 # coz dt=0.05 <--> 6h in "model time scale" t = modelling.Chronology(0.05, dkObs=1, T=200 * day, BurnIn=10 * day) Dyn = { 'M': Nx, 'model': step, 'linear': dstep_dx, 'noise': 0, } # X0 = modelling.GaussRV(C=0.01,M=Nx) # Decreased from Pajonk's C=1. X0 = modelling.GaussRV(C=0.01, mu=x0) jj = np.arange(Nx) Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = 0.1 HMM = modelling.HiddenMarkovModel(Dyn, Obs, t, X0, LP=LPs(jj)) #################### # Suggested tuning #################### # xps += ExtKF(infl=2) # xps += EnKF('Sqrt',N=3,infl=1.01) # xps += PartFilt(reg=1.0, N=100, NER=0.4) # add reg! # xps += PartFilt(reg=1.0, N=1000, NER=0.1) # add reg!
def hmod(E, t): return E[obs_inds(t)] # Localization. batch_shape = [2, 2] # width (in grid points) of each state batch. # Increasing the width # => quicker analysis (but less rel. speed-up by parallelzt., depending on NPROC) # => worse (increased) rmse (but width 4 is only slightly worse than 1); # if inflation is applied locally, then rmse might actually improve. localizer = nd_Id_localization(shape[::-1], batch_shape[::-1], obs_inds, periodic=False) Obs = { 'M': Ny, 'model': hmod, 'noise': modelling.GaussRV(C=4*np.eye(Ny)), 'localizer': localizer, } # Moving localization mask for smoothers: Obs['loc_shift'] = lambda ii, dt: ii # no movement (suboptimal, but easy) # Jacobian left unspecified coz it's (usually) employed by methods that # compute full cov, which in this case is too big. ############################ # Other ############################ HMM = modelling.HiddenMarkovModel(Dyn, Obs, t, X0, LP=LP_setup(obs_inds))
import dapper.mods as modelling from dapper.mods.LotkaVolterra import step, dstep_dx, x0, LP_setup, Tplot t = modelling.Chronology(0.5, dto=10, T=1000, BurnIn=Tplot, Tplot=Tplot) Nx = len(x0) Dyn = { 'M': Nx, 'model': step, 'linear': dstep_dx, 'noise': 0, } X0 = modelling.GaussRV(mu=x0, C=0.01**2) jj = [1, 3] Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = 0.04**2 HMM = modelling.HiddenMarkovModel(Dyn, Obs, t, X0, LP=LP_setup(jj)) #################### # Suggested tuning #################### # Not carefully tuned: # xps += EnKF_N(N=6) # xps += ExtKF(infl=1.02)
Ny much lower than for the case H = Identity, even though the rmse is a lot lower with spectral H. Am I missing something? """ import numpy as np import dapper.mods as modelling from dapper.mods.Lorenz96.sakov2008 import Dyn, Nx, tseq # The (Nx-Ny) highest frequency observation modes are left out of H below. # If Ny>Nx, then H no longer has independent (let alone orthogonal) columns, # yet more information is gained, since the observations are noisy. Ny = 12 X0 = modelling.GaussRV(M=Nx, C=0.001) def make_H(Ny, Nx): xx = np.linspace(-1, 1, Nx + 1)[1:] H = np.zeros((Ny, Nx)) H[0] = 1 / np.sqrt(2) for k in range(-(Ny // 2), (Ny + 1) // 2): ind = 2 * abs(k) - (k < 0) H[ind] = np.sin(np.pi * k * xx + np.pi / 4) H /= np.sqrt(Nx / 2) return H H = make_H(Ny, Nx) # plt.figure(1).gca().matshow(H)
from dapper.tools.localization import nd_Id_localization # Sakov uses K=300000, BurnIn=1000*0.05 tseq = modelling.Chronology(0.05, dko=1, Ko=1000, Tplot=Tplot, BurnIn=2*Tplot) Nx = 40 x0 = x0(Nx) Dyn = { 'M': Nx, 'model': step, 'linear': dstep_dx, 'noise': 0, } X0 = modelling.GaussRV(mu=x0, C=0.001) jj = np.arange(Nx) # obs_inds Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = 1 Obs['localizer'] = nd_Id_localization((Nx,), (2,)) HMM = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0) HMM.liveplotters = LPs(jj) #################### # Suggested tuning ####################
"""From `dapper.mods.Lorenz96.todter2015` again, but with Gaussian likelihood.""" import dapper.mods as modelling from dapper.mods.Lorenz96.todter2015 import HMM as _HMM HMM = _HMM.copy() HMM.Obs.noise = modelling.GaussRV(C=HMM.Obs.noise.C) #################### # Suggested tuning #################### # rmse.a # xps += LETKF(N=40,rot=True,infl=1.04 ,loc_rad=5) # 0.42 # xps += LETKF(N=80,rot=True,infl=1.04 ,loc_rad=5) # 0.42 # xps += LNETF(N=40,rot=True,infl=1.10,Rs=1.9,loc_rad=5) # 0.54 # xps += LNETF(N=80,rot=True,infl=1.06,Rs=1.4,loc_rad=5) # 0.47
"""Settings that produce somewhat interesting/challenging DA problems.""" import numpy as np import dapper.mods as modelling from dapper.mods.Ikeda import step, x0, Tplot, LPs tseq = modelling.Chronology(1, dko=1, Ko=1000, Tplot=Tplot, BurnIn=4 * Tplot) Nx = len(x0) Dyn = { 'M': Nx, 'model': step, 'noise': 0, } X0 = modelling.GaussRV(C=.1, mu=x0) jj = np.arange(Nx) # obs_inds Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = .1 # modelling.GaussRV(C=CovMat(1*eye(Nx))) HMM = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0) HMM.liveplotters = LPs(jj) #################### # Suggested tuning ####################
t = modelling.Chronology(0.01, dkObs=12, KObs=1000, Tplot=Tplot, BurnIn=4 * Tplot) Nx = len(x0) Dyn = { 'M': Nx, 'model': step, 'linear': dstep_dx, 'noise': 0, } X0 = modelling.GaussRV(C=2, mu=x0) Obs = modelling.partial_Id_Obs(Nx, np.arange(Nx)) Obs['noise'] = 8.0 HMM = modelling.HiddenMarkovModel(Dyn, Obs, t, X0) #################### # Suggested tuning #################### # Compare with Anderson's figure 10. # Benchmarks are fairly reliable (KObs=2000): # from dapper.mods.Lorenz63.anderson2010rhf import HMM # rmse.a # xps += SL_EAKF(N=20,infl=1.01,rot=True,loc_rad=np.inf) # 0.87 # xps += EnKF_N (N=20,rot=True) # 0.87 # xps += RHF (N=50,infl=1.10) # 1.28
model.Force = 8.17 tseq = modelling.Chronology(0.01, dko=10, K=4000, Tplot=10, BurnIn=10) Nx = 1000 Dyn = { 'M': Nx, 'model': step, # It's not clear from the paper whether Q=0.5 or 0.25. # But I'm pretty sure it's applied each dto (not dt). 'noise': 0.25 / tseq.dto, # 'noise': 0.5 / t.dto, } X0 = modelling.GaussRV(mu=x0(Nx), C=0.001) jj = linspace_int(Nx, Nx // 4, periodic=True) Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = 0.1**2 Obs['localizer'] = nd_Id_localization((Nx, ), (1, ), jj, periodic=True) HMM = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0) HMM.liveplotters = LPs(jj) # Pinheiro et al. use # - N = 30, but this is only really stated for their PF. # - loc-rad = 4, but don't state its definition, nor the exact tapering function. # - infl-factor 1.05, but I'm not sure if it's squared or not. #
Fm = Fmat(Nx, -1, 1, tseq.dt) def step(x, t, dt): assert dt == tseq.dt return x @ Fm.T Dyn = { 'M': Nx, 'model': step, 'linear': lambda x, t, dt: Fm, 'noise': 0, } X0 = modelling.GaussRV(mu=np.zeros(Nx), C=homogeneous_1D_cov(Nx, Nx / 8, kind='Gauss')) Ny = 4 jj = modelling.linspace_int(Nx, Ny) Obs = modelling.partial_Id_Obs(Nx, jj) Obs['noise'] = 0.01 HMM = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0, LP=LPs(jj)) #################### # Suggested tuning #################### # xps += EnKF('PertObs',N=16 ,infl=1.02) # xps += EnKF('Sqrt' ,N=16 ,infl=1.0)
"""The identity model (that does nothing, i.e. sets `output = input`). This means that the state dynamics are just Brownian motion. Next to setting the state to a constant, this is the simplest model you can think of. """ import dapper.mods as modelling tseq = modelling.Chronology(1, dko=1, Ko=2000, Tplot=10, BurnIn=0) M = 4 Obs = {'noise': 2, 'M': M} Dyn = {'noise': 1, 'M': M} X0 = modelling.GaussRV(C=1, M=M) HMM = modelling.HiddenMarkovModel(Dyn, Obs, tseq, X0) ######################### # Benchmarking script # ######################### # import dapper as dpr # dpr.rc.field_summaries.append("ms") # # We do not include Climatology and OptInterp because their variance and accuracy # # are less interesting since they grow with the duration of the experiment. # import dapper.da_methods as da # xps = dpr.xpList() # xps += da.Var3D("eye", xB=2) # xps += da.ExtKF() # xps += da.EnKF('Sqrt', N=100) # save_as = xps.launch(HMM, save_as=False)