用Lorenz1963模式展示的数据同化实验¶
海洋学院,沈浙奇,2024年6月
关于实验和模型的一些说明¶
我认为介绍一个概念最好的办法是提供一个可操作的实验,让读者可以重复实验过程,并从中看出现象和效果。鉴于我们介绍的数据同化,是一种将观测数据和数值模式有机结合,通过数学方法产生最优的模式状态估计的技术。我们就只能通过开展数值实验来展示其效果。所谓的数值实验,实质上是一种使用数学模型和计算机程序来模拟现实世界中的物理现象或过程的实验方法。它允许科学家和工程师在没有实际进行物理实验的情况下,通过模拟来预测和分析实验结果。
数值实验依赖于数值模式,而所谓的数值模式(在本学科中,它是“数值预报模式”的简称)是一种基于数学模型和计算机算法来预测天气、气候、海洋流动等自然现象的系统。这种模式通过将地球大气或海洋的物理过程转化为数学方程组,然后使用数值方法求解这些方程组来预测未来的环境状态。
我们采用的数值实验方法被称为孪生实验。孪生试验是一种经典的数值试验框架。因为在现实情况下,真实的状态值往往是未知的,而且有噪声的观测值是由观测设备收集的,代价较高。为了测试算法,需要事先知道真实的状态,以便评估所开发算法的收敛性和准确性。在孪生试验中,首先根据动力模式和实际情况的相似性,选择一个模式作为测试案例。通过固定所有参数和运行模式积分,计算出一个参考的真值轨迹,直到达到某个最终时间。然后通过在空间和时间的某些点上对真值进行取样来合成“观测值”。可以在离散时间直接或者间接地对部分模式变量进行观测,并人为地添加任意的随机噪声(如高斯白噪声)。使用相应的数据同化技术,从有误差的状态变量初值或不准确的模式参数,利用合成的观测数据开始同化试验。利用算法的输出数据与真值进行比较,可以评估其性能。孪生试验可以高效地评估不同的同化方法和/或观测噪声水平的影响,而不需要使用真正的观测手段。在这个意义上,孪生试验的概念在数据同化的研究中很受欢迎。
由美国数学家、气象学家洛伦茨(Edward Norton Lorenz,1917~2008年)提出的数值模式经常用于数值天气预报研究,是在孪生试验中被广泛使用的测试模式。Lorenz63模式(Lorenz,1963)是大气可预报性研究中的经典数值模式,其控制方程为
$$\frac{dx}{dt}=\sigma(y-x) \qquad (1)$$$$\frac{dy}{dt}=\rho x-y-xz \qquad (2)$$$$\frac{dz}{dt}=xy-\beta z \qquad (3)$$Lorenz63模型是对洛伦茨的关于大气对流的12变量模型的进一步简化,凭借这个模型,他发现了确定性的非周期流,后人将其称为一种“混沌”现象。在这个简化的模型中,他定义了新的三个变量,将大气对流运动中非周期的部分进行了保留。在方程中,x与对流运动的强度成正比,y与上升流和下降流之间的温度差成正比,z与垂直温度曲线与线性的差异成正比。其中$\sigma$, $\rho$ 和 $\beta$是参数,分别为Prandtl数,Rayleigh数和一个与求解区域相关的参数。洛伦茨的工作说明,当Rayleigh较大时,整个系统呈现较强的非线性,从而导致复杂的混沌现象。所以在实验中,参数的数值一般设置为10,28,8/3。
关于编程环境配置¶
我们使用python中的numpy包来开展数值实验。
Python对于大众来说比较熟悉,它是一种高级、解释型、通用的编程语言,以其清晰的语法和代码可读性而闻名。它的优势包括易于学习、跨平台、丰富的库支持和强大的社区。
NumPy是Python的一个基础科学计算库,全称为Numerical Python。它提供高性能的多维数组对象ndarray和矩阵运算,广泛应用于数值计算、数据分析、机器学习、图像处理等领域。NumPy的优势在于其简洁的API、高效的内存使用和广泛的社区支持,是进行大规模数据处理和科学计算的首选工具。
我相信大部分读者已经具备运行python代码的基础。如果没有的话,我提供一个最简单的配置环境方案如下:
- 下载anaconda: https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Windows-x86_64.exe%EF%BC%8C 运行安装器装上整个anaconda
- 打开Anaconda,就像打开一个应用市场,目前里面有用的几个是“Spyder”,“Jupyter-notebook”
- numpy和matplotlib两个扩展包一般都是默认安装的,但是在运行python编程的时候总会有一些扩展包需要主动安装,这里以此为例介绍如何用conda install命令安装扩展包: 如果是windows环境可以使用Anaconda Prompt打开命令界面(这可以通过在Windows搜索栏中输入"Anaconda Prompt"并选择它来完成)。在命令行分别运行命令: conda install numpy 和 conda install matplotlib (以后使用conda install *** 可以按照python需要的各种包)
- 使用jupyter-notebook可以打开当前文档并运行(我当前这个文件就是利用jupyter-notebook产生的)。当然,如果是初学者或者不适应notebook,最简单的办法是打开一个叫做Spyder的程序,新建一个.py后缀的文件,将我的代码拷到这个文件中,然后点击上方的三角形运行代码。
关于数值模式¶
关于数值模式还是想说几句最基础的话,这是一般大二以上的理工科本科生都该了解的,但我发现还是有很多人不知道。
方程(1)-(3)是常微分方程组,刻画了$x,y,z$三个应变量关于时间$t$的演变规律。这个方程的定解需要提供初始条件。如果解出这个方程,写出三个变量关于t的表达式,意味可以根据初始时刻$t_0$的状态值$(x_0,y_0,z_0)$预报出后面任意时刻的状态$(x,y,z)$,“预报”这个词的含义也正体现在这里。上面追求的这种解被称为解析解。实践中,由于很多常微分方程(特别是非线性方程)没有解析解或者解析解难以获得,需要寻求“数值解”。数值解提供的不是$(x,y,z)$关于$t$的表达式,而是在步长($h$)非常小的时间网格上,每一步$t_k$对应的$(x_k,y_k,z_k)$,其中的$k$是整数。这意味着虽然我们不能得到表达式,但是我们还是可以频率非常高得给出每个时刻上的“近似”解。现实中的数值预报也是这么做的,只不过大多数情况下需要先把偏微分方程离散为关于时间的常微分方程,然后求解常微分方程。
所以,数值解法的主要步骤包括:
- 离散化:将连续的微分方程转化为离散的差分方程。这通常通过将时间或空间划分为小的间隔来实现。
- 数值方法:选择合适的数值方法来求解差分方程。常见的方法包括欧拉法、改进的欧拉法、龙格-库塔法等。
- 迭代求解:从初始条件开始,逐步迭代计算每个时间步或空间点的解。
数值模式往简单了说,就是这么一个特定方程的求解器。只不过相较于洛伦茨最初那12个方程或者3个方程的系统,现代的全球天气模型处理的是包含500,000个方程的系统。
Lorenz63模式的数值解¶
以下的代码就提供了一个Lorenz63模式方程的求解器。
import numpy as np
# 定义模式方程和积分格式
def Lorenz63(state,*args): #Lorenz63模式
sigma = args[0]
beta = args[1]
rho = args[2]
x, y, z = state
f = np.zeros(3)
f[0] = sigma * (y - x)
f[1] = x * (rho - z) - y
f[2] = x * y - beta * z
return f
def RK4(rhs,state,dt,*args): # Runge-Kutta格式,输入的rhs代表模式右端方程
k1 = rhs(state,*args)
k2 = rhs(state+k1*dt/2,*args)
k3 = rhs(state+k2*dt/2,*args)
k4 = rhs(state+k3*dt,*args) # 输出新的一步的状态
new_state = state + (dt/6)*(k1+2*k2+2*k3+k4)
return new_state
运行这段之后看不到任何结果,因为这里只是用def这个功能定义了两个后面可以调用的函数。
- 一个被命名为Lorenz63的函数显然只是输出方程(1)-(3)的右端项,它的输入为$(x,y,z)$构成的矢量和三个参数值。
- 下一个RK4的函数是大名鼎鼎的龙格-库塔四阶格式,它是用来进行数值积分的。输入当前步$t_k$对应的$(x_k,y_k,z_k)$和步长$dt$就可以得到下一步$t_{k+1}=t_k+dt$对应的$(x_{k+1},y_{k+1},z_{k+1})$。这个非常简单,任何一本《计算方法》的教材都会教给你。
然后,给定一些参数后,我们看看模式运行的效果。
sigma = 10.0; beta = 8.0/3.0; rho = 28.0 # 参数值
dt = 0.01 # 模式步长
x0 = np.array([1.508870, -1.531271, 25.46091]) # 我们也用洛伦茨开展他的实验用的初值,这里的矢量x0代表了(x,y,z)的三个应变量,写成矢量主要为方便编程
Xt = np.zeros([3000,3]);Xt[0]=x0 # 我们积分3000步看看结果,这里涉及python的数组保存方式,第一维代表时间步数,第二维代表x,y,z的3个变量
# 二维数值Xt使用Xt[0]的方式调用时,相当于Xt[0,:],意味着第一维取0,第二维用全体,所以时间维放前面代码简单
for k in range(2999): # 从0积分到2999
Xt[k+1] = RK4(Lorenz63,Xt[k],dt,sigma,beta,rho)
#%% 画图
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(Xt[:,0], Xt[:,1], Xt[:,2],'r', label='Lorenz 63 model')
ax.legend()
plt.xlabel('x');plt.ylabel('y');
ax.set_zlabel('z')
Text(0.5, 0, 'z')
Text(0.5, 0, 'z')
画图之前的代码,只是提供了参数和初值,然后一步一步迭代获取数值解。
图中展示的是3个变量在相空间中的变化。相空间是一个在物理学和数学中使用的概念,特别是在经典力学和统计力学中。它是一个多维空间,其中每一个维度代表一个物理系统的状态变量。在经典力学中,相空间通常用来描述一个系统的状态,这些状态变量包括位置和动量。在统计力学中,相空间用于描述一个系统的微观状态。在这种情况下,相空间中的每个点代表一个可能的微观状态,而系统的宏观性质可以通过在相空间中对这些点进行加权求和来计算。相空间的概念在理解物理系统的动力学和热力学行为中非常重要,它提供了一个框架来分析系统的演化和平衡状态。
图展示的是 Lorenz63 模式自由积分的结果在相空间中图像,它显示出洛伦茨吸引子的形状—— 由“浑然一体”的左右两簇构成,各自围绕一个不动点。当运动轨道在一个簇中 由外向内绕到中心附近后,就随机地跳到另一个簇的外缘继续向内绕,然后在达到中心附近后再突然跳回到原来的那一个簇的外缘,如此构成随机性地来回盘旋。 这一图案颇似蝴蝶展翅,混沌理论的“蝴蝶效应”也与此吸引子的形状有关。
解对于初值的依赖性¶
下面的代码用于说明该模式对于初值的敏感性,即洛伦茨的发现。参考洛伦茨故事中的一个情节:
在1961年冬的一天,为了仔细检视一大段他感兴趣的序列,洛伦茨抄了一次近道。他没有重新开始整个运行,而是从中间切入,将之前输出的数输入计算机,作为后续运行的初始条件。然后他来到走廊以躲开噪声,并喝上一杯咖啡。当他在一个小时后回到办公室时,他看到了某种意料之外的东西,某种将种下一门新科学的种子的东西。这次新的运行原本应该与旧的一模一样。数是洛伦茨自己输入的。程序也没有变动。但当他检视新的输出时,洛伦茨发现,他的天气如此之快地偏离了上一次运行的模式,以至于在短短几个月里,原有的相似性完全消失不见。他突然明白了过来。机器并没有出故障。问题在于他当初输入的数。在计算机的内存中,数值以六位小数的形式存储。而在输出中,为了节省空间,计算机只显示三位小数:0.506。洛伦茨当初输入的是四舍五入后更短的数,他以为这个千分之一的差异无关紧要。
所以,我们也使用保留三位小数的初值看看输出的结果。
# 模式积分
x0p = np.array([1.508, -1.531, 25.460]) # 这一次,我们只使用3位小数
Xp = np.zeros([3000,3]);Xp[0]=x0p
for k in range(2999): # 从0积分到2999
Xp[k+1] = RK4(Lorenz63,Xp[k],dt,sigma,beta,rho)
# 这一次,我们使用完全相同的流程积分模式,只不过初值忽略了小于千分之一的量,即初始误差小于千分之一。
#%% 画图
plt.figure(figsize=(8,4))
Ylabels = ['x','y','z']
for j in range(3):
plt.subplot(3,1,j+1)
plt.plot(dt*np.arange(0,3000), Xt[:,j],'k:', label='original')
plt.plot(dt*np.arange(0,3000), Xp[:,j],'b',label='restart')
plt.xlim(0,30);plt.ylabel(Ylabels[j],fontsize=14)
plt.grid(axis='x')
if j<2:
plt.xticks(dt*np.arange(0,3001,300),[])
if j==2:
plt.legend(ncol=2);plt.xlabel('Time Units')
plt.xticks(dt*np.arange(0,3001,300))
这一次,我们分开看$x,y,z$三个变量随时间的变化,并且把第一次积分的结果和第二次重新积分的结果进行比较。时间步长是0.01,所以分歧大概出现在1300步左右,结果出现了很大的不同,导致后面的结果完全称不上是一个有用的预报。这说明,在洛伦茨的这个方程组中,小的误差被证明会引发灾难性后果。
这是一个全新的发现,因为长期以来,人们在科学研究中总是试图去忽略那些非常小的误差的影响,并假设它们的结果也是微不足道的。人们认为: 给定对于一个系统的初始条件的一个近似知识,以及对于自然定律的一个理解,我们就能够计算出这个系统的近似行为。这个假设存在于科学的哲学核心。正如一位理论研究者喜欢告诉他的学生:“西方科学的基本思想是,当你尝试解释地球上一张台球桌上的一颗台球的运动时,你不需要将另一个星系里某颗行星上一片落叶的影响考虑进来。非常微小的影响可以忽略不计。事物运行的方式中存在一种收敛性,任意小的影响不会扩大成为任意大的效应。”在经典科学中,对于近似和收敛的信念是合理的,因为它确实有效。
而混沌揭示的是,哪怕非常小的初始误差,也会导致一些基于非线性动力系统的预报完全不可靠。这似乎从根上否定了长期天气预报的可能性。
但是这种混沌特性不是一开始就有的。事实上,只有当Rayleigh数较大的时候,整个系统才呈现一种混沌特性,如果采用较小的$\rho$,情况可能有所不同,读者可以自行尝试一下。
同化实验¶
既然没有办法做长期的预报,那么就可以通过不断地把更精准的实际观测融合进来,提供精度更高的初始值,然后重复进行短期预报。也就是说,不能指望提供一次初值就一劳永逸地提供完美的预报;而是要不断修正状态,作为下一阶段的初值,开展相对可靠的短期预报。为说明这点,我们开展孪生实验。
为了开展孪生试验或者OSSE,需要首先进行一段时间的积分,并将积分的结果设定为“真实值”,这个积分称为叫做真值试验。同时也需要造一系列观测用于同化试验。一个基本的设定是:当我们开展同化试验时,采取的设置必须不同于真值试验以引入模式误差,从而模拟现实中的预报模式。例如,我们可以使用不同的模式初值和/或模式参数。 人造观测从真值试验的积分结果中选取,但是为了模拟现实,需要考虑以下两点:第一,观测在时空上是残缺的。也就是说,我们假设间隔一定的时间(比如每积分20步)才能观测一次,每次观测可能只观测到部分变量。因此,需要定义观测算子h 将模式变量投影到观测变量上。第二,观测存在误差。我们假设观测误差的协方差矩阵为R,均值为0,因此在真值取样后往往会叠加上服从多元高斯分布N(0,R)的随机观测误差。
#%% Lorenz63模式真值试验和观测构造
sigma = 10.0; beta = 8.0/3.0; rho = 28.0 # 模式参数值
dt = 0.01 # 模式积分步长
n = 3 # 模式的状态维数,即有多少个状态变量
m = 3 # 模式的观测维数,即多少个变量可以被观测到,这里先假定所有变量都能被观测
tm = 10 # 同化试验时间窗口
nt = int(tm/dt) # 总积分步数
t = np.linspace(0,tm,nt+1) # 模式时间网格
x0True = np.array([1.508870, -1.531271, 25.46091])# 真实值的初值
np.random.seed(seed=1) # 设置随机种子,由于电脑的随机数是伪随机,记录了随机种子之后,每次运行这个脚本产生的“随机”的观测误差都是一样的。
sig_m= 0.50 # 观测误差标准差
R = sig_m**2*np.eye(n) # 观测误差协方差矩阵,设为对角阵使得不同变量的误差互不相干
dt_m = 0.2 # 观测之间的时间间隔(即每20模式步观测一次)
tm_m = 10 # 最大观测时间(即多少时间之后停止同化,可小于同化试验时间窗口)
nt_m = int(tm_m/dt_m) # 进行同化的总次数
ind_m = (np.linspace(int(dt_m/dt),int(tm_m/dt),nt_m)).astype(int) # 这是有观测的时刻在总时间网格中的位置指标
t_m = t[ind_m] # 观测网格
def h(x): # 定义观测算子:观测算子用于构建模式变量和观测之间的关系。当所有变量被观测时,使用单位阵。
H = np.eye(n) # 观测矩阵为单位阵。
yo = H@x # 单位阵乘以状态变量,即所有变量直接被观测。
return yo
Xtrue = np.zeros([n,nt+1]) # 真值保存在xTrue变量中
Xtrue[:,0] = x0True # 初始化真值
km = 0 # 观测计数
yo = np.zeros([3,nt_m]) # 观测保存在yo变量中
for k in range(nt): # 按模式时间网格开展模式积分循环
Xtrue[:,k+1] = RK4(Lorenz63,Xtrue[:,k],dt,sigma,beta,rho) # 真实值积分
if (km<nt_m) and (k+1==ind_m[km]): # 用指标判断是否进行观测
yo[:,km] = h(Xtrue[:,k+1]) + np.random.normal(0,sig_m,[3,]) # 通过判断,在观测时间取出真值作为观测值,同时叠加高斯分布随机噪声
km = km+1 # 观测计数,用于循环控制
# 画图
## 以下提供真值和观测画图的参考脚本
import matplotlib.pyplot as plt
plt.figure(figsize=(10,6))
lbs = ['x','y','z']
for j in range(3):
plt.subplot(3,1,j+1)
plt.plot(t,Xtrue[j],'b-',lw=2,label='True')
plt.plot(t_m,yo[j],'go',ms=8,markerfacecolor='white',label='Observation')
plt.ylabel(lbs[j],fontsize=16)
plt.xticks(fontsize=16);plt.yticks(fontsize=16)
if j==0:
plt.legend(ncol=2, fontsize=12)
plt.ylim(-21,21)
plt.title('The True States and Observations',fontsize=16)
if j==2:
plt.xlabel('Time',fontsize=16)
我们可以看画图之前的五行代码,即for循环中的内容(注意到这里是根据模式的步长积分的):
- xTrue[:,k+1] = RK4(Lorenz63,xTrue[:,k],dt,sigma,beta,rho)代表模式积分,和前面展示的一样,模式积分就是这样实现的。
- 然后我们加了一个判断,判断是否需要进行观测
- 如果需要进行观测,我们就用h取样,并叠加噪声
- 这些观测被我们存下来,用于后面的同化实验
那么在同化实验中,我们从一个比较不准确的初值(如[1,-1,20])开始积分,结果必然和真值差异很大,我们将这一次积分的结果叫做背景积分(Background integration)。对应每一次有观测的时刻,我们能利用的是观测数据,通过某些程序将观测数据和背景积分到此处的结果相结合,得到新的改进的初值,然后继续向后积分。这个结合的算法就叫做同化,他基于背景积分的结果和此时的观测数据,输出新的状态场作为后面积分的初值。这个新的状态场叫做“分析”。
在代码的实现上,对应前面观测取样的部分,我们使用一个程序输入背景场和观测,输出分析场。先不管这个程序的原理是什么,我们来看一下效果。
def Lin3dvar(xb,w,H,R,B,opt):
# 三维变分的算法
if opt == 1: # 模式空间算法
Bi = np.linalg.inv(B)
Ri = np.linalg.inv(R)
A = Bi + (H.T)@Ri@H
b = Bi@xb + (H.T)@Ri@w
xa = np.linalg.solve(A,b) # 求解线性方程组
elif opt == 2: # 模式空间增量算法
Bi = np.linalg.inv(B)
Ri = np.linalg.inv(R)
A = Bi + (H.T)@Ri@H
b = (H.T)@Ri@(w-H@xb)
xa = xb + np.linalg.solve(A,b) # 求解线性方程组
elif opt == 3: # 观测空间增量方法
A = R + H@B@(H.T)
b = (w-H@xb)
xa = xb + B@(H.T)@np.linalg.solve(A,b) #solve a linear system
return xa
x0b = np.array([1,-1,20]) # 背景积分的初值
Xb = np.zeros([3,nt+1]); Xb[:,0] = x0b # 控制试验结果存在xb中
# --------------- 背景积分实验 ------------------------
for k in range(nt): # 模式积分循环
Xb[:,k+1] = RK4(Lorenz63,Xb[:,k],dt,sigma,beta,rho) # 不加同化的背景积分结果,后面和同化结果进行对比
# --------------- 数据同化实验 ------------------------
sig_b= 1 # 设定初始的背景误差
B = sig_b**2*np.eye(3) # 设定初始背景误差协方差矩阵,B矩阵的取值对于变分同化比较重要,这个简单模式可以使用简单的对角阵
Xa = np.zeros([3,nt+1]); Xa[:,0] = x0b # 同化试验结果存在Xa中,第一步没同化,所以数值也是x0b
km = 0 # 同化次数计数
H = np.eye(3) # 如前述,观测算子是单位阵
for k in range(nt): # 模式积分循环
Xa[:,k+1] = RK4(Lorenz63,Xa[:,k],dt,sigma,beta,rho) # 没有遇到观测的时候,就是正常积分模式
if (km<nt_m) and (k+1==ind_m[km]): # 当有观测时,使用3dvar同化
Xa[:,k+1] = Lin3dvar(Xa[:,k+1],yo[:,km],H,R,B,1) # 调用3dvar,更新状态和协方差
km = km+1
# 3DVar结果画图
plt.figure(figsize=(10,8))
lbs = ['x','y','z']
for j in range(3):
plt.subplot(4,1,j+1)
plt.plot(t,Xtrue[j],'b-',lw=2,label='True')
plt.plot(t,Xb[j],'--',color='orange',lw=2,label='Background')
plt.plot(t_m,yo[j],'go',ms=8,markerfacecolor='white',label='Observation')
plt.plot(t,Xa[j],'-.',color='red',lw=2,label='Analysis')
plt.ylabel(lbs[j],fontsize=16)
plt.xticks(fontsize=16);plt.yticks(fontsize=16)
if j==0:
plt.legend(ncol=2, loc='upper right',fontsize=12)
plt.title("3DVar",fontsize=16)
RMSEb = np.sqrt(np.mean((Xb-Xtrue)**2,0))
RMSEa = np.sqrt(np.mean((Xa-Xtrue)**2,0))
plt.subplot(4,1,4)
plt.plot(t,RMSEb,'--',color='orange',label='Background')
plt.plot(t,RMSEa,color='red',label='Analysis')
plt.text(0,20,'mean RMSEb = %0.2f' %np.mean(RMSEb))
plt.text(0,25,'mean RMSEa = %0.2f' %np.mean(RMSEa))
plt.ylabel('RMSE',fontsize=16)
plt.xlabel('time',fontsize=16)
plt.xticks(fontsize=16);plt.yticks(fontsize=16)
plt.legend(ncol=2, loc='upper right',fontsize=12)
<matplotlib.legend.Legend at 0x10c528ed0>
<matplotlib.legend.Legend at 0x10c528ed0>
这个图展示的效果就非常明显了。
- 没有观测约束,模式积分一段时间(大概是150步)之后,预报的误差就非常大了,几乎称不上有预报技巧。
- 红线对应的情况每20步同化一次。由于观测和真实状态比较接近,同化时候使用Lin3dvar程序可以结合背景积分得到的背景场和观测数据,给出一个比背景场更接近真值的分析场。将分析场作为初值进行短期预报,其准确性更高。然后到下一次观测,再重复这个步骤,能够避免误差的不受控增长。
从这里也可以看出,同化频率还是很重要的,如果观测的间隔很大的话,很可能到同化的时候,背景场的误差已经很大了。读者可以自行尝试一下不同的同化间隔。
这里的Lin3dvar是线性三维变分(linear 3D-Variational),我个人觉得线性这个词甚至可以删掉,因为3D-Var本身就是针对线性系统的方法。
具体三维变分是怎么来的,还有其他的同化方法吗?我觉得这些可以放到后面去讨论。
讨论¶
这个文件的重点说明以下问题:
- 为什么要同化?
- 同化解决什么问题?
- 同化的流程是什么?
对此,我的解答概括如下:
- 因为混沌现象,数值模式预报能力有限,需要不断构造精准的初值进行短期预报。
- 同化通过将背景积分和观测数据结合,得到更接近真实情况的分析场。
- 将模式积分到有观测的时刻,然后使用数学方法将两者组合,输出分析场,再作为初值放回模式积分。
通过代码,我大致介绍了:什么是数值模式,怎样进行数值积分,Lorenz63模式的数值积分结果体现的“混沌”概念(解对初值的敏感性),孪生实验怎么做,基于3D-Var的数据同化发挥了什么作用。