WRF进阶:如何增加/耦合新参数化方案至WRF中?WRF如何添加新输入变量
总结一下WRF修改物理参数化方案的流程。修改对应phys/文件夹下的代码,包括计算模块与驱动模块如果不涉及变量的改变,则直接编译即可,如果涉及变量的增添,在修改初始化部分代码与变量输入模块代码。修改Registry文件,增加变量描述、针对需要增加新方案的,参考博客修改Makefile文件,编译自己的新方案模块。分配模块空间,确保在namelist.input中可以选择。修改驱动文件,创建一个新Ca
本内容相关视频讲解:WRF物理参数化方案的修改
背景——模型耦合
什么是模型耦合(Couple)?
经过搜索,可以看到如下定义:
Coupling refers to communication and interchange of information between models.
直译而言,模式的耦合可以理解为模式间的信息交换与相互作用,那么,又怎样理解模式间的信息交换与作用?模式间的信息又指什么?
按笔者个人的经验,模式的“信息”可以理解为:模式的输入与输出,而模式间的耦合,可以理解为两个及以上的模型之间输入与输出变量间的相互连接。
比如说,WRF-Chem便是一个典型的在线耦合模式,中尺度天气预测模式WRF提供了气象边界场与气象条件,这些输出的气象条件作为一个输入,用来计算大气内各种化学物质的传输、沉降过程,同时,化学模型计算所输出的各类化学物质的源汇,又可以作为一个输入,去计算其当前气象条件下,气溶胶粒子对气象条件的影响,这种模式间不同输入与输出的连接与迭代,便是”耦合“。
而最简单的理解就是:一个模式输出的结果,作为另一个模式的输入,便可以称之为”耦合“。
这类定义随着学科的差异而有着不同的定义,在地学领域内,模型的耦合可分为在线(Online) 与**离线(Offline)**耦合。
离线耦合通常仅仅只是将一个模型的输出结果提供给另一个模型,而并不考虑二者间的反馈作用,比方说WRF/SLEUTH的耦合,将SLEUTH输出的土地利用类型变化输入至WRF中,以此来研究土地利用类型对气象天气的影响,不考虑气象条件对土地利用类型的影响。离线耦合的模式彼此间较为独立,运行也是分开运行。
在线耦合则是考虑了模式间的反馈作用,WRF-Chem便是在线耦合模式的代表,在线耦合模式模块间存在着内部间的相互迭代,考虑两个模块间的相互作用,在线耦合也被称作”fully coupled“
那么,当我们想实现一个在线的模式耦合,我们应当如何做?我们如何应用一个新的物理方案到WRF中呢?
关于增加一个新的物理参数化方案到WRF中的步骤,已有博客Implementing a new physics scheme进行了极好的概括,不过,由于年代较远且细节不足等原因,仅仅依赖此篇博客,或许仍然会存在一些问题,在此,我将以自己所做的工作为实例,讲述耦合新的方案到WRF中的流程。
技术路线——WRF结构
在正式开始修改源码前,我们首先应当对WRF各个模块与其间的关系有一个初步的了解。WRF模式,尤其是模拟真实大气的部分结构较为复杂,会考虑真实三维大气从地面到高空的全部过程。
总体而言,WRF代码的框架可以概括为以下部分:
- 用来读取WPS前处理文件、完成初始化与求解的dyn_em
- 对于网格与模拟域的处理:frame
- 读取变量、变量处理、插值:share
- 物理参数化方案: phys
由于WRF由多个模块组成,模块与模块间存在相互多次调用的情况,在修改WRF源码时,应当注意修改部分的对应。
物理参数方案
WRF中的物理参数化方案包括长短波辐射方案、积云方案、云微物理方案、边界层参数化方案、陆面方案等。
这些方案的代码全都在WRF/phys的文件夹下,通常,除了各个方案的代码以外,还包括物理常数模块、以及驱动物理参数化的模块,如,地面方案的surface_driver模块。
对物理源码进行修改时,除了变量的增添删改,还应当模块的使用与对应模块的修改,如,当你给CLM模块中的函数增加了一个变量,除了在函数内与模块内声明外,还应当在使用了CLM木块的surface_driver增加变量的声明。
初始化
输入WRF中的任何变量,都需要在module_initialize_real.F中进行初始化,即,在wrfinput文件中生成,以及 first_rk_step模块根据选择的参数化方案分配处理变量。
输入变量
在模式初始化时,如果需要增加新的变量并进行初始化的话,需要在相应的模块optional_input
进行增加,否则不会对新增变量进行处理。
变量描述——Registry
WRF会根据你的namelist设置以及Registry文件中对于变量以及pacakge的描述与定义,自动生成一段源代码,该代码会控制wrfout结果的输入与输出。
在增加变量描述时,一定要确保变量的描述特征与所需求一致,比如维度以及变量的驶入输出以及插值覆盖,这些属性都会影响运行时WRF对新增变量的处理。
关于Registry的具体运算,建议参考:How to use WRF Registry
并行运算
WRF在运行时,有时需要读取外部数据,比较典型的代表便是运行陆面方案。
由于WRF运行往往需要进行并行运算,因此,在读取外部数据后,还需要将其传递给其他进程以便并行运算。
通常,WRF中读取外部数据的会使用命令
IF ( wrf_dm_on_monitor() ) THEN
监测WRF是否在进程中,如果WRF在运行,则Open Read Close读取数据。
随后,使用,CALL wrf_dm_bcast_real
函数命令,将读取的数据传播至其他并行进程。
可参考:How to parallelize a part of reading an aerosol file in module_initialize_real.F in dyn_em directory
模块编译
WRF作为一个中尺度模式,其各个过程都存储与相应独立的模块(module)中。每个moudle相互独立,具有不同的功能,又彼此联系耦合,当一个module想使用另一个module时,则需要在module开头 USE moudle module name,比如说,在CLM模块中,经常使用:
use shr_kind_mod, only: r8 => shr_kind_r8
use module_cam_support, only: endrun
使用shr_kind模块中的r8提高变量的精度(双浮点),使用cam_support模块用来DEBUG,并停止模式运行。
流程总结
总结一下WRF修改物理参数化方案的流程。
针对只修改原方案,不增加方案的:
- 修改对应phys/文件夹下的代码,包括计算模块与驱动模块
- 如果不涉及变量的改变,则直接编译即可,如果涉及变量的增添,在修改初始化部分代码与变量输入模块代码。
- 修改Registry文件,增加变量描述、
针对需要增加新方案的,参考博客Implementing a new physics scheme,相比起简单修改方案,需要多出以下步骤:
- 修改Makefile文件,编译自己的新方案模块。
- 分配模块空间,确保在namelist.input中可以选择。
- 修改驱动文件,创建一个新Case,同时在Regeistry中增加pacakge。
如果涉及到变量的增加,则需同样的步骤。
上述修改完并编译成功后,可能会出现变量未正确输入或运算的问题,建议在代码中加上write函数输出确认变量的输入输出过程。
此外,可能存在即使模式正确耦合运行,却运行速度降低的问题,一般是由于并行部分处理有问题,需要检查并行部分的处理。
实例
基本信息
对于不同的研究方向与问题而言,对于WRF的修改必然都会有所不同,但是,有些东西是共通的,不同的只有其中的物理过程差异,而WRF架构的变量输入、输出、并行、模块这一部分则是每一个对于WRF 的modification需要了解的。
我本次修改想实现的目的是:
- 将CLM 陆面模式中的SNICAR积雪反照率方案耦合至Noah-mp模式中。
- 增加一个输入变量,MBC,表示雪中BC浓度,此变量可通过MET_EM文件输入至wrfinput中。
- 增加的变量MBC将参与新耦合的雪反照率方案中。
根据我的需求,我对原本的SNICAR方案进行了简化:
- 不考虑雪变质、老化等过程,忽略雪粒有效半径的计算,将雪有效半径固定。
- 不考虑BC 的粒径与coated作用,仅考虑浓度。
- 将晴天与非晴天单独分开处理,即将散射与直射分开,只考虑晴天于非晴天两种情况。
下面开始讲我的实际操作:
函数添加
这一部分主要在于不同变量间的声明,注意,耦合的关键在于新函数计算所需的变量可以由Noah-MP提供,在不同函数间,声明的变量具有不同的名称与相同的物理含义,需要辨别。
创建一个新的函数,反照率函数:
SUBROUTINE SNOWALB_SNICAR_DIR (parameters,NBAND,NSNOW,ISNOW,COSZ,BCS,UALB,SNICE,SNLIQ,SNEQV,ALBSND,ALBSNI)
! --------------------------------------------------------------------------------------------------
IMPLICIT NONE
! --------------------------------------------------------------------------------------------------
! INput
type (noahmp_parameters), INTENT(IN) :: parameters
INTEGER,INTENT(IN) :: NBAND !number of waveband classes=numrad
INTEGER,INTENT(IN) :: ISNOW !actual no. of snow layers
INTEGER,INTENT(IN) :: NSNOW !maximum no. of snow layers
REAL,INTENT(IN) :: COSZ !cosINe solar zenith angle
REAL(r8),INTENT(IN) :: BCS !bc IN snow (ng/g)
REAL,INTENT(IN) :: UALB !Albedo of underlyINg surface
REAL, DIMENSION(-NSNOW+1: 0), INTENT(IN) :: SNICE !snow ice mass (kg/m2)
REAL, DIMENSION(-NSNOW+1: 0), INTENT(IN) :: SNLIQ !snow liq mass (kg/m2)
REAL, INTENT(IN) :: SNEQV !snow water eqv. [mm]
! REAL(r8), INTENT(IN) :: h2osno_liq(-NSNOW+1:0) ! liquid water content (col,lyr) [kg/m2]
! REAL(r8), INTENT(IN) :: h2osno_ice(-NSNOW+1:0) ! ice content (col,lyr) [kg/m2]
! INTEGER, INTENT(IN) :: snw_rds(-NSNOW+1:0) ! snow effective radius (col,lyr) [microns, m^-6]
! REAL(r8), INTENT(IN) :: mss_cnc_aer_in(-NSNOW+1:0) ! mass concentration of all aerosol species (col,lyr,aer) [kg/kg],此处只考虑一种
! REAL(r8), INTENT(IN) :: albsfc(numrad) ! albedo of surface underlyINg snow (col,bnd) [frc]
! OUTput
REAL, DIMENSION(1:2),INTENT(OUT) :: ALBSND !snow albedo for direct(1=vis, 2=nir)
REAL, DIMENSION(1:2),INTENT(OUT) :: ALBSNI !snow albedo for diffuse
! ---------------------------------------------------------------------------------------------
! ------------------------ local variables ----------------------------------------------------
!-----------------------------------------------------------------------
!
! variables for snow radiative transfer calculations
! Local variables representINg sINgle-column values of arrays:
INTEGER :: snl_lcl ! negative number of snow layers [nbr]
INTEGER :: snw_rds_lcl(-NSNOW+1:0) ! snow effective radius [m^-6]
REAL(r8):: flx_slrd_lcl(1:numrad_snw) ! direct beam INcident irradiance [W/m2] (set to 1)
REAL(r8):: flx_slri_lcl(1:numrad_snw) ! diffuse INcident irradiance [W/m2] (set to 1)
REAL(r8):: mss_cnc_aer_lcl(-NSNOW+1:0) ! aerosol mass concentration (lyr,aer_nbr) [kg/kg]
REAL(r8):: h2osno_lcl ! total column snow mass [kg/m2]
REAL(r8):: h2osno_liq_lcl(-NSNOW+1:0) ! liquid water mass [kg/m2]
REAL(r8):: h2osno_ice_lcl(-NSNOW+1:0) ! ice mass [kg/m2]
REAL(r8):: albsfc_lcl(1:numrad_snw) ! albedo of underlyINg surface [frc]
REAL(r8):: ss_alb_snw_lcl(-NSNOW+1:0) ! sINgle-scatter albedo of ice graINs (lyr) [frc]
REAL(r8):: asm_prm_snw_lcl(-NSNOW+1:0) ! asymmetry parameter of ice graINs (lyr) [frc]
REAL(r8):: ext_cff_mss_snw_lcl(-NSNOW+1:0) ! mass extINction coefficient of ice graINs (lyr) [m2/kg]
REAL(r8):: ss_alb_aer_lcl ! sINgle-scatter albedo of aerosol species (aer_nbr) [frc]
REAL(r8):: asm_prm_aer_lcl ! asymmetry parameter of aerosol species (aer_nbr) [frc]
REAL(r8):: ext_cff_mss_aer_lcl ! mass extINction coefficient of aerosol species (aer_nbr) [m2/kg]
REAL(r8) :: albout(1:NBAND) ! snow albedo, averaged INto 2 bands (=0 if no sun or no snow) (col,bnd) [frc]
REAL(r8) :: flx_abs(-NSNOW+1:1,1:NBAND) ! absorbed flux IN each layer per unit flux INcident on top of snowpack (col,lyr,bnd) [frc]
! Other local variables
INTEGER :: APRX_TYP ! two-stream approximation type (1=EddINgton, 2=Quadrature, 3=Hemispheric Mean) [nbr]
INTEGER :: DELTA ! flag to use Delta approximation (Joseph, 1976) (1= use, 0= don't use)
REAL(r8):: flx_wgt(1:numrad_snw) ! weights applied to spectral bands, specific to direct and diffuse cases (bnd) [frc]
INTEGER :: flg_nosnl ! flag: =1 if there is snow, but zero snow layers, =0 if at least 1 snow layer [flg]
INTEGER :: trip ! flag: =1 to redo RT calculation if result is unREAListic
INTEGER :: flg_dover ! defINes conditions for RT redo (explaINed below)
REAL(r8):: albedo_1 ! temporary snow albedo [frc]
REAL(r8):: flx_sum ! temporary summation variable for NIR weightINg
REAL(r8):: albout_lcl(1:numrad_snw) ! snow albedo by band [frc]
REAL(r8):: flx_abs_lcl(-NSNOW+1:1,1:numrad_snw)! absorbed flux per unit INcident flux at top of snowpack (lyr,bnd) [frc]
REAL(r8):: L_snw(-NSNOW+1:0) ! h2o mass (liquid+solid) IN snow layer (lyr) [kg/m2]
REAL(r8):: tau_snw(-NSNOW+1:0) ! snow optical depth (lyr) [unitless]
REAL(r8):: L_aer(-NSNOW+1:0) ! aerosol mass IN snow layer (lyr,nbr_aer) [kg/m2]
REAL(r8):: tau_aer(-NSNOW+1:0) ! aerosol optical depth (lyr,nbr_aer) [unitless]
REAL(r8):: tau_sum ! cumulative (snow+aerosol) optical depth [unitless]
REAL(r8):: tau_clm(-NSNOW+1:0) ! column optical depth from layer bottom to snowpack top (lyr) [unitless]
REAL(r8):: omega_sum ! temporary summation of sINgle-scatter albedo of all aerosols [frc]
REAL(r8):: g_sum ! temporary summation of asymmetry parameter of all aerosols [frc]
REAL(r8):: tau(-NSNOW+1:0) ! weighted optical depth of snow+aerosol layer (lyr) [unitless]
REAL(r8):: omega(-NSNOW+1:0) ! weighted sINgle-scatter albedo of snow+aerosol layer (lyr) [frc]
REAL(r8):: g(-NSNOW+1:0) ! weighted asymmetry parameter of snow+aerosol layer (lyr) [frc]
REAL(r8):: tau_star(-NSNOW+1:0) ! transformed (i.e. Delta-EddINgton) optical depth of snow+aerosol layer (lyr) [unitless]
REAL(r8):: omega_star(-NSNOW+1:0) ! transformed (i.e. Delta-EddINgton) SSA of snow+aerosol layer (lyr) [frc]
REAL(r8):: g_star(-NSNOW+1:0) ! transformed (i.e. Delta-EddINgton) asymmetry paramater of snow+aerosol layer (lyr) [frc]
INTEGER :: g_idx, c_idx, l_idx ! gridcell, column, and landunit INdices [idx]
INTEGER :: bnd_idx ! spectral band INdex (1 <= bnd_idx <= numrad_snw) [idx]
INTEGER :: rds_idx ! snow effective radius INdex for retrievINg Mie parameters from lookup table [idx]
INTEGER :: snl_btm ! INdex of bottom snow layer (0) [idx]
INTEGER :: snl_top ! INdex of top snow layer (-4 to 0) [idx]
INTEGER :: fc ! column filter INdex
INTEGER :: i ! layer INdex [idx]
INTEGER :: j ! aerosol number INdex [idx]
INTEGER :: n ! tridiagonal matrix INdex [idx]
INTEGER :: m ! secondary layer INdex [idx]
INTEGER :: ix,k ! an INdex
REAL(r8):: F_direct(-NSNOW+1:0) ! direct-beam radiation at bottom of layer INterface (lyr) [W/m^2]
REAL(r8):: F_net(-NSNOW+1:0) ! net radiative flux at bottom of layer INterface (lyr) [W/m^2]
REAL(r8):: F_abs(-NSNOW+1:0) ! net absorbed radiative energy (lyr) [W/m^2]
REAL(r8):: F_abs_sum ! total absorbed energy IN column [W/m^2]
REAL(r8):: F_sfc_pls ! upward radiative flux at snowpack top [W/m^2]
REAL(r8):: F_btm_net ! net flux at bottom of snowpack [W/m^2]
REAL(r8):: F_sfc_net ! net flux at top of snowpack [W/m^2]
REAL(r8):: energy_sum ! sum of all energy terms; should be 0.0 [W/m^2]
REAL(r8):: F_direct_btm ! direct-beam radiation at bottom of snowpack [W/m^2]
REAL(r8):: mu_not ! cosINe of solar zenith angle (used locally) [frc]
INTEGER :: err_idx ! counter for number of times through error loop [nbr]
REAL(r8):: lat_coord ! gridcell latitude (debuggINg only)
REAL(r8):: lon_coord ! gridcell longitude (debuggINg only)
INTEGER :: sfctype ! underlyINg surface type (debuggINg only)
REAL(r8):: pi ! 3.1415...
LOGICAL :: opened
INTEGER :: lu_unit=10
logical, external :: wrf_dm_on_monitor
! INtermediate variables for radiative transfer approximation:
REAL(r8):: gamma1(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: gamma2(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: gamma3(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: gamma4(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: lambda(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: GAMMA(-NSNOW+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: mu_one ! two-stream coefficient from Toon et al. (lyr) [unitless]
REAL(r8):: e1(-NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (lyr)
REAL(r8):: e2(-NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (lyr)
REAL(r8):: e3(-NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (lyr)
REAL(r8):: e4(-NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (lyr)
REAL(r8):: C_pls_btm(-NSNOW+1:0) ! INtermediate variable: upward flux at bottom INterface (lyr) [W/m2]
REAL(r8):: C_mns_btm(-NSNOW+1:0) ! INtermediate variable: downward flux at bottom INterface (lyr) [W/m2]
REAL(r8):: C_pls_top(-NSNOW+1:0) ! INtermediate variable: upward flux at top INterface (lyr) [W/m2]
REAL(r8):: C_mns_top(-NSNOW+1:0) ! INtermediate variable: downward flux at top INterface (lyr) [W/m2]
REAL(r8):: A(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: B(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: D(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: E(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: AS(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: DS(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: X(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
REAL(r8):: Y(-2*NSNOW+1:0) ! tri-diag INtermediate variable from Toon et al. (2*lyr)
! ---------------------------------------------------------------------------------------------
! zero albedos for all poINts
ALBSND(1: NBAND) = 0.
ALBSNI(1: NBAND) = 0.
! DefINe constants
pi = SHR_CONST_PI
! always use Delta approximation for snow
DELTA = 1
! Zero absorbed radiative fluxes:
do i=-NSNOW+1,1,1
flx_abs_lcl(:,:) = 0._r8
flx_abs(i,:) = 0._r8
enddo
h2osno_lcl = SNEQV
if (h2osno_lcl > min_snw) then
! Assign local (single-column) variables to global values
! If there is snow, but zero snow layers, we must create a layer locally.
! This layer is presumed to have the fresh snow effective radius.
if ( ISNOW > -1) then
flg_nosnl = 1
snl_lcl = -1
h2osno_ice_lcl(0) = h2osno_lcl
h2osno_liq_lcl(0) = 0._r8
snw_rds_lcl(0) = nint(snw_rds_min)
else
flg_nosnl = 0
snl_lcl = ISNOW
h2osno_liq_lcl(:) = SNLIQ(:)
h2osno_ice_lcl(:) = SNICE(:)
snw_rds_lcl(:) = 150
endif
! Set local aerosol array
snl_btm = 0
snl_top = snl_lcl+1
! Set local aerosol array
mss_cnc_aer_lcl(:) = BCS*1.0_r8
! Set spectral underlying surface albedos to their corresponding VIS or NIR albedos
albsfc_lcl(1) = UALB
albsfc_lcl(nir_bnd_bgn:nir_bnd_end) = UALB
! Incident flux weighting parameters
! - sum of all VIS bands must equal 1
! - sum of all NIR bands must equal 1
!
! Spectral bands (5-band case)
! Band 1: 0.3-0.7um (VIS)
! Band 2: 0.7-1.0um (NIR)
! Band 3: 1.0-1.2um (NIR)
! Band 4: 1.2-1.5um (NIR)
! Band 5: 1.5-5.0um (NIR)
! 5-band weights DIRECT
flx_wgt(1) = 1._r8
flx_wgt(2) = 0.49352158521175_r8
flx_wgt(3) = 0.18099494230665_r8
flx_wgt(4) = 0.12094898498813_r8
flx_wgt(5) = 0.20453448749347_r8
! Loop over snow spectral bands
do bnd_idx = 1,numrad_snw
mu_not = COSZ ! must set here, because of error handling
flg_dover = 1 ! default is to redo
err_idx = 0 ! number of times through loop
do while (flg_dover > 0)
! DEFAULT APPROXIMATIONS:
! VIS: Delta-Eddington
! NIR (all): Delta-Hemispheric Mean
! WARNING: DO NOT USE DELTA-EDDINGTON FOR NIR DIFFUSE - this sometimes results in negative albedo
!
! ERROR CONDITIONS:
! Conditions which cause "trip", resulting in redo of RT approximation:
! 1. negative absorbed flux
! 2. total absorbed flux greater than incident flux
! 3. negative albedo
! NOTE: These errors have only been encountered in spectral bands 4 and 5
!
! ERROR HANDLING
! 1st error (flg_dover=2): switch approximation (Edd->HM or HM->Edd)
! 2nd error (flg_dover=3): change zenith angle by 0.02 (this happens about 1 in 10^6 cases)
! 3rd error (flg_dover=4): switch approximation with new zenith
! Subsequent errors: repeatedly change zenith and approximations...
if ( bnd_idx == 1) then
if (flg_dover == 2) then
APRX_TYP = 3
elseif (flg_dover == 3) then
APRX_TYP = 1
if (COSZ > 0.5_r8) then
mu_not = mu_not - 0.02_r8
else
mu_not = mu_not + 0.02_r8
endif
elseif (flg_dover == 4) then
APRX_TYP = 3
else
APRX_TYP = 1
endif
else
if (flg_dover == 2) then
APRX_TYP = 1
elseif (flg_dover == 3) then
APRX_TYP = 3
if (COSZ > 0.5_r8) then
mu_not = mu_not - 0.02_r8
else
mu_not = mu_not + 0.02_r8
endif
elseif (flg_dover == 4) then
APRX_TYP = 1
else
APRX_TYP = 3
endif
endif
! Set direct or diffuse incident irradiance to 1
! (This has to be within the bnd loop because mu_not is adjusted in rare cases)
flx_slrd_lcl(bnd_idx) = 1._r8/(mu_not*pi) ! this corresponds to incident irradiance of 1.0
flx_slri_lcl(bnd_idx) = 0._r8
! Pre-emptive error handling: aerosols can reap havoc on these absorptive bands.
! Since extremely high soot concentrations have a negligible effect on these bands, zero them.
if ( (numrad_snw == 5).and.((bnd_idx == 5).or.(bnd_idx == 4)) ) then
mss_cnc_aer_lcl = 0._r8
endif
! Define local Mie parameters based on snow grain size and aerosol species,
! retrieved from a lookup table.
do i=snl_top,snl_btm,1
rds_idx = snw_rds_lcl(i) - snw_rds_min_tbl + 1
! snow optical properties (direct radiation)
ss_alb_snw_lcl(i) = ss_alb_snw_drc(rds_idx,bnd_idx)
asm_prm_snw_lcl(i) = asm_prm_snw_drc(rds_idx,bnd_idx)
ext_cff_mss_snw_lcl(i) = ext_cff_mss_snw_drc(rds_idx,bnd_idx)
enddo
! aerosol species optical properties only BC nocoated
ss_alb_aer_lcl = ss_alb_bc1(1,bnd_idx)
asm_prm_aer_lcl = asm_prm_bc1(1,bnd_idx)
ext_cff_mss_aer_lcl = ext_cff_mss_bc1(1,bnd_idx)
! 1. snow and aerosol layer column mass (L_snw, L_aer [kg/m^2])
! 2. optical Depths (tau_snw, tau_aer)
! 3. weighted Mie properties (tau, omega, g)
! Weighted Mie parameters of each layer
do i=snl_top,snl_btm,1
L_snw(i) = h2osno_ice_lcl(i)+h2osno_liq_lcl(i)
tau_snw(i) = L_snw(i)*ext_cff_mss_snw_lcl(i)
L_aer(i) = L_snw(i)*mss_cnc_aer_lcl(i)
tau_aer(i) = L_aer(i)*ext_cff_mss_aer_lcl
tau_sum = 0._r8
omega_sum = 0._r8
g_sum = 0._r8
tau_sum = tau_sum + tau_aer(i)
omega_sum = omega_sum + (tau_aer(i)*ss_alb_aer_lcl)
g_sum = g_sum + (tau_aer(i)*ss_alb_aer_lcl*asm_prm_aer_lcl)
tau(i) = tau_sum + tau_snw(i)
if(tau(i) == 0) then
write(6,*) 'FATAL ERROR in SNICAR RT, tau(',i,') is the denominatoer can not equal to ',tau(i)
call endrun()
end if
omega(i) = (1/tau(i))*(omega_sum+(ss_alb_snw_lcl(i)*tau_snw(i)))
g(i) = (1/(tau(i)*omega(i)))*(g_sum+ (asm_prm_snw_lcl(i)*ss_alb_snw_lcl(i)*tau_snw(i)))
enddo
! DELTA transformations, if requested
if (DELTA == 1) then
do i=snl_top,snl_btm,1
g_star(i) = g(i)/(1+g(i))
omega_star(i) = ((1-(g(i)**2))*omega(i)) / (1-(omega(i)*(g(i)**2)))
tau_star(i) = (1-(omega(i)*(g(i)**2)))*tau(i)
enddo
else
do i=snl_top,snl_btm,1
g_star(i) = g(i)
omega_star(i) = omega(i)
tau_star(i) = tau(i)
enddo
endif
! Total column optical depth:
! tau_clm(i) = total optical depth above the bottom of layer i
tau_clm(snl_top) = 0._r8
do i=snl_top+1,snl_btm,1
tau_clm(i) = tau_clm(i-1)+tau_star(i-1)
enddo
! Direct radiation at bottom of snowpack:
F_direct_btm = albsfc_lcl(bnd_idx)*mu_not*exp(-(tau_clm(snl_btm)+tau_star(snl_btm))/mu_not)*pi*flx_slrd_lcl(bnd_idx)
! Intermediates
! Gamma values are approximation-specific.
! Eddington
if (APRX_TYP==1) then
do i=snl_top,snl_btm,1
gamma1(i) = (7-(omega_star(i)*(4+(3*g_star(i)))))/4
gamma2(i) = -(1-(omega_star(i)*(4-(3*g_star(i)))))/4
gamma3(i) = (2-(3*g_star(i)*mu_not))/4
gamma4(i) = 1-gamma3(i)
mu_one = 0.5
enddo
! Quadrature
elseif (APRX_TYP==2) then
do i=snl_top,snl_btm,1
gamma1(i) = (3**0.5)*(2-(omega_star(i)*(1+g_star(i))))/2
gamma2(i) = omega_star(i)*(3**0.5)*(1-g_star(i))/2
gamma3(i) = (1-((3**0.5)*g_star(i)*mu_not))/2
gamma4(i) = 1-gamma3(i)
mu_one = 1/(3**0.5)
enddo
! Hemispheric Mean
elseif (APRX_TYP==3) then
do i=snl_top,snl_btm,1
gamma1(i) = 2 - (omega_star(i)*(1+g_star(i)))
gamma2(i) = omega_star(i)*(1-g_star(i))
gamma3(i) = (1-((3**0.5)*g_star(i)*mu_not))/2
gamma4(i) = 1-gamma3(i)
mu_one = 0.5
enddo
endif
! Intermediates for tri-diagonal solution
do i=snl_top,snl_btm,1
lambda(i) = sqrt(abs((gamma1(i)**2) - (gamma2(i)**2)))
GAMMA(i) = gamma2(i)/(gamma1(i)+lambda(i))
e1(i) = 1+(GAMMA(i)*exp(-lambda(i)*tau_star(i)))
e2(i) = 1-(GAMMA(i)*exp(-lambda(i)*tau_star(i)))
e3(i) = GAMMA(i) + exp(-lambda(i)*tau_star(i))
e4(i) = GAMMA(i) - exp(-lambda(i)*tau_star(i))
enddo !enddo over snow layers
! Intermediates for tri-diagonal solution
do i=snl_top,snl_btm,1
C_pls_btm(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* &
exp(-(tau_clm(i)+tau_star(i))/mu_not)* &
(((gamma1(i)-(1/mu_not))*gamma3(i))+ &
(gamma4(i)*gamma2(i))))/((lambda(i)**2)-(1/(mu_not**2)))
C_mns_btm(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* &
exp(-(tau_clm(i)+tau_star(i))/mu_not)* &
(((gamma1(i)+(1/mu_not))*gamma4(i))+ &
(gamma2(i)*gamma3(i))))/((lambda(i)**2)-(1/(mu_not**2)))
C_pls_top(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* &
exp(-tau_clm(i)/mu_not)*(((gamma1(i)-(1/mu_not))* &
gamma3(i))+(gamma4(i)*gamma2(i))))/((lambda(i)**2)-(1/(mu_not**2)))
C_mns_top(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* &
exp(-tau_clm(i)/mu_not)*(((gamma1(i)+(1/mu_not))* &
gamma4(i))+(gamma2(i)*gamma3(i))))/((lambda(i)**2)-(1/(mu_not**2)))
enddo
! Coefficients for tridiaganol matrix solution
do i=2*snl_lcl+1,0,1
!Boundary values for i=1 and i=2*snl_lcl, specifics for i=odd and i=even
if (i==(2*snl_lcl+1)) then
A(i) = 0
B(i) = e1(snl_top)
D(i) = -e2(snl_top)
E(i) = flx_slri_lcl(bnd_idx)-C_mns_top(snl_top)
elseif(i==0) then
A(i) = e1(snl_btm)-(albsfc_lcl(bnd_idx)*e3(snl_btm))
B(i) = e2(snl_btm)-(albsfc_lcl(bnd_idx)*e4(snl_btm))
D(i) = 0
E(i) = F_direct_btm-C_pls_btm(snl_btm)+(albsfc_lcl(bnd_idx)*C_mns_btm(snl_btm))
elseif(mod(i,2)==-1) then ! If odd and i>=3 (n=1 for i=3)
n=floor(i/2.0)
A(i) = (e2(n)*e3(n))-(e4(n)*e1(n))
B(i) = (e1(n)*e1(n+1))-(e3(n)*e3(n+1))
D(i) = (e3(n)*e4(n+1))-(e1(n)*e2(n+1))
E(i) = (e3(n)*(C_pls_top(n+1)-C_pls_btm(n)))+(e1(n)*(C_mns_btm(n)-C_mns_top(n+1)))
elseif(mod(i,2)==0) then ! If even and i<=2*snl_lcl
n=(i/2)
A(i) = (e2(n+1)*e1(n))-(e3(n)*e4(n+1))
B(i) = (e2(n)*e2(n+1))-(e4(n)*e4(n+1))
D(i) = (e1(n+1)*e4(n+1))-(e2(n+1)*e3(n+1))
E(i) = (e2(n+1)*(C_pls_top(n+1)-C_pls_btm(n)))+(e4(n+1)*(C_mns_top(n+1)-C_mns_btm(n)))
endif
enddo
AS(0) = A(0)/B(0)
DS(0) = E(0)/B(0)
do i=-1,(2*snl_lcl+1),-1
X(i) = 1/(B(i)-(D(i)*AS(i+1)))
AS(i) = A(i)*X(i)
DS(i) = (E(i)-(D(i)*DS(i+1)))*X(i)
enddo
Y(2*snl_lcl+1) = DS(2*snl_lcl+1)
do i=(2*snl_lcl+2),0,1
Y(i) = DS(i)-(AS(i)*Y(i-1))
enddo
! Downward direct-beam and net flux (F_net) at the base of each layer:
do i=snl_top,snl_btm,1
F_direct(i) = mu_not*pi*flx_slrd_lcl(bnd_idx)*exp(-(tau_clm(i)+tau_star(i))/mu_not)
F_net(i) = (Y(2*i-1)*(e1(i)-e3(i))) + (Y(2*i)*(e2(i)-e4(i))) + &
C_pls_btm(i) - C_mns_btm(i) - F_direct(i)
enddo
! Upward flux at snowpack top:
F_sfc_pls = (Y(2*snl_lcl+1)*(exp(-lambda(snl_top)*tau_star(snl_top))+ &
GAMMA(snl_top))) + (Y(2*snl_lcl+2)*(exp(-lambda(snl_top)* &
tau_star(snl_top))-GAMMA(snl_top))) + C_pls_top(snl_top)
! Net flux at bottom = absorbed radiation by underlying surface:
F_btm_net = -F_net(snl_btm)
! Bulk column albedo and surface net flux
albedo_1 = F_sfc_pls/((mu_not*pi*flx_slrd_lcl(bnd_idx))+flx_slri_lcl(bnd_idx))
F_sfc_net = F_sfc_pls - ((mu_not*pi*flx_slrd_lcl(bnd_idx))+flx_slri_lcl(bnd_idx))
trip = 0
! Absorbed flux in each layer
do i=snl_top,snl_btm,1
if(i==snl_top) then
F_abs(i) = F_net(i)-F_sfc_net
else
F_abs(i) = F_net(i)-F_net(i-1)
endif
flx_abs_lcl(i,bnd_idx) = F_abs(i)
! ERROR check: negative absorption
if (flx_abs_lcl(i,bnd_idx) < -0.00001) then
trip = 1
endif
enddo
flx_abs_lcl(1,bnd_idx) = F_btm_net
if (flg_nosnl == 1) then
! If there are no snow layers (but still snow), all absorbed energy must be in top soil layer
!flx_abs_lcl(:,bnd_idx) = 0._r8
!flx_abs_lcl(1,bnd_idx) = F_abs(0) + F_btm_net
! changed on 20070408:
! OK to put absorbed energy in the fictitous snow layer because routine SurfaceRadiation
! handles the case of no snow layers. Then, if a snow layer is addded between now and
! SurfaceRadiation (called in Hydrology1), absorbed energy will be properly distributed.
flx_abs_lcl(0,bnd_idx) = F_abs(0)
flx_abs_lcl(1,bnd_idx) = F_btm_net
endif
!Underflow check (we've already tripped the error condition above)
do i=snl_top,1,1
if (flx_abs_lcl(i,bnd_idx) < 0._r8) then
flx_abs_lcl(i,bnd_idx) = 0._r8
endif
enddo
F_abs_sum = 0._r8
do i=snl_top,snl_btm,1
F_abs_sum = F_abs_sum + F_abs(i)
enddo
!ERROR check: absorption greater than incident flux
! (should make condition more generic than "1._r8")
if (F_abs_sum > 1._r8) then
trip = 1
endif
!ERROR check:
if ((albedo_1 < 0._r8).and.(trip==0)) then
write(6,*) 'ERROR: albedo_1 <0 = ', albedo_1
trip = 1
endif
! Set conditions for redoing RT calculation
if ((trip == 1).and.(flg_dover == 1)) then
flg_dover = 2
elseif ((trip == 1).and.(flg_dover == 2)) then
flg_dover = 3
elseif ((trip == 1).and.(flg_dover == 3)) then
flg_dover = 4
elseif((trip == 1).and.(flg_dover == 4).and.(err_idx < 20)) then
flg_dover = 3
err_idx = err_idx + 1
write(6,*) "SNICAR WARNING: Both approximations failed with new zenith angle :(. Zenith= ", mu_not
elseif((trip == 1).and.(flg_dover == 4).and.(err_idx >= 20)) then
flg_dover = 0
write(6,*) "SNICAR ERROR: FOUND A WORMHOLE. STUCK IN INFINITE LOOP! Called from: "
write(6,*) "SNICAR STATS: L_snw(0)= ", L_snw(0)
write(6,*) "SNICAR STATS: h2osno= ", h2osno_lcl, " snl= ", snl_lcl
write(6,*) "SNICAR STATS: soot1(0)= ", mss_cnc_aer_lcl
call endrun()
else
flg_dover = 0
endif
enddo !enddo while (flg_dover > 0)
! Energy conservation check:
! Incident direct+diffuse radiation equals (absorbed+bulk_transmitted+bulk_reflected)
energy_sum = (mu_not*pi*flx_slrd_lcl(bnd_idx)) + flx_slri_lcl(bnd_idx) - (F_abs_sum + F_btm_net + F_sfc_pls)
if (abs(energy_sum) > 0.00001_r8) then
write (6,"(a,e12.6,a,i6,a,i6)") "SNICAR ERROR: Energy conservation error of : ", energy_sum
! call endrun()
endif
albout_lcl(bnd_idx) = albedo_1
! Check that albedo is less than 1
if (albout_lcl(bnd_idx) > 1.0) then
write (6,*) "SNICAR ERROR: Albedo > 1.0 "
write (6,*) "SNICAR STATS: bnd_idx= ",bnd_idx
write (6,*) "SNICAR STATS: albout_lcl(bnd_idx)= ",albout_lcl(bnd_idx), " albsfc_lcl(bnd_idx)= ",albsfc_lcl(bnd_idx)
write (6,*) "SNICAR STATS: h2osno= ", h2osno_lcl, " snl= ", snl_lcl
write (6,*) "SNICAR STATS: coszen= ", COSZ, " flg_slr= ", 1
write (6,*) "SNICAR STATS: soot(0)= ", mss_cnc_aer_lcl
write (6,*) "SNICAR STATS: L_snw(-4)= ", L_snw(-4)
write (6,*) "SNICAR STATS: L_snw(-3)= ", L_snw(-3)
write (6,*) "SNICAR STATS: L_snw(-2)= ", L_snw(-2)
write (6,*) "SNICAR STATS: L_snw(-1)= ", L_snw(-1)
write (6,*) "SNICAR STATS: L_snw(0)= ", L_snw(0)
call endrun()
endif
enddo ! loop over wvl bands
! Weight output NIR albedo appropriately
albout(1) = albout_lcl(1)
flx_sum = 0._r8
do bnd_idx= nir_bnd_bgn,nir_bnd_end
flx_sum = flx_sum + flx_wgt(bnd_idx)*albout_lcl(bnd_idx)
end do
albout(2) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end))
! Weight output NIR absorbed layer fluxes (flx_abs) appropriately
flx_abs(:,1) = flx_abs_lcl(:,1)
do i=snl_top,1,1
flx_sum = 0._r8
do bnd_idx= nir_bnd_bgn,nir_bnd_end
flx_sum = flx_sum + flx_wgt(bnd_idx)*flx_abs_lcl(i,bnd_idx)
enddo
flx_abs(i,2) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end))
end do
elseif ((h2osno_lcl < min_snw) .and. (h2osno_lcl > 0._r8)) then
albout(1) = UALB
albout(2) = UALB
! There is either zero snow, or no sun
else
albout(1) = 0._r8
albout(2) = 0._r8
endif ! if column has snow and coszen > 0
ALBSND(1)=albout(1)
ALBSNI(1)=albout(1)
ALBSND(2)=albout(2)
ALBSNI(2)=albout(2)
write (8,*) "check the ALBSND", ALBSND(1)
write (8,*) "check the ALBSNI", ALBSNI(2)
END SUBROUTINE SNOWALB_SNICAR_DIR
新函数定义好后,在对应的地方使用:
private :: SNOWALB_SNICAR_DIR
IF(OPT_ALB == 3) THEN
CALL SNOWALB_SNICAR_DIR (parameters,NBAND,NSNOW,ISNOW,COSZ,BCS,UALB,SNICE,SNLIQ,SNEQV,ALBSND,ALBSNI)
END IF
变量添加
变量添加比较麻烦,由于Fortran需要对每一个变量都声明并分配的特点,我们要找出所有与该过程相关的函数,并将对应的变量声明添加上去。
在NoahMPLSM模块中,我主要新增了两个变量 UALB与BCS.。
其中,UALB从背景反照率ALBBCK中获取,BCS则来源于自己添加的变量MBC。
为此,我们需要增加相应的变量声明:
REAL(r8),INTENT(IN) :: BCS !bc IN snow (ng/g)
REAL,INTENT(IN) :: UALB !Albedo of underlyINg surface
注意,该声明涉及所有使用学反照率的函数与模块,我们都需要进行添加。
修改完所有以后,我们在Registry中加入:
state real mbc ij misc 1 - i012rh "MBC" "BC in snow" "ng/g"
这样,MBC就可以通过grid%mbc直接赋值了。
在对应的dyn_em文件夹下,修改module_first_rk_step_part1.F加入mbc变量。
修改phys文件夹所有与noahmp模块相关的模块,包括:
module_sf_noahmplsm.F module_sf_noahmpdrv.F module_surface_driver.F,如果修改noahmp的初始化函数的话,还需修改moudle_phys_init.F。
大致的顺序是:WRF使用first_rk_step读取处理初始化变量,first_rk_step使用surface_driver模块对地面变量初始化,real完成后,WRF会根据选择的物理方案,调用phys_init针对方案对不同方案的各个变量进行初始化,在使用Noah-MP方案时,phys_init会调用Noahmpdrv模块里的NOAHMPINIT函数,noahmp-drv模块则需要使用noahlsm模块中的NOAHMPSHFLX这个函数进行运算,不同模块间都存在相互耦合的关系。
除了上述以外,为了确保MBC被正确读取以及之后进行并行运算,最好修改share下的 module_wps_io_arw.F:
VarName='FLAG_MBC'
call retrieve_index(index,VarName,varname_all,nrecs,iret)
call mpi_file_read_at(iunit,file_offset(index)+5*4, &
igarb,1,mpi_integer4, &
mpi_status_ignore, ierr)
flag_mbc=igarb
以及moule_optional_input:
CALL optional_mbc ( grid , fid , &
ids, ide, jds, jde, kds, kde, &
ims, ime, jms, jme, kms, kme, &
its, ite, jts, jte, kts, kte )
SUBROUTINE optional_mbc ( grid , fid , &
ids, ide, jds, jde, kds, kde, &
ims, ime, jms, jme, kms, kme, &
its, ite, jts, jte, kts, kte )
USE module_io_wrf
USE module_domain , ONLY : domain
USE module_configure , ONLY : grid_config_rec_type
USE module_io_domain
IMPLICIT NONE
TYPE ( domain ) :: grid
INTEGER , INTENT(IN) :: fid
INTEGER :: ids, ide, jds, jde, kds, kde, &
ims, ime, jms, jme, kms, kme, &
its, ite, jts, jte, kts, kte
INTEGER :: itmp , icnt , ierr
flag_name = ' '
flag_mbc = 0
flag_name(1:8) = 'MBC '
CALL wrf_get_dom_ti_integer ( fid, 'FLAG_' // flag_name, itmp, 1, icnt, ierr )
IF ( ierr .EQ. 0 ) THEN
flag_mbc = itmp
END IF
END SUBROUTINE optional_mbc
来保证初始化的并行运算。
致谢
感谢NCAR的 He cenlin教授、UCLA的薛永康教授以及本所冯锦明研究员的回复与解答。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)