diff --git a/bld/build-namelist b/bld/build-namelist
index 3b2182bdc7..3a4a574f2b 100755
--- a/bld/build-namelist
+++ b/bld/build-namelist
@@ -3686,7 +3686,7 @@ if ($cfg->get('microphys') eq 'rk') {
}
# Eddy Diffusivity Adjustments
-if ($cfg->get('pbl') eq "uw" or $cfg->get('pbl') eq "spcam_m2005") {
+if ($cfg->get('pbl') eq "uw") {
add_default($nl, 'kv_top_pressure');
add_default($nl, 'kv_top_scale');
add_default($nl, 'kv_freetrop_scale');
@@ -4369,11 +4369,6 @@ if ($offline_drv ne 'stub') {
}
}
-if ($phys eq 'spcam_sam1mom' or $phys eq 'spcam_m2005') {
- add_default($nl, 'iradsw', 'val'=>'1');
- add_default($nl, 'iradlw', 'val'=>'1');
-}
-
#-----------------------------------------------------------------------------------------------
# Rename component logfiles.
#
diff --git a/bld/config_files/definition.xml b/bld/config_files/definition.xml
index 9feec64a03..f5731e289b 100644
--- a/bld/config_files/definition.xml
+++ b/bld/config_files/definition.xml
@@ -57,19 +57,19 @@ Option to turn on waccmx thermosphere/ionosphere extension: 0 => no, 1 => yes
Ionosphere model used in WACCMX.
-
-Physics package: cam4, cam5, cam6, cam7, held_suarez, adiabatic, kessler, tj2016, grayrad, spcam_sam1mom, spcam_m2005.
+
+Physics package: cam4, cam5, cam6, cam7, held_suarez, adiabatic, kessler, tj2016, grayrad.
Switch to turn on Harmonized Emissions Component (HEMCO) for chemistry: 0 => no, 1 => yes.
-
+
Microphysics package: rk (Rasch and Kristjansson), mg1 (Morrison and
Gettelman two moment scheme CAM5.1), mg2 (Morrison and Gettelman second
-version CAM6), mg3 (MG scheme 3rd version, graupel), PUMAS, SPCAM_m2005, SPCAM_sam1mom.
+version CAM6), mg3 (MG scheme 3rd version, graupel), PUMAS.
-
-Macrophysics package: RK, Park, CLUBB_SGS, SPCAM_sam1mom, SPCAM_m2005.
+
+Macrophysics package: RK, Park, CLUBB_SGS.
Switch to turn on CLUBB_SGS package: 0 => no, 1 => yes
@@ -83,9 +83,9 @@ Switch to turn on UNICON package: 0 => off, 1 => on
Switch to turn on/off advecting CLUBB moments: 0 => no, 1 => yes
-
+
PBL package: uw (University of Washington), hb (Holtslag and Boville), hbr
- (Holtslag, Boville, and Rasch), clubb_sgs, spcam_sam1om, spcam_m2005, none.
+ (Holtslag, Boville, and Rasch), clubb_sgs, none.
Radiative transfer calculation:
@@ -310,23 +310,5 @@ that setting to allow for cross-compilation, and for instances where the
$OSNAME value is too generic. For example, currently on both cray-xt and
bluegene systems $OSNAME has the value "linux".
-
-Switch to turn on SPCAM version of CLUBB_SGS package: 0 => no, 1 => yes
-
-
-SPCAM number of grid points in x
-
-
-SPCAM number of grid points in y
-
-
-SPCAM number of grid points in z
-
-
-SPCAM horizontal grid spacing, m
-
-
-SPCAM time step, s
-
diff --git a/bld/configure b/bld/configure
index 82bae67421..9b9b930284 100755
--- a/bld/configure
+++ b/bld/configure
@@ -98,18 +98,12 @@ OPTIONS
-pcols Set maximum number of columns in a chunk to .
-pergro Switch enables building CAM for perturbation growth tests.
-phys Physics option [cam4 | cam5 | cam6 | cam7 |
- held_suarez | adiabatic | kessler | tj2016 | grayrad
- spcam_sam1mom | spcam_m2005]. Default: cam6
+ held_suarez | adiabatic | kessler | tj2016 | grayrad].
-prog_species Comma-separate list of prognostic mozart species packages.
Currently available: DST,SSLT,SO4,GHG,OC,BC,CARBON16
-psubcols Maximum number of sub-columns in a run - set to 1 if not using sub-columns (default)
-rad Specify the radiation package [rrtmg | rrtmgp | rrtmgp_gpu | camrt]
-silhs Switch on SILHS.
- -spcam_clubb_sgs Turn on the SPCAM version of CLUBB
- -spcam_nx SPCAM x-grid. - defaults to 4 (note the CRM requires spcam_nx to be greater than or equal to 4)
- -spcam_ny SPCAM y-grid. - defaults to 1
- -spcam_dx SPCAM horizontal grid spacing.
- -spcam_dt SPCAM timestep.
-unicon Switch to turn on the UNICON scheme. Default: off.
-usr_mech_infile Path and file name of the user supplied chemistry mechanism file.
-waccm_phys Switch enables the use of WACCM physics in any chemistry configuration.
@@ -253,7 +247,6 @@ GetOptions(
"cosp_libdir=s" => \$opts{'cosp_libdir'},
"cppdefs=s" => \$opts{'cppdefs'},
"cpl=s" => \$opts{'cpl'},
- "spcam_clubb_sgs" => \$opts{'spcam_clubb_sgs'},
"debug" => \$opts{'debug'},
"dyn=s" => \$opts{'dyn'},
"edit_chem_mech" => \$opts{'edit_chem_mech'},
@@ -302,10 +295,6 @@ GetOptions(
"silhs" => \$opts{'silhs'},
"s|silent" => \$opts{'silent'},
"smp!" => \$opts{'smp'},
- "spcam_nx=s" => \$opts{'spcam_nx'},
- "spcam_ny=s" => \$opts{'spcam_ny'},
- "spcam_dx=s" => \$opts{'spcam_dx'},
- "spcam_dt=s" => \$opts{'spcam_dt'},
"spmd!" => \$opts{'spmd'},
"target_os=s" => \$opts{'target_os'},
"unicon" => \$opts{'unicon'},
@@ -565,10 +554,10 @@ if ($phys_pkg =~ m/^adiabatic$|^held_suarez$|^kessler$|^tj2016$|^grayrad$/) {
my $chem_pkg = 'not_set';
# defaults based on physics package
-if ($simple_phys or $phys_pkg =~ m/^cam[34]$/ or $phys_pkg eq 'spcam_sam1mom') {
+if ($simple_phys or $phys_pkg =~ m/^cam[34]$/) {
$chem_pkg = 'none';
}
-elsif ($phys_pkg eq 'cam5' or $phys_pkg eq 'spcam_m2005') {
+elsif ($phys_pkg eq 'cam5') {
$chem_pkg = 'trop_mam3';
}
elsif ($phys_pkg eq 'cam6') {
@@ -599,7 +588,7 @@ if (defined $opts{'chem'}) {
" -chem can only be set to 'none' or 'terminator'.\n";
}
}
- elsif ($phys_pkg =~ m/^cam4$|^spcam_sam1mom$/) {
+ elsif ($phys_pkg =~ m/cam4/) {
# The modal aerosols are not valid with cam4 physics
if ($chem_pkg =~ /_mam/) {
die "configure ERROR: -phys=$phys_pkg -chem=$chem_pkg\n".
@@ -764,46 +753,6 @@ my $co2_cycle = $cfg_ref->get('co2_cycle');
if ($co2_cycle and $print>=2) { print "co2_cycle option: ON$eol"; }
-#-----------------------------------------------------------------------------------------------
-# Superparameterization mode (SPCAM)
-#
-# These values all default to 1 unless specified by the user during configure
-
-if ($phys_pkg eq 'spcam_sam1mom' or $phys_pkg eq 'spcam_m2005') {
-
- if ($smp eq 'ON') {
- die "ERROR: SPCAM may not be used with threading $eol";
- }
-
- if ($print>=2) {print "Configure CAM for SPCAM (superparameterization) mode: $phys_pkg.$eol"; }
-
- if (defined $opts{'spcam_nx'}) {
- $cfg_ref->set('spcam_nx', $opts{'spcam_nx'});
- my $spcam_nx = $cfg_ref->get('spcam_nx');
- if ($spcam_nx < 4) {
- die "configure ERROR: spcam_nx must be greater than or equal to 4\n";
- }
- if ($print>=2) {print "spcam_nx= $spcam_nx $eol"; }
- }
- if (defined $opts{'spcam_ny'}) {
- $cfg_ref->set('spcam_ny', $opts{'spcam_ny'});
- my $spcam_ny = $cfg_ref->get('spcam_ny');
- if ($print>=2) {print "spcam_ny= $spcam_ny $eol"; }
- }
- if (defined $opts{'spcam_dx'}) {
- $cfg_ref->set('spcam_dx', $opts{'spcam_dx'});
- my $spcam_dx = $cfg_ref->get('spcam_dx');
- if ($print>=2) {print "spcam_nx= $spcam_dx $eol"; }
- }
- if (defined $opts{'spcam_dt'}) {
- $cfg_ref->set('spcam_dt', $opts{'spcam_dt'});
- my $spcam_dt = $cfg_ref->get('spcam_dt');
- if ($print>=2) {print "spcam_nt= $spcam_dt $eol"; }
- }
-
-}
-
-
#-----------------------------------------------------------------------------------------------
# Micro-physics package
@@ -821,12 +770,6 @@ elsif ($phys_pkg eq 'cam6') {
elsif ($phys_pkg eq 'cam7') {
$microphys_pkg = 'mg3';
}
-elsif ($phys_pkg eq 'spcam_sam1mom') {
- $microphys_pkg = 'spcam_sam1mom';
-}
-elsif ($phys_pkg eq 'spcam_m2005') {
- $microphys_pkg = 'spcam_m2005';
-}
# Allow the user to override the default via the commandline.
if (defined $opts{'microphys'}) {
@@ -916,14 +859,6 @@ $cfg_ref->set('silhs', $silhs);
if ($print>=2) { print "silhs: $silhs$eol"; }
-#-----------------------------------------------------------------------------------------------
-# SPCAM version of CLUBB
-if (defined $opts{'spcam_clubb_sgs'}) {
- $cfg_ref->set('spcam_clubb_sgs', $opts{'spcam_clubb_sgs'});
-}
-my $spcam_clubb_sgs = $cfg_ref->get('spcam_clubb_sgs');
-
-
#-----------------------------------------------------------------------------------------------
# Break apart CLUBB options into separate fields
@@ -955,18 +890,12 @@ elsif ($phys_pkg =~ /cam6/) {
$macrophys_pkg = 'park';
}
}
-elsif ($phys_pkg =~ /cam7/ and $clubb_sgs) {
+elsif ($phys_pkg =~ /cam7/) {
$macrophys_pkg = 'clubb_sgs';
}
-elsif ($phys_pkg eq 'spcam_sam1mom') {
- $macrophys_pkg = 'spcam_sam1mom';
-}
-elsif ($phys_pkg eq 'spcam_m2005') {
- $macrophys_pkg = 'spcam_m2005';
-}
# user overrides
-if ($clubb_sgs or $spcam_clubb_sgs) {
+if ($clubb_sgs) {
$macrophys_pkg = 'clubb_sgs';
}
@@ -998,15 +927,9 @@ elsif ($phys_pkg =~ /cam6/) {
$pbl_pkg = 'uw';
}
}
-elsif ($phys_pkg =~ /cam7/ and $clubb_sgs) {
+elsif ($phys_pkg =~ /cam7/) {
$pbl_pkg = 'clubb_sgs';
}
-elsif ($phys_pkg eq 'spcam_sam1mom') {
- $pbl_pkg = 'spcam_sam1mom';
-}
-elsif ($phys_pkg eq 'spcam_m2005') {
- $pbl_pkg = 'spcam_m2005';
-}
# Allow the user to override the default via the commandline.
if ($clubb_sgs == 1) {
@@ -1058,10 +981,10 @@ if ($unicon and $print>=2) { print "Using UNICON scheme.$eol"; }
# Set default
my $rad_pkg = 'none';
-if ($phys_pkg =~ m/cam4|spcam_sam1mom/) {
+if ($phys_pkg =~ m/cam4/) {
$rad_pkg = 'camrt';
}
-elsif ($phys_pkg =~ m/cam5|cam6|spcam_m2005/) {
+elsif ($phys_pkg =~ m/cam5|cam6/) {
$rad_pkg = 'rrtmg';
}
elsif ($phys_pkg =~ m/cam7/) {
@@ -1116,31 +1039,6 @@ if ($cosp and ($phys_pkg ne 'cam5' and $phys_pkg ne 'cam6' and $phys_pkg ne 'cam
if ($cosp and $print>=2) { print "COSP simulator enabled$eol"; }
-#-----------------------------------------------------------------------------------------------
-# Checks for SPCAM compatability
-
-if ($phys_pkg eq 'spcam_sam1mom') {
- if ($rad_pkg ne 'camrt') {
- die "configure ERROR: radiation package: $rad_pkg is not compatible\n".
- " with sam1mom -- it should be camrt\n";
- }
- if ($chem_pkg ne 'none') {
- die "configure ERROR: chemistry package: $chem_pkg is not compatible\n".
- " with sam1mom -- it should be none\n";
- }
-}
-
-if ($phys_pkg eq 'spcam_m2005') {
- if ($rad_pkg !~ m/rrtmg/) {
- die "configure ERROR: radiation package: $rad_pkg is not compatible\n".
- " with m2005 -- it should be rrtmg\n";
- }
- if ($chem_pkg ne 'trop_mam3') {
- die "configure ERROR: chemistry package: $chem_pkg is not compatible\n".
- " with m2005 -- it should be trop_mam3\n";
- }
-}
-
#-----------------------------------------------------------------------------------------------
# offline unit driver
if (defined $opts{'offline_drv'}) {
@@ -1349,10 +1247,10 @@ elsif ($phys_pkg eq 'cam7') {
elsif ($phys_pkg eq 'cam6') {
$nlev = 32;
}
-elsif ($phys_pkg eq 'cam5' or $phys_pkg eq 'spcam_m2005') {
+elsif ($phys_pkg eq 'cam5') {
$nlev = 30;
}
-elsif ($phys_pkg eq 'cam4' or $phys_pkg eq 'spcam_sam1mom') {
+elsif ($phys_pkg eq 'cam4') {
$nlev = 26;
}
else {
@@ -1377,10 +1275,6 @@ $cfg_ref->set('nlev', $nlev);
if ($print>=2) { print "Number of vertical levels: $nlev$eol"; }
-if ($phys_pkg eq 'spcam_sam1mom' or $phys_pkg eq 'spcam_m2005') {
- $cfg_ref->set('spcam_nz', $nlev-2);
-}
-
#------------------------------------------------------------------------------------------------
# chemistry preprocessor....
# -- avoid using the chem_preprocessor unless it's required
@@ -1539,11 +1433,11 @@ else {
unless ($simple_phys) {
# Microphysics parameterization
- if ($microphys_pkg eq 'rk' or $microphys_pkg eq 'spcam_sam1mom') {
+ if ($microphys_pkg eq 'rk') {
$nadv += 2;
if ($print>=2) { print "Advected constituents added by $microphys_pkg microphysics: 2$eol"; }
}
- elsif ($microphys_pkg =~ /^mg1/ or $microphys_pkg eq 'spcam_m2005') {
+ elsif ($microphys_pkg =~ /^mg1/) {
$nadv += 4;
if ($print>=2) { print "Advected constituents added by $microphys_pkg microphysics: 4$eol"; }
}
@@ -1843,28 +1737,6 @@ my $cfg_cppdefs = ' ';
# Building for perturbation growth tests
if ($pergro eq "ON") { $cfg_cppdefs .= " -DPERGRO"; }
-# Building for superparameterization
-my $spcam_clubb_sgs = $cfg_ref->get('spcam_clubb_sgs');
-my $spcam_nx = $cfg_ref->get('spcam_nx');
-my $spcam_ny = $cfg_ref->get('spcam_ny');
-my $spcam_nz = $cfg_ref->get('spcam_nz');
-my $spcam_dx = $cfg_ref->get('spcam_dx');
-my $spcam_dt = $cfg_ref->get('spcam_dt');
-
-my $yes3Dval = 1; # default to 3D for spcam
-if ($spcam_ny eq 1) {$yes3Dval = 0;} #Turn off if not using 3D
-
-if ($phys_pkg eq 'spcam_m2005' or $phys_pkg eq 'spcam_sam1mom') {
- $cfg_cppdefs .= " -DSPCAM_NX=$spcam_nx -DSPCAM_NY=$spcam_ny -DSPCAM_NZ=$spcam_nz -DSPCAM_DX=$spcam_dx -DSPCAM_DT=$spcam_dt -DYES3DVAL=$yes3Dval -DCRM ";
- if ( $spcam_clubb_sgs == 1 ) {
- $cfg_cppdefs .= "-DSPCAM_CLUBB_SGS -DCLUBB_CRM -DCLUBB_REAL_TYPE=dp -DCLUBB_SAM"; ## -DNO_LAPACK_ISNAN";
- }
-}
-
-if ($phys_pkg eq 'spcam_m2005') {$cfg_cppdefs .= " -DECPP -Dm2005";}
-
-if ($phys_pkg eq 'spcam_sam1mom') {$cfg_cppdefs .= " -Dsam1mom";}
-
# Configure CAM to produce IOP files for SCAM
if ($camiop eq 'ON') { $cfg_cppdefs .= " -DBFB_CAM_SCAM_IOP"; }
@@ -2289,29 +2161,6 @@ sub write_filepath
print $fh "$camsrcdir/src/physics/pumas-frozen\n";
}
- # Superparameterization
- if ($phys_pkg eq 'spcam_m2005' or $phys_pkg eq 'spcam_sam1mom') {
- print $fh "$camsrcdir/src/physics/spcam\n";
- print $fh "$camsrcdir/src/physics/spcam/crm\n";
-
- # add additional directories for sam6.10.4
- print $fh "$camsrcdir/src/physics/spcam/crm/ADV_MPDATA\n";
- if ($phys_pkg eq 'spcam_sam1mom') {
- print $fh "$camsrcdir/src/physics/spcam/crm/MICRO_SAM1MOM\n";
- }
- if ($phys_pkg eq 'spcam_m2005') {
- print $fh "$camsrcdir/src/physics/spcam/crm/MICRO_M2005\n";
- print $fh "$camsrcdir/src/physics/spcam/ecpp\n";
- }
- if ( $spcam_clubb_sgs == 1 ) {
- print $fh "$camsrcdir/src/physics/spcam/crm/CLUBB\n";
- print $fh "$camsrcdir/src/physics/spcam/crm/SGS_CLUBBkvhkvm\n"
- }
- else {
- print $fh "$camsrcdir/src/physics/spcam/crm/SGS_TKE\n";
- }
- }
-
# This directory contains much of the code for physics packages,
# as well as the cam specific interface modules that may need to
# be overridden by modules from directories that occur earlier
diff --git a/bld/namelist_files/namelist_defaults_cam.xml b/bld/namelist_files/namelist_defaults_cam.xml
index 53af1a16cf..b2a84861cf 100644
--- a/bld/namelist_files/namelist_defaults_cam.xml
+++ b/bld/namelist_files/namelist_defaults_cam.xml
@@ -352,8 +352,6 @@
98288.0D0 98288.0D0 98288.0D0
- 98288.0D0
- 98288.0D0 atm/cam/topo/fv_0.9x1.25_nc3000_Nsw006_Nrs002_Co008_Fi001_ZR_c160505.nc
@@ -1993,8 +1991,6 @@
NEU
-MOZ
-OFF
@@ -2090,7 +2086,6 @@
.true. .false. .false.
- .true. 0.075D0 0.100D0
@@ -2108,14 +2103,12 @@
.true. .true. .true.
- .true. 0 1 1 1
- 1 0.01d0 0.001d0
@@ -2123,7 +2116,6 @@
.false. .true.
- .true. .false.
@@ -2333,10 +2325,8 @@
RK MG MG
-SPCAM_m2005
-SPCAM_sam1mom
-MG
-MG
+MG
+MG 1 0
@@ -2462,8 +2452,6 @@
rkparkCLUBB_SGS
-SPCAM_sam1mom
-SPCAM_m2005
@@ -2511,9 +2499,7 @@
0.37D00.35D0
-0.35D00.45D0
-0.45D00.45D00.35D02.30D0
@@ -2523,7 +2509,6 @@
2.30D00.45D02.30D0
-0.45D00.55D00.22D0
@@ -2552,7 +2537,6 @@
2.30D00.70D02.300D0
-0.13D00.26D02.30D0
@@ -2576,7 +2560,6 @@
1.00D01.50D01.10D0
-1.2D00.60D0
@@ -2591,9 +2574,6 @@
0.4D01.0D0
-1.00D0
-1.00D0
-
.false..true.
@@ -2656,16 +2636,12 @@
HB HBR CLUBB_SGS
-SPCAM_m2005
-SPCAM_sam1mom ZM off UNICON NONE
-SPCAM
-SPCAM NONE UW
@@ -2673,8 +2649,6 @@
Hack Hack CLUBB_SGS
-SPCAM
-SPCAM .true.
@@ -2800,10 +2774,8 @@
1.1D0 1.0D0 1.05D0
- 1.0D0 1.1D0 1.0D0
- 1.0D0 1.e-7 5.e-3
@@ -2960,19 +2932,14 @@
4 4 4
- 442
-4242
-4242
-424242424242
-4212
@@ -3204,8 +3171,6 @@
'Q' 'Q','CLDLIQ','RAINQM' 'Q','CLDLIQ','CLDICE'
-'Q','CLDLIQ','CLDICE'
-'Q','CLDLIQ','CLDICE' 'Q','CLDLIQ','CLDICE' 'Q','CLDLIQ','CLDICE','RAINQM','SNOWQM' 'Q','CLDLIQ','CLDICE','RAINQM','SNOWQM','GRAUQM'
diff --git a/bld/namelist_files/namelist_definition.xml b/bld/namelist_files/namelist_definition.xml
index d07e4c403f..8458b6e42d 100644
--- a/bld/namelist_files/namelist_definition.xml
+++ b/bld/namelist_files/namelist_definition.xml
@@ -3611,16 +3611,14 @@ Default: 10
group="phys_ctl_nl" valid_values="ZM,UNICON,off,CLUBB_SGS" >
Type of deep convection scheme employed. 'ZM' for Zhang-McFarlane;
'off' for none; or 'UNICON' which doesn't distinquish shallow and deep.
-Default: 'ZM' unless using 'UNICON', 'SPCAM' or 'pbl=none'
+Default: 'ZM' unless using 'UNICON' or 'pbl=none'
+ group="phys_ctl_nl" valid_values="NONE,RK,MG" >
Type of microphysics scheme employed. 'RK' for Rasch and Kristjansson
(1998); 'MG' for Morrison and Gettelman (2008), Gettelman et al (2010)
two moment scheme for CAM5 and CAM6
-SPCAM has two different microphysics schemes: SPCAM_m2005 (Morrison et al 2005),
-SPCAM_sam1mom (Khairoutinov 2003)
Default: set by build-namelist (depends on value set in configure).
@@ -3640,14 +3638,12 @@ Default: set by build-namelist
+ group="phys_ctl_nl" valid_values="Hack,UW,CLUBB_SGS,UNICON" >
Type of shallow convection scheme employed.
'Hack' for Hack shallow convection;
'UW' for original McCaa UW pbl scheme, modified by Sungsu Park;
'CLUBB_SGS' for CLUBB_SGS
'UNICON' which doesn't distinquish shallow and deep.
- 'SPCAM_m2005' for SPCAM double moment
- 'SPCAM_sam1mom' for SPCAM single moment
Default: set by build-namelist (depends on {{ hilight }}eddy_scheme{{ closehilight }}).
@@ -5370,7 +5366,7 @@ Default: 0
+ group="phys_ctl_nl" valid_values="HB,diag_TKE,HBR,CLUBB_SGS" >
Type of eddy scheme employed by the vertical diffusion package. 'HB' for
Holtslag and Boville; 'diag_TKE' for diagnostic tke version of Grenier and
Bretherton; 'HBR' for Rasch modified version of 'HB'.
@@ -6189,7 +6185,7 @@ Wet deposition method used
MOZ --> mozart scheme is used
NEU --> J Neu's scheme is used
OFF --> wet deposition is turned off
-Default: NEU except for SPCAM runs
+Default: NEU
CAM cam6 physics:
CAM cam5 physics:CAM cam4 physics:
- CAM simplified and non-versioned physics :
+ CAM simplified and non-versioned physics :
-
-
-
-
- F2000Nuopc
- 2000_CAM40_CLM50%SP_CICE%PRES_DOCN%DOM_SROF_SGLC_SWAV
-
-
@@ -274,16 +265,6 @@
HIST_CAM60_SLND_SICE_DOCN%AQP3_SROF_SGLC_SWAV
-
- QSPCAMS
- 2000_CAM%SPCAMS_SLND_SICE_DOCN%AQP3_SROF_SGLC_SWAV
-
-
-
- QPSPCAMM
- 2000_CAM%SPCAMM_SLND_SICE_DOCN%AQP3_SROF_SGLC_SWAV
-
-
QPC62000_CAM60_SLND_SICE_DOCN%AQP3_SROF_SGLC_SWAV
@@ -360,16 +341,6 @@
1850_CAM60_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV
-
- FSPCAMM
- 2000_CAM%SPCAMM_CLM50%SP_CICE%PRES_DOCN%DOM_RTM_SGLC_SWAV
-
-
-
- FSPCAMS
- 2000_CAM%SPCAMS_CLM50%SP_CICE%PRES_DOCN%DOM_RTM_SGLC_SWAV
-
-
FHIST_BDRDHIST_CAM60_CLM50%BGC-CROP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV_BGC%BDRD
@@ -400,19 +371,6 @@
-
-
-
- FSPCAMCLBS
- 2000_CAM%SPCAMCLBS_CLM50%SP_CICE%PRES_DOCN%DOM_RTM_SGLC_SWAV
-
-
-
- FSPCAMCLBM
- 2000_CAM%SPCAMCLBM_CLM50%SP_CICE%PRES_DOCN%DOM_RTM_SGLC_SWAV
-
-
-
@@ -787,76 +745,6 @@
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
-
-
- 1
- 1
-
-
-
diff --git a/cime_config/config_pes.xml b/cime_config/config_pes.xml
index f488c21b27..2650843bfe 100644
--- a/cime_config/config_pes.xml
+++ b/cime_config/config_pes.xml
@@ -2057,18 +2057,6 @@
-
-
- 1
- 1
- 1
- 1
- 1
- 1
- 1
- 1
-
- none
diff --git a/cime_config/testdefs/testlist_cam.xml b/cime_config/testdefs/testlist_cam.xml
index d0789ac1f3..46117e6067 100644
--- a/cime_config/testdefs/testlist_cam.xml
+++ b/cime_config/testdefs/testlist_cam.xml
@@ -381,26 +381,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -2156,70 +2136,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/doc/ChangeLog b/doc/ChangeLog
index dde451cd76..cb0780103d 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,5 +1,140 @@
===============================================================
+Tag name: cam6_4_067
+Originator(s): eaton
+Date: 15 February 2025
+One-line Summary: Remove SP-CAM
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1217
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+
+Resolve #1171 - Remove SP-CAM from cam_development
+
+Describe any changes made to build system:
+. remove spcam build options
+
+Describe any changes made to the namelist:
+. remove spcam namelist options
+
+List any changes to the defaults for the boundary datasets: none
+
+Describe any substantial timing or memory changes: none
+
+Code reviewed by: cacraig
+
+List all files eliminated:
+doc/ReleaseNotes
+. This file hasn't been updated since cam5.4. Put this information
+ somewhere else.
+
+src/physics/cam/spcam_drivers.F90
+src/physics/spcam/*
+src/physics/spcam/crm/*
+src/physics/spcam/crm/ADV_MPDATA/*
+src/physics/spcam/crm/CLUBB/*
+src/physics/spcam/crm/MICRO_M2005/*
+src/physics/spcam/crm/MICRO_SAM1MOM/*
+src/physics/spcam/crm/SGS_CLUBBkvhkvm/*
+src/physics/spcam/crm/SGS_TKE/*
+src/physics/spcam/ecpp/*
+. remove all SPCAM source and drivers
+
+List all files added and what they do: none
+
+List all existing files that have been modified, and describe the changes:
+
+bld/build-namelist
+. remove dependencies on spcam
+
+bld/config_files/definition.xml
+. remove 'phys' options spcam_sam1mom and spcam_m2005
+. remove 'microphys' options spcam_sam1mom and spcam_m2005
+. remove 'macrophys' options spcam_sam1mom and spcam_m2005
+. remove 'pbl' options spcam_sam1mom and spcam_m2005
+. remove parameters 'spcam_clubb_sgs', 'spcam_nx', 'spcam_ny', 'spcam_nz',
+ 'spcam_dx', 'spcam_dt'
+
+bld/configure
+. remove -phys options spcam_sam1mom and spcam_m2005
+. remove commandline options -spcam_clubb_sgs, -spcam_nx, -spcam_ny,
+ -spcam_dx, -spcam_dt
+. remove code paths for spcam:
+ src/physics/spcam/
+ src/physics/spcam/crm/
+ src/physics/spcam/crm/ADV_MPDATA/
+ src/physics/spcam/crm/MICRO_SAM1MOM/
+ src/physics/spcam/crm/MICRO_M2005/
+ src/physics/spcam/crm/CLUBB/
+ src/physics/spcam/crm/SGS_CLUBBkvhkvm/
+ src/physics/spcam/crm/SGS_TKE/
+ src/physics/spcam/ecpp/
+
+bld/namelist_files/namelist_defaults_cam.xml
+. remove dependencies on phys, microphys, macrophys, and pbl attributes
+ spcam_sam1mom and spcam_m2005
+. remove dependencies on spcam_clubb_sgs attribute
+
+bld/namelist_files/namelist_definition.xml
+. remove SPCAM_sam1mom and SPCAM_m2005 as valid values for microp_scheme,
+ shallow_scheme, and eddy_scheme
+
+cime_config/config_component.xml
+. remove physics options %SPCAMS, %SPCAMCLBS, %SPCAMM, %SPCAMCLBM
+
+cime_config/config_compsets.xml
+. remove F2000Nuopc - not used
+. remove QSPCAMS, QPSPCAMM, FSPCAMM, FSPCAMS
+. remove FSPCAMCLBS, FSPCAMCLBM
+. remove SPCAM settings for NTHRDS_[ATM,CPL,ESP,GLC,ICE,LND,OCN,ROF,WAV]
+
+cime_config/config_pes.xml
+. remove settings for SPCAM*
+
+cime_config/testdefs/testlist_cam.xml
+. remove tests for SPCAM*
+
+src/chemistry/modal_aero/aero_model.F90
+src/physics/cam/cloud_diagnostics.F90
+src/physics/cam/cloud_fraction.F90
+src/physics/cam/conv_water.F90
+src/physics/cam/convect_deep.F90
+src/physics/cam/convect_shallow.F90
+src/physics/cam/diffusion_solver.F90
+src/physics/cam/microp_driver.F90
+src/physics/cam/ndrop.F90
+src/physics/cam/phys_control.F90
+src/physics/cam/physpkg.F90
+src/physics/cam/pkg_cldoptics.F90
+src/physics/cam/vertical_diffusion.F90
+. remove dependecies on SPCAM_sam1mom and SPCAM_m2005
+
+If there were any failures reported from running test_driver.sh on any test
+platform, and checkin with these failures has been OK'd by the gatekeeper,
+then copy the lines from the td.*.status files for the failed tests to the
+appropriate machine below. All failed tests must be justified.
+
+derecho/intel/aux_cam:
+ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s (Overall: FAIL)
+- pre-existing failures due to HEMCO not having reproducible results (issues #1018 and #856)
+
+SMS_D_Ln9.f19_f19_mg17.FXHIST.derecho_intel.cam-outfrq9s_amie (Overall: FAIL)
+SMS_D_Ln9_P1280x1.ne0CONUSne30x8_ne0CONUSne30x8_mt12.FCHIST.derecho_intel.cam-outfrq9s (Overall: FAIL)
+- pre-existing failures due to build-namelist error requiring CLM/CTSM external update
+
+derecho/nvhpc/aux_cam: All PASS
+
+izumi/nag/aux_cam: All PASS
+
+izumi/gnu/aux_cam: All PASS
+
+CAM tag used for the baseline comparison tests if different than previous
+tag:
+
+Summarize any changes to answers: BFB
+
+===============================================================
+===============================================================
+
Tag name: cam6_4_066
Originator(s): fvitt
Date: 14 Feb 2025
diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes
deleted file mode 100644
index c8ababd26a..0000000000
--- a/doc/ReleaseNotes
+++ /dev/null
@@ -1,300 +0,0 @@
--------------------------------------------------
-New features in CAM-5.4
--------------------------------------------------
-
-## CAM-SE
-* Update SE dycore tuning parameters (XXEaton)
- - Change time stepping method to RK5 (Kinnmark & Gray Runga-Kutta 5
- stage; 3rd order accurate in time)
- - Set the namelists variables as recommended for RK5 in:
- http://www.cgd.ucar.edu/cms/pel/software/cam-se-dt-table.pdf
- - Add "tstep_type" namelist option for SE dycore
- - Turn on the FV energy fixer.
- - Remove the variable "energy_fixer" from the cam namelist.
- - Reformulated total energy as applied in physics_update and the energy fixers for CAM-FV and CAM-SE dycores.
-
-## CAM-FV
-* Vertical remapping is now applied to temperature instead of energy. This
- primarily affects WACCM by reducing numerical artifacts near the model top.
- - Reformulated total energy as applied in physics_update and the energy fixers for CAM-FV and CAM-SE dycores.
-
-## CARMA
-* Add six new CARMA models:
- - cirrus_dust
- - meteor_impact
- - mixed_sulfate
- - pmc_sulfate
- - tholin
- - test_tracers2
-
-* Further development of CARMA-CAM integration, including:
- - New sulfate model features.
- - "Fractal" code for soot.
- - Port to the NAG compiler.
-
-## CLUBB
-* Update the version of CLUBB used
-* Add features to the interface (all options, controlled by namelist switches)
- - rain evaportation-turbulence feedback
- - advection of CLUBB's moments
- - cloud top radiational cooling parameterization
- - explicit diffusion on CLUBBs prognostic temperature and total water
- - provide support for CLUBB/microphysics sub-stepping
-
-## CHEMISTRY
-
-* Added ability to use wild fire emissions produced by CLM4.5
-
-* Added option for external forcing of H2O from CH4 oxidation when running
- low-top CAM5 without chemistry. CH4 oxidation is an important source of
- H2O in the stratosphere.
-
-* Reaction constants updated to JPL10
-
-* Added functionality to provide rate groupings (summations) diagnostics
-
-* Corrections to aerosol surface area
-
-* NEU wet deposition changes
- . set TICE to 263
- . disable wet deposition poleward of 60 degrees and pressures < 200 mbar
- . correction Henry's Law parameters used for SO2 deposition (in seq_drydep_mod)
- . correction in units of NEU wet deposition diagnostics
-
-* Chemistry preprocessor updates:
- . enthalpies for chemical potential heating now specified in mechanism files
- . added ability to put comments at the end of reactions in mechanism file following '#' or '!'
- . bug fixes for species names longer than 8 characters (up to 16 characters)
-
-
-## COSP
- . Update from COSP1.3 (version used for CMIP5) to COSP1.4 (version endorsed for CMIP6)
- - includes code optimizations, new CALIPSO cloud phase diagnostics, new timing variables
- - retains radiatively active snow in all simulators (merged from CESM version of COSP1.3)
- - fixes bug affecting convective ice input into COSP
-
-## AEROSOLS
-
-* Added 4-mode modal aerosol model (MAM4)
-
-* Enhancements to emission specifications (surface and elevated):
- . ability to specify emissions from multiple input files for any given species
- . optional global attribute 'input_method' (set to: 'SERIAL', 'CYCLICAL',
- or 'INTERP_MISSING_MONTHS') in the emissions input file which overrides the
- corresponding *type namelist option on a file-by-file basis
- . optional multiplier proceeding the emissions filepath, e.g.:
- 'NAME -> 0.5*/path.../filename.nc'
-
-* Prognostic Modal Aerosols: Provide the capability to prognose modal aerosols in the stratosphere. This
- gives CAM5 and WACCM5 the ability to simulate aerosols in the stratosphere
- which originate from volcanic eruptions. To this end, accumulation to coarse
- mode exchange is allowed and the widths and edges of the modes are modified
-
-* Added options to use different then default values for solubility factors for
- BULK aerosols
-
-## DUST
-
-* Defaults changed for soil_erod and dust_emis_fact.
- . All grids except the 0.9x1.25 FV and a few low resolution grid now use
- the soid_erod dataset generated for the 1.9x2.5 FV grid.
- . The value of dust_emis_fact has been changed for FV 1/2 and 1/4 degree
- grids to 0.45 based on tuning done at PNNL. The value for FV 1 degree
- was not changed since that will require retuning the production configuration.
-
-* Tuned following Albani et al., 2014 to best match observations
-
-* New soil erodibility file from Albani which specifically improves the dust in the Middle East
-
-## Radiation
-
-* New optical properties with less absorbing optics for MAM3 and MAM4 (use aeronet dust optics and dust in the aitken mode 2)
-
-* Added option to calculate solar insolation using the mean of cosz in a radiation time step. When this option is turned on,
- it eliminates the spurious zonal oscillation of daily insolation caused by discrete time sampling.
-
-## Microphysics
-
-* New microphysics scheme: MG version 2 adds prognostic precipitation and has
- a cleaner implementation compared to the original MG scheme.
-
-* It is now possible to control both the number of microphysics substeps per
- physics time step, and joint macrophysics/microphysics substepping, via the
- namelist.
-
-* Add pre-existing ice option to nucleate_ice code.
-
-* Add option for Hoose heterogeneous freezing parameterization.
-
-* Add option to specify/parameterize precipitation fraction
-
-* Add option to use a different dehydration threshold (rhmin) for in the polar stratosphere.
-
-* New switch to use alternative autoconversion scheme in MG2 (following Seifert and Behang 2001): when active this
- uses a different autoconversion and accretion scheme for liquid in MG2
-
-* Add Song and Zhang 2012 version of MG 2-moment microphysics in ZM convective scheme as an option
-
-## Macrophysics
-
-* Add option for a ice supersaturation closure (supported in both CAM5 and CAM-CLUBB)
-
-## Deep convection
-
-* Minor improvements to the ZM scheme improve robustness for some inputs
- (e.g. unusually high temperatures).
-
-* Add option for convective organization in ZM (based on Mapes and Neale 2010)
-
-## Sub-columns
-* Modifications to pbuf and history to support sub-columns
-
-* Introduced sub-column interface and utlities routines
-
-* Microphysics now has the ability to be run on grid(usual) or subcolumns
-
-## Gravity waves
-
-* New AMIP configuration with a high vertical resolution uses spectral
- gravity waves in the low top model.
-
-* A long-wavelength inertial gravity wave spectrum has been added, and
- frontogenesis can now trigger waves in this spectrum.
-
-* Gravity waves can be triggered from shallow, as well as deep, convection.
-
-* The entire gravity wave scheme has been audited to correct conservation
- issues, internal inconsistencies, and problems with hard-coded parameters.
- This should result in more accurate and less noisy output.
-
-* WACCM's gravity wave functionality can now be enabled in non-WACCM runs,
- and can be enabled/disabled at run time via the namelist.
-
-* Most gravity wave parameters that were previously hard-coded are now
- set by the namelist instead.
-
-* Added "tau_0_ubc" option, to enforce an upper boundary condition of tau = 0
- in the gravity wave parameterization.
-
-## WACCM
-
-* WACCM5 with prognostic modal aerosols in the stratosphere
-
-* Reaction constants updated to JPL10
-
-* Background ionization from star light added to WACCM
-
-* New specification of stratospheric aerosols (volcanic)
-
-* New treatment of stratospheric aerosol chemistry
-
-* Corrections to age-of-air tracers
-
-* Bug fixes and usability improvements for SC-WACCM and WACCM5 that were
- also added between CESM 1.2.1 and CESM 1.2.2.
-
-* Include SC-WACCM5 which has prognostic modal aerosols
-
-* WACCM-X now has an option to turn on the extended ionosphere including
- calculation of electron and ion temperatur and ion transport ambipolar
- diffusion
-
-## SCAM
-
-## SPCAM
-* Super-parameterized CAM (SPCAM) implements a 2D cloud resolving model (the
- System for Atmospheric Modeling SAM, version 6.8.2) in CAM. When it is turned on,
- it replaces CAM's parameterization for moist convection and large-scale condensation
- with this alternate model.
-
-* The SPCAM package allows CLUBB to be used or not. It is important to note that there is
- a SPCAM-specific version of CLUBB within the CRM package and it is not the same CLUBB being
- used by CAM
-
-## AQUAPLANET
-* CESM-aquaplanet is now supported out-of-the-box via prescribed-SST (QPCx) and
- slab-ocean (QSCx) compsets (where x is CAM version).
-
-
--------------------------------------------------
-CODE CLEANUP AND REFACTORING
--------------------------------------------------
-
-* CARMA and the MG microphysics interface now use micro_mg_utils to get
- size distribution parameters for cloud droplets, ice, and precipitation.
- Previously this was done with duplicated code.
-
-* The chemistry-aerosol model interface was refactored to provide a more
- extendable framework. This will ease incorporation of other aerosol
- models (e.g., a sectional aerosol model)
-
-* The SE dycore now uses Pa instead of hPa, which is consistent with CAM's
- physics.
-
-* The CAM and WACCM gravity wave modules have been merged together, and the
- result was extensively refactored. The CAM interface (gw_drag.F90) has been
- separated from a new set of modules that constitute a portable layer, and
- the routines for the wave sources, wave propagation, and effective diffusion
- have been separated from each other as well.
-
-* Removed the WACCM_PHYS preprocessor macro, and brought WACCM physics modules
- up to date with current CAM conventions:
-
- - qbo, radheat, and iondrag have their own namelists. If WACCM is off, we
- compile in stubs rather than using the WACCM_PHYS macro.
- - Molecular diffusion is turned on/off at run time based on the namelist and
- the extent of the vertical grid.
- - Each type of gravity wave source is turned on/off via the namelist.
- - WACCM-specific fields set by the dycore are now communicated via the physics
- buffer rather than the physics_state object, and are only set if needed.
-
-* Remove restriction that radiation diagnostic calculations reuse the water
- uptake and wet radius values calculated for the climate affecting modes.
- These quantities are now recomputed for the diagnostic modes.
-
-* satellite history output was refactored to improve run-time performance
- -- find nearest neighbor operation was parallelized
-
-* The vertical diffusion code was refactored to use new tridiagonal matrix
- types, which represent operators in the diffusion equation.
-
--------------------------------------------------
-CAM INFRASTRUCTURE CHANGES
--------------------------------------------------
-
-* Improve the microp_aero driver by removing code that belonged in a CAM
- specific interface for the nucleate_ice parameterization and adding the
- missing CAM interface layer (nucleate_ice_cam).
-
-* Add two new functions to the rad_constituents interfaces to make it
- easier to access the mode and specie indices for specific modes and
- specie types.
-
-* Type descriptions in namelist_definitions.xml can now include variables
- as dimensions. For instance, both "integer(n)" and "integer(2)" can be
- used for a 1-D integer array.
-
-* The rad_climate and rad_diag_* arrays can now be set to a larger size
- using the new "-max_n_rad_cnst" configure option.
-
-* Turning on CESM's DEBUG mode now also turns on state_debug_checks.
-
-* The Lahey compiler is no longer supported because it doesn't support Fortran
- 2003 features.
-
-* Added a new namelist variable, history_aero_optics, to add modal aerosol
- optics diagnostics to the default history fields. The existing
- history_aerosol variable turns on diagnostics related to the aerosol
- production and removal tendencies.
-
-* Preliminary implementation of further flags to control default history
- outputs, including:
- - history_waccm
- - history_waccmx
- - history_chemistry
- - history_carma
- - history_clubb
-
-* CAM history changes:
- . increased number of fields in fincls from 750 to 1000
- . can have up to 10 simultaneous history files (or streams)
diff --git a/src/chemistry/modal_aero/aero_model.F90 b/src/chemistry/modal_aero/aero_model.F90
index 056b998a36..d75730c2ff 100644
--- a/src/chemistry/modal_aero/aero_model.F90
+++ b/src/chemistry/modal_aero/aero_model.F90
@@ -1006,7 +1006,6 @@ subroutine aero_model_gasaerexch( loffset, ncol, lchnk, troplev, delt, reaction_
real(r8), pointer :: fldcw(:,:)
real(r8), pointer :: sulfeq(:,:,:)
- logical :: is_spcam_m2005
!
! ... initialize nh3
!
@@ -1014,7 +1013,6 @@ subroutine aero_model_gasaerexch( loffset, ncol, lchnk, troplev, delt, reaction_
nh3_beg = vmr(1:ncol,:,nh3_ndx)
end if
!
- is_spcam_m2005 = cam_physpkg_is('spcam_m2005')
call pbuf_get_field(pbuf, dgnum_idx, dgnum)
call pbuf_get_field(pbuf, dgnumwet_idx, dgnumwet )
@@ -1046,14 +1044,13 @@ subroutine aero_model_gasaerexch( loffset, ncol, lchnk, troplev, delt, reaction_
!
call qqcw2vmr( lchnk, vmrcw, mbar, ncol, loffset, pbuf )
- if (.not. is_spcam_m2005) then ! regular CAM
- dvmrdt(:ncol,:,:) = vmr(:ncol,:,:)
- dvmrcwdt(:ncol,:,:) = vmrcw(:ncol,:,:)
+ dvmrdt(:ncol,:,:) = vmr(:ncol,:,:)
+ dvmrcwdt(:ncol,:,:) = vmrcw(:ncol,:,:)
! aqueous chemistry ...
- if( has_sox ) then
- call setsox( &
+ if( has_sox ) then
+ call setsox( &
ncol, &
lchnk, &
loffset, &
@@ -1076,21 +1073,21 @@ subroutine aero_model_gasaerexch( loffset, ncol, lchnk, troplev, delt, reaction_
aqso4_o3 &
)
- do n = 1, ntot_amode
- l = lptr_so4_cw_amode(n)
- if (l > 0) then
- call outfld( trim(cnst_name_cw(l))//'AQSO4', aqso4(:ncol,n), ncol, lchnk)
- call outfld( trim(cnst_name_cw(l))//'AQH2SO4', aqh2so4(:ncol,n), ncol, lchnk)
- end if
- end do
+ do n = 1, ntot_amode
+ l = lptr_so4_cw_amode(n)
+ if (l > 0) then
+ call outfld( trim(cnst_name_cw(l))//'AQSO4', aqso4(:ncol,n), ncol, lchnk)
+ call outfld( trim(cnst_name_cw(l))//'AQH2SO4', aqh2so4(:ncol,n), ncol, lchnk)
+ end if
+ end do
- call outfld( 'AQSO4_H2O2', aqso4_h2o2(:ncol), ncol, lchnk)
- call outfld( 'AQSO4_O3', aqso4_o3(:ncol), ncol, lchnk)
- call outfld( 'XPH_LWC', xphlwc(:ncol,:), ncol, lchnk )
+ call outfld( 'AQSO4_H2O2', aqso4_h2o2(:ncol), ncol, lchnk)
+ call outfld( 'AQSO4_O3', aqso4_o3(:ncol), ncol, lchnk)
+ call outfld( 'XPH_LWC', xphlwc(:ncol,:), ncol, lchnk )
- endif
+ endif
-! Tendency due to aqueous chemistry
+ ! Tendency due to aqueous chemistry
dvmrdt = (vmr - dvmrdt) / delt
dvmrcwdt = (vmrcw - dvmrcwdt) / delt
do m = 1, gas_pcnst
@@ -1102,15 +1099,6 @@ subroutine aero_model_gasaerexch( loffset, ncol, lchnk, troplev, delt, reaction_
call outfld( name, wrk(:ncol), ncol, lchnk )
enddo
- else if (is_spcam_m2005) then ! SPCAM ECPP
-! when ECPP is used, aqueous chemistry is done in ECPP,
-! and not updated here.
-! Minghuai Wang, 2010-02 (Minghuai.Wang@pnl.gov)
-!
- dvmrdt = 0.0_r8
- dvmrcwdt = 0.0_r8
- endif
-
! do gas-aerosol exchange (h2so4, msa, nh3 condensation)
if (ndx_h2so4 > 0) then
diff --git a/src/physics/cam/cloud_diagnostics.F90 b/src/physics/cam/cloud_diagnostics.F90
index bd0f9b8e9d..0aea0afbaf 100644
--- a/src/physics/cam/cloud_diagnostics.F90
+++ b/src/physics/cam/cloud_diagnostics.F90
@@ -30,7 +30,7 @@ module cloud_diagnostics
integer :: dei_idx, mu_idx, lambda_idx, iciwp_idx, iclwp_idx, cld_idx ! index into pbuf for cloud fields
integer :: ixcldice, ixcldliq, rei_idx, rel_idx
- logical :: do_cld_diag, mg_clouds, rk_clouds, camrt_rad, spcam_m2005_clouds, spcam_sam1mom_clouds
+ logical :: do_cld_diag, camrt_rad
logical :: one_mom_clouds, two_mom_clouds
integer :: cicewp_idx = -1
@@ -45,8 +45,6 @@ module cloud_diagnostics
! Index fields for precipitation efficiency.
integer :: acpr_idx, acgcme_idx, acnum_idx
- logical :: use_spcam
-
contains
!===============================================================================
@@ -59,12 +57,8 @@ subroutine cloud_diagnostics_register
call phys_getopts(radiation_scheme_out=rad_pkg,microp_scheme_out=microp_pgk)
camrt_rad = rad_pkg .eq. 'camrt'
- rk_clouds = microp_pgk == 'RK'
- mg_clouds = microp_pgk == 'MG'
- spcam_m2005_clouds = microp_pgk == 'SPCAM_m2005'
- spcam_sam1mom_clouds = microp_pgk == 'SPCAM_sam1mom'
- one_mom_clouds = (rk_clouds .or. spcam_sam1mom_clouds)
- two_mom_clouds = (mg_clouds .or. spcam_m2005_clouds)
+ one_mom_clouds = microp_pgk == 'RK'
+ two_mom_clouds = microp_pgk == 'MG'
if (one_mom_clouds) then
call pbuf_add_field('CLDEMIS','physpkg', dtype_r8,(/pcols,pver/), cldemis_idx)
@@ -110,8 +104,6 @@ subroutine cloud_diagnostics_init(pbuf2d)
! grid box total cloud ice water mixing ratio (kg/kg)
gb_totcldicemr_idx = pbuf_get_index('GB_TOTCLDICEMR')
- call phys_getopts(use_spcam_out=use_spcam)
-
if (two_mom_clouds) then
! initialize to zero
@@ -152,10 +144,10 @@ subroutine cloud_diagnostics_init(pbuf2d)
if (.not.do_cld_diag) return
- if (rk_clouds) then
+ if (one_mom_clouds) then
wpunits = 'gram/m2'
sampling_seq='rad_lwsw'
- else if (two_mom_clouds .or. spcam_sam1mom_clouds) then
+ else if (two_mom_clouds) then
wpunits = 'kg/m2'
sampling_seq=''
end if
@@ -206,7 +198,7 @@ subroutine cloud_diagnostics_init(pbuf2d)
call add_default ('TGCLDLWP', 1, ' ')
call add_default ('TGCLDIWP', 1, ' ')
call add_default ('TGCLDCWP', 1, ' ')
- if(rk_clouds) then
+ if(one_mom_clouds) then
if (camrt_rad) then
call add_default ('EMIS', 1, ' ')
else
@@ -452,12 +444,10 @@ subroutine cloud_diagnostics_calc(state, pbuf)
! Determine parameters for maximum/random overlap
call cldovrlap(lchnk, ncol, state%pint, cld, nmxrgn, pmxrgn)
- if(.not. use_spcam) then ! in spcam, these diagnostics are calcluated in crm_physics.F90
-! Cloud cover diagnostics (done in radiation_tend for camrt)
+ ! Cloud cover diagnostics (done in radiation_tend for camrt)
if (.not.camrt_rad) then
call cloud_cover_diags_out(lchnk, ncol, cld, state%pmid, nmxrgn, pmxrgn )
endif
- end if
tgicewp(:ncol) = 0._r8
tgliqwp(:ncol) = 0._r8
@@ -503,15 +493,12 @@ subroutine cloud_diagnostics_calc(state, pbuf)
endif
- if (.not. use_spcam) then
- ! for spcam, these are diagnostics in crm_physics.F90
- call outfld('GCLDLWP' ,gwp , pcols,lchnk)
- call outfld('TGCLDCWP',tgwp , pcols,lchnk)
- call outfld('TGCLDLWP',tgliqwp, pcols,lchnk)
- call outfld('TGCLDIWP',tgicewp, pcols,lchnk)
- call outfld('ICLDTWP' ,cwp , pcols,lchnk)
- call outfld('ICLDIWP' ,cicewp , pcols,lchnk)
- endif
+ call outfld('GCLDLWP' ,gwp , pcols,lchnk)
+ call outfld('TGCLDCWP',tgwp , pcols,lchnk)
+ call outfld('TGCLDLWP',tgliqwp, pcols,lchnk)
+ call outfld('TGCLDIWP',tgicewp, pcols,lchnk)
+ call outfld('ICLDTWP' ,cwp , pcols,lchnk)
+ call outfld('ICLDIWP' ,cicewp , pcols,lchnk)
! Compute total preciptable water in column (in mm)
tpw(:ncol) = 0.0_r8
diff --git a/src/physics/cam/cloud_fraction.F90 b/src/physics/cam/cloud_fraction.F90
index 821b84695d..7b3297b67d 100644
--- a/src/physics/cam/cloud_fraction.F90
+++ b/src/physics/cam/cloud_fraction.F90
@@ -204,11 +204,10 @@ subroutine cldfrc_init
macrop_scheme_out = macrop_scheme )
! Limit CAM5 cloud physics to below top cloud level.
- if ( .not. (macrop_scheme == "rk" .or. macrop_scheme == "SPCAM_sam1mom")) top_lev = trop_cloud_top_lev
+ if ( .not. macrop_scheme == "rk") top_lev = trop_cloud_top_lev
! Turn off inversion_cld if any UW PBL scheme is being used
- if ( (eddy_scheme .eq. 'diag_TKE' ) .or. (shallow_scheme .eq. 'UW' ) .or.&
- (shallow_scheme .eq. 'SPCAM_m2005') ) then
+ if ( eddy_scheme .eq. 'diag_TKE' .or. shallow_scheme .eq. 'UW' ) then
inversion_cld_off = .true.
else
inversion_cld_off = .false.
@@ -217,7 +216,7 @@ subroutine cldfrc_init
if ( masterproc ) then
write(iulog,*)'tuning parameters cldfrc_init: inversion_cld_off',inversion_cld_off
write(iulog,*)'tuning parameters cldfrc_init: dp1',dp1,'dp2',dp2,'sh1',sh1,'sh2',sh2
- if (shallow_scheme .ne. 'UW' .or. shallow_scheme .eq. 'SPCAM_m2005' ) then
+ if (shallow_scheme .ne. 'UW') then
write(iulog,*)'tuning parameters cldfrc_init: rhminl',rhminl,'rhminl_adj_land',rhminl_adj_land, &
'rhminh',rhminh,'premit',premit,'premib',premib
write(iulog,*)'tuning parameters cldfrc_init: iceopt',iceopt,'icecrit',icecrit
diff --git a/src/physics/cam/conv_water.F90 b/src/physics/cam/conv_water.F90
index d848895366..fb054c87b1 100644
--- a/src/physics/cam/conv_water.F90
+++ b/src/physics/cam/conv_water.F90
@@ -334,7 +334,7 @@ subroutine conv_water_4rad(state, pbuf)
! Select radiation constants (effective radii) for emissivity averaging.
- if( microp_scheme == 'RK' .or. microp_scheme == 'SPCAM_sam1mom') then
+ if( microp_scheme == 'RK') then
kabsi = 0.005_r8 + 1._r8/rei(i,k)
else
kabsi = 0.005_r8 + 1._r8/min(max(13._r8,rei(i,k)),130._r8)
diff --git a/src/physics/cam/convect_deep.F90 b/src/physics/cam/convect_deep.F90
index ebba3ba9fa..2262d8f25c 100644
--- a/src/physics/cam/convect_deep.F90
+++ b/src/physics/cam/convect_deep.F90
@@ -140,9 +140,6 @@ subroutine convect_deep_init(pref_edge)
call zm_conv_init(pref_edge)
case('UNICON')
if (masterproc) write(iulog,*)'convect_deep: deep convection done by UNICON'
- case('SPCAM')
- if (masterproc) write(iulog,*)'convect_deep: deep convection done by SPCAM'
- return
case default
if (masterproc) write(iulog,*)'WARNING: convect_deep: no deep convection scheme. May fail.'
end select
diff --git a/src/physics/cam/convect_shallow.F90 b/src/physics/cam/convect_shallow.F90
index e757e1eef4..9edd28c696 100644
--- a/src/physics/cam/convect_shallow.F90
+++ b/src/physics/cam/convect_shallow.F90
@@ -90,9 +90,6 @@ subroutine convect_shallow_register
call phys_getopts( shallow_scheme_out = shallow_scheme, microp_scheme_out = microp_scheme)
- ! SPCAM registers its own fields
- if (shallow_scheme == 'SPCAM') return
-
call pbuf_add_field('ICWMRSH', 'physpkg' ,dtype_r8,(/pcols,pver/), icwmrsh_idx )
call pbuf_add_field('RPRDSH', 'physpkg' ,dtype_r8,(/pcols,pver/), rprdsh_idx )
call pbuf_add_field('RPRDTOT', 'physpkg' ,dtype_r8,(/pcols,pver/), rprdtot_idx )
@@ -167,9 +164,6 @@ subroutine convect_shallow_init(pref_edge, pbuf2d)
integer k
character(len=16) :: eddy_scheme
- ! SPCAM does its own convection
- if (shallow_scheme == 'SPCAM') return
-
! ------------------------------------------------- !
! Variables for detailed abalysis of UW-ShCu scheme !
! ------------------------------------------------- !
diff --git a/src/physics/cam/diffusion_solver.F90 b/src/physics/cam/diffusion_solver.F90
index 94fc4bc395..2a715596ea 100644
--- a/src/physics/cam/diffusion_solver.F90
+++ b/src/physics/cam/diffusion_solver.F90
@@ -368,8 +368,6 @@ end function vd_lu_qdecomp
! Combined molecular and eddy diffusion.
real(r8) :: kv_total(pcols,pver+1)
- logical :: use_spcam
-
!--------------------------------
! Variables needed for WACCM-X
!--------------------------------
@@ -389,8 +387,6 @@ end function vd_lu_qdecomp
! Main Computation Begins !
! ----------------------- !
- call phys_getopts(use_spcam_out = use_spcam)
-
errstring = ''
if( ( diffuse(fieldlist,'u') .or. diffuse(fieldlist,'v') ) .and. .not. diffuse(fieldlist,'s') ) then
errstring = 'diffusion_solver.compute_vdiff: must diffuse s if diffusing u or v'
@@ -719,16 +715,14 @@ end function vd_lu_qdecomp
! moist static energy,not the dry static energy.
if( diffuse(fieldlist,'s') ) then
- if (.not. use_spcam) then
! Add counter-gradient to input static energy profiles
+ do k = 1, pver
+ dse(:ncol,k) = dse(:ncol,k) + ztodt * p%rdel(:,k) * gravit * &
+ ( rhoi(:ncol,k+1) * kvh(:ncol,k+1) * cgh(:ncol,k+1) &
+ - rhoi(:ncol,k ) * kvh(:ncol,k ) * cgh(:ncol,k ) )
+ end do
- do k = 1, pver
- dse(:ncol,k) = dse(:ncol,k) + ztodt * p%rdel(:,k) * gravit * &
- ( rhoi(:ncol,k+1) * kvh(:ncol,k+1) * cgh(:ncol,k+1) &
- - rhoi(:ncol,k ) * kvh(:ncol,k ) * cgh(:ncol,k ) )
- end do
- endif
! Add the explicit surface fluxes to the lowest layer
dse(:ncol,pver) = dse(:ncol,pver) + tmp1(:ncol) * shflx(:ncol)
@@ -746,12 +740,10 @@ end function vd_lu_qdecomp
! Boundary layer thickness of "0._r8" signifies that the boundary
! condition is defined directly on the top interface.
- if (.not. use_spcam) then
- dse(:ncol,:) = fin_vol_solve(ztodt, p, dse(:ncol,:), ncol, pver, &
- coef_q_diff=kvh(:ncol,:)*dpidz_sq, &
- upper_bndry=interface_boundary, &
- l_cond=BoundaryData(dse_top(:ncol)))
- endif
+ dse(:ncol,:) = fin_vol_solve(ztodt, p, dse(:ncol,:), ncol, pver, &
+ coef_q_diff=kvh(:ncol,:)*dpidz_sq, &
+ upper_bndry=interface_boundary, &
+ l_cond=BoundaryData(dse_top(:ncol)))
! Calculate flux at top interface
@@ -764,11 +756,9 @@ end function vd_lu_qdecomp
ttemp = ttemp0
! upper boundary is zero flux for extended model
- if (.not. use_spcam) then
- ttemp = fin_vol_solve(ztodt, p, ttemp, ncol, pver, &
- coef_q_diff=kvt(:ncol,:)*dpidz_sq, &
- coef_q_weight=cpairv(:ncol,:))
- end if
+ ttemp = fin_vol_solve(ztodt, p, ttemp, ncol, pver, &
+ coef_q_diff=kvt(:ncol,:)*dpidz_sq, &
+ coef_q_weight=cpairv(:ncol,:))
!-------------------------------------
@@ -789,12 +779,10 @@ end function vd_lu_qdecomp
! Boundary layer thickness of "0._r8" signifies that the boundary
! condition is defined directly on the top interface.
- if (.not. use_spcam) then
- dse(:ncol,:) = fin_vol_solve(ztodt, p, dse(:ncol,:), ncol, pver, &
- coef_q_diff=kv_total(:ncol,:)*dpidz_sq, &
- upper_bndry=interface_boundary, &
- l_cond=BoundaryData(dse_top(:ncol)))
- end if
+ dse(:ncol,:) = fin_vol_solve(ztodt, p, dse(:ncol,:), ncol, pver, &
+ coef_q_diff=kv_total(:ncol,:)*dpidz_sq, &
+ upper_bndry=interface_boundary, &
+ l_cond=BoundaryData(dse_top(:ncol)))
! Calculate flux at top interface
@@ -826,27 +814,25 @@ end function vd_lu_qdecomp
do m = 1, ncnst
if( diffuse(fieldlist,'q',m) ) then
- if (.not. use_spcam) then
- ! Add the nonlocal transport terms to constituents in the PBL.
- ! Check for neg q's in each constituent and put the original vertical
- ! profile back if a neg value is found. A neg value implies that the
- ! quasi-equilibrium conditions assumed for the countergradient term are
- ! strongly violated.
+ ! Add the nonlocal transport terms to constituents in the PBL.
+ ! Check for neg q's in each constituent and put the original vertical
+ ! profile back if a neg value is found. A neg value implies that the
+ ! quasi-equilibrium conditions assumed for the countergradient term are
+ ! strongly violated.
- qtm(:ncol,:pver) = q(:ncol,:pver,m)
+ qtm(:ncol,:pver) = q(:ncol,:pver,m)
- do k = 1, pver
- q(:ncol,k,m) = q(:ncol,k,m) + &
- ztodt * p%rdel(:,k) * gravit * ( cflx(:ncol,m) * rrho(:ncol) ) * &
- ( rhoi(:ncol,k+1) * kvh(:ncol,k+1) * cgs(:ncol,k+1) &
- - rhoi(:ncol,k ) * kvh(:ncol,k ) * cgs(:ncol,k ) )
- end do
- lqtst(:ncol) = all(q(:ncol,1:pver,m) >= qmincg(m), 2)
- do k = 1, pver
- q(:ncol,k,m) = merge( q(:ncol,k,m), qtm(:ncol,k), lqtst(:ncol) )
- end do
- endif
+ do k = 1, pver
+ q(:ncol,k,m) = q(:ncol,k,m) + &
+ ztodt * p%rdel(:,k) * gravit * ( cflx(:ncol,m) * rrho(:ncol) ) * &
+ ( rhoi(:ncol,k+1) * kvh(:ncol,k+1) * cgs(:ncol,k+1) &
+ - rhoi(:ncol,k ) * kvh(:ncol,k ) * cgs(:ncol,k ) )
+ end do
+ lqtst(:ncol) = all(q(:ncol,1:pver,m) >= qmincg(m), 2)
+ do k = 1, pver
+ q(:ncol,k,m) = merge( q(:ncol,k,m), qtm(:ncol,k), lqtst(:ncol) )
+ end do
! Add the explicit surface fluxes to the lowest layer
@@ -894,9 +880,7 @@ end function vd_lu_qdecomp
endif
end if
- if (.not. use_spcam) then
- call no_molec_decomp%left_div(q(:ncol,:,m))
- end if
+ call no_molec_decomp%left_div(q(:ncol,:,m))
end if
diff --git a/src/physics/cam/microp_driver.F90 b/src/physics/cam/microp_driver.F90
index baf11c4a9e..b328e3a670 100644
--- a/src/physics/cam/microp_driver.F90
+++ b/src/physics/cam/microp_driver.F90
@@ -50,7 +50,7 @@ subroutine microp_driver_readnl(nlfile)
select case (microp_scheme)
case ('MG')
call micro_pumas_cam_readnl(nlfile)
- case ('NONE', 'RK', 'SPCAM_sam1mom', 'SPCAM_m2005')
+ case ('NONE', 'RK')
continue
case default
call endrun('microp_driver_readnl:: unrecognized microp_scheme, "'//trim(microp_scheme)//'"')
@@ -95,7 +95,7 @@ function microp_driver_implements_cnst(name)
select case (microp_scheme)
case ('MG')
microp_driver_implements_cnst = micro_pumas_cam_implements_cnst(name)
- case ('NONE', 'RK', 'SPCAM_sam1mom', 'SPCAM_m2005')
+ case ('NONE', 'RK')
continue
case default
call endrun('microp_driver_implements_cnst:: unrecognized microp_scheme, '//trim(microp_scheme))
@@ -123,12 +123,6 @@ subroutine microp_driver_init_cnst(name, latvals, lonvals, mask, q)
case ('RK')
! microp_driver doesn't handle this one
continue
- case ('SPCAM_m2005')
- ! microp_driver doesn't handle this one
- continue
- case ('SPCAM_sam1mom')
- ! microp_driver doesn't handle this one
- continue
case default
call endrun('microp_driver_init_cnst:: unrecognized microp_scheme'//trim(microp_scheme))
end select
diff --git a/src/physics/cam/ndrop.F90 b/src/physics/cam/ndrop.F90
index ea3d7522da..3a2bed88c3 100644
--- a/src/physics/cam/ndrop.F90
+++ b/src/physics/cam/ndrop.F90
@@ -173,7 +173,7 @@ end subroutine ndrop_init
subroutine dropmixnuc( aero_props, aero_state, &
state, ptend, dtmicro, pbuf, wsub, wmixmin, &
- cldn, cldo, cldliqf, tendnd, factnum, from_spcam)
+ cldn, cldo, cldliqf, tendnd, factnum)
! vertical diffusion and nucleation of cloud droplets
! assume cloud presence controlled by cloud fraction
@@ -195,7 +195,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
real(r8), intent(in) :: cldn(pcols,pver) ! cloud fraction
real(r8), intent(in) :: cldo(pcols,pver) ! cloud fraction on previous time step
real(r8), intent(in) :: cldliqf(pcols,pver) ! liquid cloud fraction (liquid / (liquid + ice))
- logical, intent(in),optional :: from_spcam ! value insignificant - if variable present, is called from spcam
! output arguments
real(r8), intent(out) :: tendnd(pcols,pver) ! change in droplet number concentration (#/kg/s)
@@ -311,7 +310,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
real(r8) :: zerogas(pver)
character*200 fieldnamegas
- logical :: called_from_spcam
integer :: errnum
character(len=shr_kind_cs) :: errstr
!-------------------------------------------------------------------------------
@@ -374,14 +372,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
! intersitial and cloud borne phases.
call aero_state%get_states( aero_props, raer, qqcw )
- called_from_spcam = (present(from_spcam))
-
- if (called_from_spcam) then
- rgas => state%q
- allocate(rgascol(pver, pcnst, 2))
- allocate(coltendgas(pcols))
- endif
-
factnum = 0._r8
wtke = 0._r8
nsource = 0._r8
@@ -450,31 +440,12 @@ subroutine dropmixnuc( aero_props, aero_state, &
raercol(top_lev:pver,mm,nsav) = raer(mm)%fld(i,top_lev:pver)
end do
- if (called_from_spcam) then
- !
- ! In the MMF model, turbulent mixing for tracer species are turned off.
- ! So the turbulent for gas species mixing are added here.
- ! (Previously, it had the turbulent mixing for aerosol species)
- !
- do m=1, pcnst
- if (cnst_species_class(m) == cnst_spec_class_gas) rgascol(:,m,nsav) = rgas(i,:,m)
- end do
-
- endif
-
! droplet nucleation/aerosol activation
! tau_cld_regenerate = time scale for regeneration of cloudy air
! by (horizontal) exchange with clear air
tau_cld_regenerate = 3600.0_r8 * 3.0_r8
- if (called_from_spcam) then
- ! when this is called in the MMF part, no cloud regeneration and decay.
- ! set the time scale be very long so that no cloud regeneration.
- tau_cld_regenerate = 3600.0_r8 * 24.0_r8 * 365.0_r8
- endif
-
-
! k-loop for growing/shrinking cloud calcs .............................
! grow_shrink_main_k_loop: &
do k = top_lev, pver
@@ -919,21 +890,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
end do
end do
- if (called_from_spcam) then
- !
- ! turbulent mixing for gas species .
- !
- do m=1, pcnst
- if (cnst_species_class(m) == cnst_spec_class_gas) then
- flxconv = 0.0_r8
- zerogas(:) = 0.0_r8
- call explmix(rgascol(1,m,nnew),zerogas,ekkp,ekkm,overlapp,overlapm, &
- rgascol(1,m,nsav),zero, flxconv, pver,dtmix,&
- .true., zerogas)
- end if
- end do
- endif
-
end do ! old_cloud_nsubmix_loop
! evaporate particles again if no cloud (either ice or liquid)
@@ -992,18 +948,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
end do
end do
- if (called_from_spcam) then
- !
- ! Gas tendency
- !
- do m=1, pcnst
- if (cnst_species_class(m) == cnst_spec_class_gas) then
- ptend%lq(m) = .true.
- ptend%q(i, :, m) = (rgascol(:,m,nnew)-rgas(i,:,m)) * dtinv
- end if
- end do
- endif
-
end do ! overall_main_i_loop
! end of main loop over i/longitude ....................................
@@ -1012,11 +956,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
call outfld('NDROPMIX', ndropmix, pcols, lchnk)
call outfld('WTKE ', wtke, pcols, lchnk)
- if(called_from_spcam) then
- call outfld('SPLCLOUD ', cldn , pcols, lchnk )
- call outfld('SPKVH ', kvh , pcols, lchnk )
- endif
-
call ccncalc(aero_state, aero_props, state, cs, ccn)
do l = 1, psat
call outfld(ccn_name(l), ccn(1,1,l), pcols, lchnk)
@@ -1031,22 +970,6 @@ subroutine dropmixnuc( aero_props, aero_state, &
end do
end do
- if(called_from_spcam) then
- !
- ! output column-integrated Gas tendency (this should be zero)
- !
- do m=1, pcnst
- if (cnst_species_class(m) == cnst_spec_class_gas) then
- do i=1, ncol
- coltendgas(i) = sum( pdel(i,:)*ptend%q(i,:,m) )/gravit
- end do
- fieldnamegas = trim(cnst_name(m)) // '_mixnuc1sp'
- call outfld( trim(fieldnamegas), coltendgas, pcols, lchnk)
- end if
- end do
- deallocate(rgascol, coltendgas)
- end if
-
deallocate( &
nact, &
mact, &
diff --git a/src/physics/cam/phys_control.F90 b/src/physics/cam/phys_control.F90
index 7105f2d6cd..0ad08646ce 100644
--- a/src/physics/cam/phys_control.F90
+++ b/src/physics/cam/phys_control.F90
@@ -85,8 +85,6 @@ module phys_control
logical, public, protected :: use_simple_phys = .false. ! true => simple physics configuration
-logical :: use_spcam ! true => use super parameterized CAM
-
logical :: prog_modal_aero ! determines whether prognostic modal aerosols are present in the run.
! Option to use heterogeneous freezing
@@ -206,9 +204,6 @@ subroutine phys_ctl_readnl(nlfile)
call mpi_bcast(do_hb_above_clubb, 1, mpi_logical, masterprocid, mpicom, ierr)
call mpi_bcast(use_hemco, 1, mpi_logical, masterprocid, mpicom, ierr)
- use_spcam = ( cam_physpkg_is('spcam_sam1mom') &
- .or. cam_physpkg_is('spcam_m2005'))
-
call cam_ctrl_set_physics_type(cam_physpkg)
! Error checking:
@@ -231,13 +226,13 @@ subroutine phys_ctl_readnl(nlfile)
endif
! Add a check to make sure CLUBB and MG are used together
- if ( do_clubb_sgs .and. ( microp_scheme .ne. 'MG') .and. .not. use_spcam) then
+ if ( do_clubb_sgs .and. microp_scheme .ne. 'MG') then
write(iulog,*)'CLUBB is only compatible with MG microphysics. Quiting'
call endrun('CLUBB and microphysics schemes incompatible')
endif
! Check that eddy_scheme, macrop_scheme, shallow_scheme are all set to CLUBB_SGS if do_clubb_sgs is true
- if (do_clubb_sgs .and. .not. use_spcam) then
+ if (do_clubb_sgs) then
if (eddy_scheme .ne. 'CLUBB_SGS' .or. macrop_scheme .ne. 'CLUBB_SGS' .or. shallow_scheme .ne. 'CLUBB_SGS') then
write(iulog,*)'eddy_scheme, macrop_scheme and shallow_scheme must all be CLUBB_SGS. Quiting'
call endrun('CLUBB and eddy, macrop or shallow schemes incompatible')
@@ -250,11 +245,6 @@ subroutine phys_ctl_readnl(nlfile)
write(iulog,*) 'cam7 is only compatible with CLUBB. Quitting'
call endrun('cam7 is only compatible with eddy, macrop, and shallow schemes = CLUBB_SGS')
end if
- ! Add a check to make sure SPCAM is not used
- if (use_spcam) then
- write(iulog,*)'SPCAM not compatible with cam7 physics. Quitting'
- call endrun('SPCAM and cam7 incompatible')
- end if
! Add check to make sure we are not trying to use `camrt`
if (trim(radiation_scheme) == 'camrt') then
write(iulog,*) ' camrt specified and it is not compatible with cam7'
@@ -324,7 +314,7 @@ subroutine phys_getopts(deep_scheme_out, shallow_scheme_out, eddy_scheme_out, mi
history_carma_out, history_clubb_out, history_dust_out, &
history_cesm_forcing_out, history_scwaccm_forcing_out, history_chemspecies_srf_out, &
cam_chempkg_out, prog_modal_aero_out, macrop_scheme_out, &
- do_clubb_sgs_out, use_spcam_out, state_debug_checks_out, cld_macmic_num_steps_out, &
+ do_clubb_sgs_out, state_debug_checks_out, cld_macmic_num_steps_out, &
offline_driver_out, convproc_do_aer_out, cam_snapshot_before_num_out, cam_snapshot_after_num_out,&
cam_take_snapshot_before_out, cam_take_snapshot_after_out, physics_grid_out, do_hb_above_clubb_out)
!-----------------------------------------------------------------------
@@ -334,7 +324,6 @@ subroutine phys_getopts(deep_scheme_out, shallow_scheme_out, eddy_scheme_out, mi
! eddy_scheme_out : vertical diffusion scheme
! microp_scheme_out : microphysics scheme
! radiation_scheme_out : radiation_scheme
-! SPCAM_microp_scheme_out : SPCAM microphysics scheme
!-----------------------------------------------------------------------
character(len=16), intent(out), optional :: deep_scheme_out
@@ -344,7 +333,6 @@ subroutine phys_getopts(deep_scheme_out, shallow_scheme_out, eddy_scheme_out, mi
character(len=16), intent(out), optional :: radiation_scheme_out
character(len=16), intent(out), optional :: macrop_scheme_out
logical, intent(out), optional :: use_subcol_microp_out
- logical, intent(out), optional :: use_spcam_out
logical, intent(out), optional :: atm_dep_flux_out
logical, intent(out), optional :: history_amwg_out
logical, intent(out), optional :: history_vdiag_out
@@ -382,7 +370,6 @@ subroutine phys_getopts(deep_scheme_out, shallow_scheme_out, eddy_scheme_out, mi
if ( present(microp_scheme_out ) ) microp_scheme_out = microp_scheme
if ( present(radiation_scheme_out ) ) radiation_scheme_out = radiation_scheme
if ( present(use_subcol_microp_out ) ) use_subcol_microp_out = use_subcol_microp
- if ( present(use_spcam_out ) ) use_spcam_out = use_spcam
if ( present(macrop_scheme_out ) ) macrop_scheme_out = macrop_scheme
if ( present(atm_dep_flux_out ) ) atm_dep_flux_out = atm_dep_flux
diff --git a/src/physics/cam/physpkg.F90 b/src/physics/cam/physpkg.F90
index c9822b18fc..5aa84b55a4 100644
--- a/src/physics/cam/physpkg.F90
+++ b/src/physics/cam/physpkg.F90
@@ -155,7 +155,6 @@ subroutine phys_register
use subcol, only: subcol_register
use subcol_utils, only: is_subcol_on, subcol_get_scheme
use dyn_comp, only: dyn_register
- use spcam_drivers, only: spcam_register
use offline_driver, only: offline_driver_reg
use hemco_interface, only: HCOI_Chunk_Init
use upper_bc, only: ubc_fixed_conc
@@ -309,9 +308,6 @@ subroutine phys_register
! shallow convection
call convect_shallow_register
-
- call spcam_register
-
! radiation
call radiation_register
call cloud_diagnostics_register
@@ -743,7 +739,6 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
use microp_aero, only: microp_aero_init
use macrop_driver, only: macrop_driver_init
use conv_water, only: conv_water_init
- use spcam_drivers, only: spcam_init
use tracers, only: tracers_init
use aoa_tracers, only: aoa_tracers_init
use rayleigh_friction, only: rayleigh_friction_init
@@ -911,16 +906,11 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
call microp_aero_init(phys_state,pbuf2d)
call microp_driver_init(pbuf2d)
call conv_water_init
- elseif( microp_scheme == 'SPCAM_m2005') then
- call conv_water_init
end if
-
! initiate CLUBB within CAM
if (do_clubb_sgs) call clubb_ini_cam(pbuf2d)
- call spcam_init(pbuf2d)
-
call qbo_init
call lunar_tides_init()
@@ -1069,7 +1059,6 @@ subroutine phys_run1(phys_state, ztodt, phys_tend, pbuf2d, cam_in, cam_out)
use cam_diagnostics,only: diag_allocate, diag_physvar_ic
use check_energy, only: check_energy_gmean
use phys_control, only: phys_getopts
- use spcam_drivers, only: tphysbc_spcam
use spmd_utils, only: mpicom
use physics_buffer, only: physics_buffer_desc, pbuf_get_chunk, pbuf_allocate
use cam_history, only: outfld, write_camiop
@@ -1096,7 +1085,6 @@ subroutine phys_run1(phys_state, ztodt, phys_tend, pbuf2d, cam_in, cam_out)
!
integer :: c ! indices
integer :: nstep ! current timestep number
- logical :: use_spcam
type(physics_buffer_desc), pointer :: phys_buffer_chunk(:)
call t_startf ('physpkg_st1')
@@ -1150,8 +1138,6 @@ subroutine phys_run1(phys_state, ztodt, phys_tend, pbuf2d, cam_in, cam_out)
call t_startf ('bc_physics')
call t_adj_detailf(+1)
- call phys_getopts( use_spcam_out = use_spcam)
-
!$OMP PARALLEL DO PRIVATE (C, phys_buffer_chunk)
do c=begchunk, endchunk
!
@@ -1163,16 +1149,8 @@ subroutine phys_run1(phys_state, ztodt, phys_tend, pbuf2d, cam_in, cam_out)
call diag_physvar_ic ( c, phys_buffer_chunk, cam_out(c), cam_in(c) )
call t_stopf ('diag_physvar_ic')
- if (use_spcam) then
- call tphysbc_spcam (ztodt, phys_state(c), &
- phys_tend(c), phys_buffer_chunk, &
- cam_out(c), cam_in(c) )
- else
- call tphysbc (ztodt, phys_state(c), &
- phys_tend(c), phys_buffer_chunk, &
- cam_out(c), cam_in(c) )
- end if
-
+ call tphysbc(ztodt, phys_state(c), phys_tend(c), phys_buffer_chunk, &
+ cam_out(c), cam_in(c) )
end do
call t_adj_detailf(-1)
diff --git a/src/physics/cam/pkg_cldoptics.F90 b/src/physics/cam/pkg_cldoptics.F90
index aa40dae6c3..221184a301 100644
--- a/src/physics/cam/pkg_cldoptics.F90
+++ b/src/physics/cam/pkg_cldoptics.F90
@@ -118,7 +118,7 @@ subroutine cldems(lchnk ,ncol ,clwp ,fice ,rei ,emis ,cldtau)
!note that optical properties for ice valid only
!in range of 13 > rei > 130 micron (Ebert and Curry 92)
- if ( microp_scheme == 'RK' .or. microp_scheme == 'SPCAM_sam1mom') then
+ if ( microp_scheme == 'RK') then
kabsi = 0.005_r8 + 1._r8/rei(i,k)
else
kabsi = 0.005_r8 + 1._r8/min(max(13._r8,rei(i,k)),130._r8)
diff --git a/src/physics/cam/spcam_drivers.F90 b/src/physics/cam/spcam_drivers.F90
deleted file mode 100644
index d44c1db730..0000000000
--- a/src/physics/cam/spcam_drivers.F90
+++ /dev/null
@@ -1,54 +0,0 @@
-module spcam_drivers
-
-! stub module
-
-use shr_kind_mod, only: r8 => shr_kind_r8
-use physics_types, only: physics_state, physics_tend
-use physics_buffer, only: physics_buffer_desc
-use camsrfexch, only: cam_out_t, cam_in_t
-use cam_abortutils, only: endrun
-
-implicit none
-private
-save
-
-public :: tphysbc_spcam, spcam_register, spcam_init
-
-!========================================================================================
-contains
-!========================================================================================
-
-subroutine tphysbc_spcam (ztodt, state, &
- tend, pbuf, &
- cam_out, cam_in )
-
- real(r8), intent(in) :: ztodt
- type(physics_state), intent(inout) :: state
- type(physics_tend ), intent(inout) :: tend
- type(physics_buffer_desc), pointer :: pbuf(:)
- type(cam_out_t), intent(inout) :: cam_out
- type(cam_in_t), intent(in) :: cam_in
- !---------------------------------------------------------------------------
-
- call endrun('tphysbc_spcam: ERROR: this is a stub')
-
-end subroutine tphysbc_spcam
-
-!========================================================================================
-
-subroutine spcam_register()
-
-end subroutine spcam_register
-
-!========================================================================================
-
-subroutine spcam_init(pbuf2d)
-
- type(physics_buffer_desc), pointer :: pbuf2d(:,:)
-
-end subroutine spcam_init
-
-!========================================================================================
-
-end module spcam_drivers
-
diff --git a/src/physics/cam/vertical_diffusion.F90 b/src/physics/cam/vertical_diffusion.F90
index c5518f94fc..efd571269a 100644
--- a/src/physics/cam/vertical_diffusion.F90
+++ b/src/physics/cam/vertical_diffusion.F90
@@ -195,7 +195,7 @@ subroutine vd_readnl(nlfile)
! Beljaars reads its own namelist.
call beljaars_drag_readnl(nlfile)
- if (eddy_scheme == 'diag_TKE' .or. eddy_scheme == 'SPCAM_m2005' ) call eddy_diff_readnl(nlfile)
+ if (eddy_scheme == 'diag_TKE') call eddy_diff_readnl(nlfile)
end subroutine vd_readnl
@@ -240,7 +240,7 @@ subroutine vd_register()
end if
! diag_TKE fields
- if (eddy_scheme == 'diag_TKE' .or. eddy_scheme == 'SPCAM_m2005') then
+ if (eddy_scheme == 'diag_TKE') then
call eddy_diff_register()
end if
@@ -433,11 +433,11 @@ subroutine vertical_diffusion_init(pbuf2d)
call phys_getopts(do_hb_above_clubb_out=do_hb_above_clubb)
select case ( eddy_scheme )
- case ( 'diag_TKE', 'SPCAM_m2005' )
+ case ( 'diag_TKE' )
if( masterproc ) write(iulog,*) &
'vertical_diffusion_init: eddy_diffusivity scheme: UW Moist Turbulence Scheme by Bretherton and Park'
call eddy_diff_init(pbuf2d, ntop_eddy, nbot_eddy)
- case ( 'HB', 'HBR', 'SPCAM_sam1mom')
+ case ( 'HB', 'HBR')
if( masterproc ) write(iulog,*) 'vertical_diffusion_init: eddy_diffusivity scheme: Holtslag and Boville'
call init_hb_diff(gravit, cpair, ntop_eddy, nbot_eddy, pref_mid, &
karman, eddy_scheme)
@@ -1010,7 +1010,7 @@ subroutine vertical_diffusion_tend( &
th(:ncol,:pver) = state%t(:ncol,:pver) * state%exner(:ncol,:pver)
select case (eddy_scheme)
- case ( 'diag_TKE', 'SPCAM_m2005' )
+ case ( 'diag_TKE' )
call eddy_diff_tend(state, pbuf, cam_in, &
ztodt, p, tint, rhoi, cldn, wstarent, &
@@ -1026,7 +1026,7 @@ subroutine vertical_diffusion_tend( &
khfs(:ncol), kqfs(:ncol), kbfs(:ncol), obklen(:ncol))
- case ( 'HB', 'HBR', 'SPCAM_sam1mom' )
+ case ( 'HB', 'HBR' )
! Modification : We may need to use 'taux' instead of 'tautotx' here, for
! consistency with the previous HB scheme.
@@ -1389,7 +1389,7 @@ subroutine vertical_diffusion_tend( &
! !
! ------------------------------------------------------------ !
- if( (eddy_scheme .eq. 'diag_TKE' .or. eddy_scheme .eq. 'SPCAM_m2005') .and. do_pseudocon_diff ) then
+ if( eddy_scheme .eq. 'diag_TKE' .and. do_pseudocon_diff ) then
ptend%q(:ncol,:pver,1) = qtten(:ncol,:pver)
ptend%s(:ncol,:pver) = slten(:ncol,:pver)
diff --git a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar.F90 b/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar.F90
deleted file mode 100644
index df9574cf4b..0000000000
--- a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar.F90
+++ /dev/null
@@ -1,47 +0,0 @@
-
-subroutine advect_scalar (f,fadv,flux,f2leadv,f2legrad,fwleadv,doit)
-
-! positively definite monotonic advection with non-oscillatory option
-
-use crmx_grid
-use crmx_vars, only: u, v, w, rho, rhow
-use crmx_params, only: docolumn
-
-implicit none
-
-real f(dimx1_s:dimx2_s, dimy1_s:dimy2_s, nzm)
-real flux(nz), fadv(nz)
-real f2leadv(nz),f2legrad(nz),fwleadv(nz)
-logical doit
-
-real df(dimx1_s:dimx2_s, dimy1_s:dimy2_s, nzm)
-integer i,j,k
-
-if(docolumn) then
- flux = 0.
- return
-end if
-
-!call t_startf ('advect_scalars')
-
- df(:,:,:) = f(:,:,:)
-
-if(RUN3D) then
- call advect_scalar3D(f, u, v, w, rho, rhow, flux)
-else
- call advect_scalar2D(f, u, w, rho, rhow, flux)
-endif
-
- do k=1,nzm
- fadv(k)=0.
- do j=1,ny
- do i=1,nx
- fadv(k)=fadv(k)+f(i,j,k)-df(i,j,k)
- end do
- end do
- end do
-
-!call t_stopf ('advect_scalars')
-
-end subroutine advect_scalar
-
diff --git a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar2D.F90 b/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar2D.F90
deleted file mode 100644
index a3773aa1ca..0000000000
--- a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar2D.F90
+++ /dev/null
@@ -1,182 +0,0 @@
-
-subroutine advect_scalar2D (f, u, w, rho, rhow, flux)
-
-! positively definite monotonic advection with non-oscillatory option
-
-use crmx_grid
-use crmx_params, only: dowallx
-implicit none
-
-
-real f(dimx1_s:dimx2_s, dimy1_s:dimy2_s, nzm)
-real u(dimx1_u:dimx2_u, dimy1_u:dimy2_u, nzm)
-real w(dimx1_w:dimx2_w, dimy1_w:dimy2_w, nz )
-real rho(nzm)
-real rhow(nz)
-real flux(nz)
-
-real mx (0:nxp1,1,nzm)
-real mn (0:nxp1,1,nzm)
-real uuu(-1:nxp3,1,nzm)
-real www(-1:nxp2,1,nz)
-
-real eps, dd
-integer i,j,k,ic,ib,kc,kb
-logical nonos
-real iadz(nzm),irho(nzm),irhow(nzm)
-
-real x1, x2, a, b, a1, a2, y
-real andiff,across,pp,pn
-andiff(x1,x2,a,b)=(abs(a)-a*a*b)*0.5*(x2-x1)
-across(x1,a1,a2)=0.03125*a1*a2*x1
-pp(y)= max(0.,y)
-pn(y)=-min(0.,y)
-
-nonos = .true.
-eps = 1.e-10
-
-j=1
-
-www(:,:,nz)=0.
-
-if(dowallx) then
-
- if(mod(rank,nsubdomains_x).eq.0) then
- do k=1,nzm
- do i=dimx1_u,1
- u(i,j,k) = 0.
- end do
- end do
- end if
- if(mod(rank,nsubdomains_x).eq.nsubdomains_x-1) then
- do k=1,nzm
- do i=nx+1,dimx2_u
- u(i,j,k) = 0.
- end do
- end do
- end if
-
-end if
-
-!-----------------------------------------
-
-if(nonos) then
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- do i=0,nxp1
- ib=i-1
- ic=i+1
- mx(i,j,k)=max(f(ib,j,k),f(ic,j,k),f(i,j,kb),f(i,j,kc),f(i,j,k))
- mn(i,j,k)=min(f(ib,j,k),f(ic,j,k),f(i,j,kb),f(i,j,kc),f(i,j,k))
- end do
- end do
-
-end if ! nonos
-
-do k=1,nzm
- kb=max(1,k-1)
- do i=-1,nxp3
- uuu(i,j,k)=max(0.,u(i,j,k))*f(i-1,j,k)+min(0.,u(i,j,k))*f(i,j,k)
- end do
- do i=-1,nxp2
- www(i,j,k)=max(0.,w(i,j,k))*f(i,j,kb)+min(0.,w(i,j,k))*f(i,j,k)
- end do
- flux(k) = 0.
- do i=1,nx
- flux(k) = flux(k) + www(i,j,k)
- end do
-end do
-
-do k=1,nzm
- irho(k) = 1./rho(k)
- iadz(k) = 1./adz(k)
- do i=-1,nxp2
- f(i,j,k) = f(i,j,k) - (uuu(i+1,j,k)-uuu(i,j,k) &
- + (www(i,j,k+1)-www(i,j,k))*iadz(k))*irho(k)
- end do
-end do
-
-
-do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- dd=2./(kc-kb)/adz(k)
- irhow(k)=1./(rhow(k)*adz(k))
- do i=0,nxp2
- ib=i-1
- uuu(i,j,k)=andiff(f(ib,j,k),f(i,j,k),u(i,j,k),irho(k)) &
- - across(dd*(f(ib,j,kc)+f(i,j,kc)-f(ib,j,kb)-f(i,j,kb)), &
- u(i,j,k), w(ib,j,k)+w(ib,j,kc)+w(i,j,k)+w(i,j,kc)) *irho(k)
- end do
-
-
- do i=0,nxp1
- ib=i-1
- ic=i+1
- www(i,j,k)=andiff(f(i,j,kb),f(i,j,k),w(i,j,k),irhow(k)) &
- -across(f(ic,j,kb)+f(ic,j,k)-f(ib,j,kb)-f(ib,j,k), &
- w(i,j,k), u(i,j,kb)+u(i,j,k)+u(ic,j,k)+u(ic,j,kb)) *irho(k)
- end do
-end do
-www(:,:,1) = 0.
-!---------- non-osscilatory option ---------------
-
-if(nonos) then
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- do i=0,nxp1
- ib=i-1
- ic=i+1
- mx(i,j,k)=max(f(ib,j,k),f(ic,j,k),f(i,j,kb),f(i,j,kc),f(i,j,k),mx(i,j,k))
- mn(i,j,k)=min(f(ib,j,k),f(ic,j,k),f(i,j,kb),f(i,j,kc),f(i,j,k),mn(i,j,k))
- end do
- end do
-
- do k=1,nzm
- kc=min(nzm,k+1)
- do i=0,nxp1
- ic=i+1
- mx(i,j,k)=rho(k)*(mx(i,j,k)-f(i,j,k))/(pn(uuu(ic,j,k)) + pp(uuu(i,j,k))+&
- iadz(k)*(pn(www(i,j,kc)) + pp(www(i,j,k)))+eps)
- mn(i,j,k)=rho(k)*(f(i,j,k)-mn(i,j,k))/(pp(uuu(ic,j,k)) + pn(uuu(i,j,k))+&
- iadz(k)*(pp(www(i,j,kc)) + pn(www(i,j,k)))+eps)
- end do
- end do
-
- do k=1,nzm
- kb=max(1,k-1)
- do i=1,nxp1
- ib=i-1
- uuu(i,j,k)= pp(uuu(i,j,k))*min(1.,mx(i,j,k), mn(ib,j,k)) &
- - pn(uuu(i,j,k))*min(1.,mx(ib,j,k),mn(i,j,k))
- end do
- do i=1,nx
- www(i,j,k)= pp(www(i,j,k))*min(1.,mx(i,j,k), mn(i,j,kb)) &
- - pn(www(i,j,k))*min(1.,mx(i,j,kb),mn(i,j,k))
- flux(k) = flux(k) + www(i,j,k)
- end do
- end do
-
-
-endif ! nonos
-
-
- do k=1,nzm
- kc=k+1
- do i=1,nx
- ! MK: added fix for very small negative values (relative to positive values)
- ! especially when such large numbers as
- ! hydrometeor concentrations are advected. The reason for negative values is
- ! most likely truncation error.
- f(i,j,k)= max(0., f(i,j,k) - (uuu(i+1,j,k)-uuu(i,j,k) &
- +(www(i,j,k+1)-www(i,j,k))*iadz(k))*irho(k))
- end do
- end do
-
-end subroutine advect_scalar2D
-
-
diff --git a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar3D.F90 b/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar3D.F90
deleted file mode 100644
index cd66086006..0000000000
--- a/src/physics/spcam/crm/ADV_MPDATA/crmx_advect_scalar3D.F90
+++ /dev/null
@@ -1,302 +0,0 @@
-
-subroutine advect_scalar3D (f, u, v, w, rho, rhow, flux)
-
-! positively definite monotonic advection with non-oscillatory option
-
-use crmx_grid
-use crmx_params, only: dowallx, dowally
-implicit none
-
-
-real f(dimx1_s:dimx2_s, dimy1_s:dimy2_s, nzm)
-real u(dimx1_u:dimx2_u, dimy1_u:dimy2_u, nzm)
-real v(dimx1_v:dimx2_v, dimy1_v:dimy2_v, nzm)
-real w(dimx1_w:dimx2_w, dimy1_w:dimy2_w, nz )
-real rho(nzm)
-real rhow(nz)
-real flux(nz)
-
-real mx (0:nxp1,0:nyp1,nzm)
-real mn (0:nxp1,0:nyp1,nzm)
-real uuu(-1:nxp3,-1:nyp2,nzm)
-real vvv(-1:nxp2,-1:nyp3,nzm)
-real www(-1:nxp2,-1:nyp2,nz)
-
-real eps, dd
-real iadz(nzm),irho(nzm),irhow(nzm)
-integer i,j,k,ic,ib,jc,jb,kc,kb
-logical nonos
-
-real x1, x2, a, b, a1, a2, y
-real andiff,across,pp,pn
-andiff(x1,x2,a,b)=(abs(a)-a*a*b)*0.5*(x2-x1)
-across(x1,a1,a2)=0.03125*a1*a2*x1
-pp(y)= max(0.,y)
-pn(y)=-min(0.,y)
-
-nonos = .true.
-eps = 1.e-10
-
-www(:,:,nz)=0.
-
-if(dowallx) then
-
- if(mod(rank,nsubdomains_x).eq.0) then
- do k=1,nzm
- do j=dimy1_u,dimy2_u
- do i=dimx1_u,1
- u(i,j,k) = 0.
- end do
- end do
- end do
- end if
- if(mod(rank,nsubdomains_x).eq.nsubdomains_x-1) then
- do k=1,nzm
- do j=dimy1_u,dimy2_u
- do i=nx+1,dimx2_u
- u(i,j,k) = 0.
- end do
- end do
- end do
- end if
-
-end if
-
-if(dowally) then
-
- if(rank.lt.nsubdomains_x) then
- do k=1,nzm
- do j=dimy1_v,1
- do i=dimx1_v,dimx2_v
- v(i,j,k) = 0.
- end do
- end do
- end do
- end if
- if(rank.gt.nsubdomains-nsubdomains_x-1) then
- do k=1,nzm
- do j=ny+1,dimy2_v
- do i=dimx1_v,dimx2_v
- v(i,j,k) = 0.
- end do
- end do
- end do
- end if
-
-end if
-
-!-----------------------------------------
-
-if(nonos) then
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- do j=0,nyp1
- jb=j-1
- jc=j+1
- do i=0,nxp1
- ib=i-1
- ic=i+1
- mx(i,j,k)=max(f(ib,j,k),f(ic,j,k),f(i,jb,k), &
- f(i,jc,k),f(i,j,kb),f(i,j,kc),f(i,j,k))
- mn(i,j,k)=min(f(ib,j,k),f(ic,j,k),f(i,jb,k), &
- f(i,jc,k),f(i,j,kb),f(i,j,kc),f(i,j,k))
- end do
- end do
- end do
-
-end if ! nonos
-
- do k=1,nzm
- do j=-1,nyp2
- do i=-1,nxp3
- uuu(i,j,k)=max(0.,u(i,j,k))*f(i-1,j,k)+min(0.,u(i,j,k))*f(i,j,k)
- end do
- end do
- end do
-
- do k=1,nzm
- do j=-1,nyp3
- do i=-1,nxp2
- vvv(i,j,k)=max(0.,v(i,j,k))*f(i,j-1,k)+min(0.,v(i,j,k))*f(i,j,k)
- end do
- end do
- end do
-
- do k=1,nzm
- kb=max(1,k-1)
- do j=-1,nyp2
- do i=-1,nxp2
- www(i,j,k)=max(0.,w(i,j,k))*f(i,j,kb)+min(0.,w(i,j,k))*f(i,j,k)
- end do
- end do
- flux(k) = 0.
- do j=1,ny
- do i=1,nx
- flux(k) = flux(k) + www(i,j,k)
- end do
- end do
- end do
-
-
- do k=1,nzm
- irho(k) = 1./rho(k)
- iadz(k) = 1./adz(k)
- do j=-1,nyp2
- do i=-1,nxp2
- f(i,j,k)=f(i,j,k) -(uuu(i+1,j,k)-uuu(i,j,k)+vvv(i,j+1,k)-vvv(i,j,k) &
- +(www(i,j,k+1)-www(i,j,k))*iadz(k))*irho(k)
- end do
- end do
- end do
-
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- dd=2./(kc-kb)/adz(k)
- do j=0,nyp1
- jb=j-1
- jc=j+1
- do i=0,nxp2
- ib=i-1
- uuu(i,j,k)=andiff(f(ib,j,k),f(i,j,k),u(i,j,k),irho(k)) &
- -(across(f(ib,jc,k)+f(i,jc,k)-f(ib,jb,k)-f(i,jb,k), &
- u(i,j,k), v(ib,j,k)+v(ib,jc,k)+v(i,jc,k)+v(i,j,k)) &
- +across(dd*(f(ib,j,kc)+f(i,j,kc)-f(ib,j,kb)-f(i,j,kb)), &
- u(i,j,k), w(ib,j,k)+w(ib,j,kc)+w(i,j,k)+w(i,j,kc))) *irho(k)
- end do
- end do
- end do
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- dd=2./(kc-kb)/adz(k)
- do j=0,nyp2
- jb=j-1
- do i=0,nxp1
- ib=i-1
- ic=i+1
- vvv(i,j,k)=andiff(f(i,jb,k),f(i,j,k),v(i,j,k),irho(k)) &
- -(across(f(ic,jb,k)+f(ic,j,k)-f(ib,jb,k)-f(ib,j,k), &
- v(i,j,k), u(i,jb,k)+u(i,j,k)+u(ic,j,k)+u(ic,jb,k)) &
- +across(dd*(f(i,jb,kc)+f(i,j,kc)-f(i,jb,kb)-f(i,j,kb)), &
- v(i,j,k), w(i,jb,k)+w(i,j,k)+w(i,j,kc)+w(i,jb,kc))) *irho(k)
- end do
- end do
- end do
-
- do k=1,nzm
- kb=max(1,k-1)
- irhow(k)=1./(rhow(k)*adz(k))
- do j=0,nyp1
- jb=j-1
- jc=j+1
- do i=0,nxp1
- ib=i-1
- ic=i+1
- www(i,j,k)=andiff(f(i,j,kb),f(i,j,k),w(i,j,k),irhow(k)) &
- -(across(f(ic,j,kb)+f(ic,j,k)-f(ib,j,kb)-f(ib,j,k), &
- w(i,j,k), u(i,j,kb)+u(i,j,k)+u(ic,j,k)+u(ic,j,kb)) &
- +across(f(i,jc,k)+f(i,jc,kb)-f(i,jb,k)-f(i,jb,kb), &
- w(i,j,k), v(i,j,kb)+v(i,jc,kb)+v(i,jc,k)+v(i,j,k))) *irho(k)
- end do
- end do
- end do
-
-www(:,:,1) = 0.
-
-!---------- non-osscilatory option ---------------
-
-if(nonos) then
-
- do k=1,nzm
- kc=min(nzm,k+1)
- kb=max(1,k-1)
- do j=0,nyp1
- jb=j-1
- jc=j+1
- do i=0,nxp1
- ib=i-1
- ic=i+1
- mx(i,j,k)=max(f(ib,j,k),f(ic,j,k),f(i,jb,k), &
- f(i,jc,k),f(i,j,kb),f(i,j,kc),f(i,j,k),mx(i,j,k))
- mn(i,j,k)=min(f(ib,j,k),f(ic,j,k),f(i,jb,k), &
- f(i,jc,k),f(i,j,kb),f(i,j,kc),f(i,j,k),mn(i,j,k))
- end do
- end do
- end do
-
- do k=1,nzm
- kc=min(nzm,k+1)
- do j=0,nyp1
- jc=j+1
- do i=0,nxp1
- ic=i+1
- mx(i,j,k)=rho(k)*(mx(i,j,k)-f(i,j,k))/ &
- (pn(uuu(ic,j,k)) + pp(uuu(i,j,k))+ &
- pn(vvv(i,jc,k)) + pp(vvv(i,j,k))+ &
- iadz(k)*(pn(www(i,j,kc)) + pp(www(i,j,k)))+eps)
- mn(i,j,k)=rho(k)*(f(i,j,k)-mn(i,j,k))/ &
- (pp(uuu(ic,j,k)) + pn(uuu(i,j,k))+ &
- pp(vvv(i,jc,k)) + pn(vvv(i,j,k))+ &
- iadz(k)*(pp(www(i,j,kc)) + pn(www(i,j,k)))+eps)
- end do
- end do
- end do
-
- do k=1,nzm
- do j=1,ny
- do i=1,nxp1
- ib=i-1
- uuu(i,j,k)=pp(uuu(i,j,k))*min(1.,mx(i,j,k), mn(ib,j,k)) &
- - pn(uuu(i,j,k))*min(1.,mx(ib,j,k),mn(i,j,k))
- end do
- end do
- end do
-
- do k=1,nzm
- do j=1,nyp1
- jb=j-1
- do i=1,nx
- vvv(i,j,k)=pp(vvv(i,j,k))*min(1.,mx(i,j,k), mn(i,jb,k)) &
- - pn(vvv(i,j,k))*min(1.,mx(i,jb,k),mn(i,j,k))
- end do
- end do
- end do
-
- do k=1,nzm
- kb=max(1,k-1)
- do j=1,ny
- do i=1,nx
- www(i,j,k)=pp(www(i,j,k))*min(1.,mx(i,j,k), mn(i,j,kb)) &
- - pn(www(i,j,k))*min(1.,mx(i,j,kb),mn(i,j,k))
- flux(k) = flux(k) + www(i,j,k)
- end do
- end do
- end do
-
-
-endif ! nonos
-
-
-do k=1,nzm
- kc=k+1
- do j=1,ny
- do i=1,nx
- ! MK: added fix for very small negative values (relative to positive values)
- ! especially when such large numbers as
- ! hydrometeor concentrations are advected. The reason for negative values is
- ! most likely truncation error.
-
- f(i,j,k)=max(0.,f(i,j,k) -(uuu(i+1,j,k)-uuu(i,j,k)+vvv(i,j+1,k)-vvv(i,j,k) &
- +(www(i,j,k+1)-www(i,j,k))*iadz(k))*irho(k))
- end do
- end do
-end do
-
-end subroutine advect_scalar3D
-
-
diff --git a/src/physics/spcam/crm/ADV_MPDATA/crmx_advection.F90 b/src/physics/spcam/crm/ADV_MPDATA/crmx_advection.F90
deleted file mode 100644
index 04b1f60d9c..0000000000
--- a/src/physics/spcam/crm/ADV_MPDATA/crmx_advection.F90
+++ /dev/null
@@ -1,3 +0,0 @@
-module crmx_advection
- integer, parameter :: NADV = 0, NADVS=0 ! add'l boundary points
-end module crmx_advection
diff --git a/src/physics/spcam/crm/CLUBB/crmx_Skw_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_Skw_module.F90
deleted file mode 100644
index 2f49672025..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_Skw_module.F90
+++ /dev/null
@@ -1,71 +0,0 @@
-!$Id: Skw_module.F90 5999 2012-12-18 23:53:13Z raut@uwm.edu $
-!-------------------------------------------------------------------------------
-module crmx_Skw_module
-
- implicit none
-
- private ! Default Scope
-
- public :: Skw_func
-
- contains
-
-!-------------------------------------------------------------------------------
- elemental function Skw_func( wp2, wp3 ) &
- result( Skw )
-
-! Description:
-! Calculate the skewness of w, Skw.
-
-! References:
-! None
-!-------------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- w_tol_sqd, &! Constant for w_{_tol}^2, i.e. threshold for vertical velocity
- Skw_max_mag ! Max magnitude of skewness
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: min, max
-
- ! Parameter Constants
- ! Factor to decrease sensitivity in the denominator of Skw calculation
- real( kind = core_rknd ), parameter :: &
- Skw_denom_coef = 8.0_core_rknd ! [-]
-
- ! Whether to apply clipping to the final result
- logical, parameter :: &
- l_clipping_kluge = .false.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- wp2, & ! w'^2 [m^2/s^2]
- wp3 ! w'^3 [m^3/s^3]
-
- ! Output Variable
- real( kind = core_rknd ) :: &
- Skw ! Result Skw [-]
-
- ! ---- Begin Code ----
-
- !Skw = wp3 / ( max( wp2, w_tol_sqd ) )**1.5_core_rknd
- ! Calculation of skewness to help reduce the sensitivity of this value to
- ! small values of wp2.
- Skw = wp3 / ( wp2 + Skw_denom_coef * w_tol_sqd )**1.5_core_rknd
-
- ! This is no longer needed since clipping is already
- ! imposed on wp2 and wp3 elsewhere in the code
- if ( l_clipping_kluge ) then
- Skw = min( max( Skw, -Skw_max_mag ), Skw_max_mag )
- end if
-
- return
- end function Skw_func
-!-----------------------------------------------------------------------
-
-end module crmx_Skw_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_T_in_K_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_T_in_K_module.F90
deleted file mode 100644
index 971bccc073..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_T_in_K_module.F90
+++ /dev/null
@@ -1,86 +0,0 @@
-! $Id: T_in_K_module.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-
-module crmx_T_in_K_module
-
- implicit none
-
- private ! Default scope
-
- public :: thlm2T_in_K, T_in_K2thlm
-
- contains
-
-!-------------------------------------------------------------------------------
- elemental function thlm2T_in_K( thlm, exner, rcm ) &
- result( T_in_K )
-
-! Description:
-! Calculates absolute temperature from liquid water potential
-! temperature. (Does not include ice.)
-
-! References:
-! Cotton and Anthes (1989), "Storm and Cloud Dynamics", Eqn. (2.51).
-!-------------------------------------------------------------------------------
- use crmx_constants_clubb, only: &
- ! Variable(s)
- Cp, & ! Dry air specific heat at constant p [J/kg/K]
- Lv ! Latent heat of vaporization [J/kg]
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input
- real( kind = core_rknd ), intent(in) :: &
- thlm, & ! Liquid potential temperature [K]
- exner, & ! Exner function [-]
- rcm ! Liquid water mixing ratio [kg/kg]
-
- real( kind = core_rknd ) :: &
- T_in_K ! Result temperature [K]
-
- ! ---- Begin Code ----
-
- T_in_K = thlm * exner + Lv * rcm / Cp
-
- return
- end function thlm2T_in_K
-!-------------------------------------------------------------------------------
- elemental function T_in_K2thlm( T_in_K, exner, rcm ) &
- result( thlm )
-
-! Description:
-! Calculates liquid water potential temperature from absolute temperature
-
-! References:
-! None
-!-------------------------------------------------------------------------------
- use crmx_constants_clubb, only: &
- ! Variable(s)
- Cp, & ! Dry air specific heat at constant p [J/kg/K]
- Lv ! Latent heat of vaporization [J/kg]
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input
- real( kind = core_rknd ), intent(in) :: &
- T_in_K, &! Result temperature [K]
- exner, & ! Exner function [-]
- rcm ! Liquid water mixing ratio [kg/kg]
-
- real( kind = core_rknd ) :: &
- thlm ! Liquid potential temperature [K]
-
- ! ---- Begin Code ----
-
- thlm = ( T_in_K - Lv/Cp * rcm ) / exner
-
- return
- end function T_in_K2thlm
-!-------------------------------------------------------------------------------
-
-end module crmx_T_in_K_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_advance_helper_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_advance_helper_module.F90
deleted file mode 100644
index 4f1d8b53a0..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_advance_helper_module.F90
+++ /dev/null
@@ -1,136 +0,0 @@
-!-------------------------------------------------------------------------
-! $Id: advance_helper_module.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-module crmx_advance_helper_module
-
-! Description:
-! This module contains helper methods for the advance_* modules.
-!------------------------------------------------------------------------
-
- implicit none
-
- public :: set_boundary_conditions_lhs, set_boundary_conditions_rhs
-
- private ! Set Default Scope
-
- contains
-
- !---------------------------------------------------------------------------
- subroutine set_boundary_conditions_lhs( diag_index, low_bound, high_bound, lhs, &
- diag_index2, low_bound2, high_bound2 )
-
- ! Description:
- ! Sets the boundary conditions for a left-hand side LAPACK matrix.
- !
- ! References:
- ! none
- !---------------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- integer, intent(in) :: &
- diag_index, low_bound, high_bound ! boundary indexes for the first variable
-
- integer, intent(in), optional :: &
- diag_index2, low_bound2, high_bound2 ! boundary indexes for the second variable
-
- real( kind = core_rknd ), dimension(:,:), intent(inout) :: &
- lhs ! left hand side of the LAPACK matrix equation
-
- ! --------------------- BEGIN CODE ----------------------
-
- if( ( present(low_bound2) .or. present(high_bound2) ) .and. &
- ( .not. present(diag_index2) ) ) then
-
- stop "Boundary index provided without diag_index."
-
- end if
-
- ! Set the lower boundaries for the first variable
- lhs(:,low_bound) = 0.0_core_rknd
- lhs(diag_index,low_bound) = 1.0_core_rknd
-
- ! Set the upper boundaries for the first variable
- lhs(:,high_bound) = 0.0_core_rknd
- lhs(diag_index,high_bound) = 1.0_core_rknd
-
- ! Set the lower boundaries for the second variable, if it is provided
- if( present(low_bound2) ) then
-
- lhs(:,low_bound2) = 0.0_core_rknd
- lhs(diag_index2,low_bound2) = 1.0_core_rknd
-
- end if
-
- ! Set the upper boundaries for the second variable, if it is provided
- if( present(high_bound2) ) then
-
- lhs(:,high_bound2) = 0.0_core_rknd
- lhs(diag_index2,high_bound2) = 1.0_core_rknd
-
- end if
-
- end subroutine set_boundary_conditions_lhs
-
- !--------------------------------------------------------------------------
- subroutine set_boundary_conditions_rhs( &
- low_value, low_bound, high_value, high_bound, &
- rhs, &
- low_value2, low_bound2, high_value2, high_bound2 )
-
- ! Description:
- ! Sets the boundary conditions for a right-hand side LAPACK vector.
- !
- ! References:
- ! none
- !---------------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! The values for the first variable
- real( kind = core_rknd ), intent(in) :: low_value, high_value
-
- ! The bounds for the first variable
- integer, intent(in) :: low_bound, high_bound
-
- ! The values for the second variable
- real( kind = core_rknd ), intent(in), optional :: low_value2, high_value2
-
- ! The bounds for the second variable
- integer, intent(in), optional :: low_bound2, high_bound2
-
- ! The right-hand side vector
- real( kind = core_rknd ), dimension(:), intent(inout) :: rhs
-
- ! -------------------- BEGIN CODE ------------------------
-
- ! Stop execution if a boundary was provided without a value
- if( (present(low_bound2) .and. (.not. present(low_value2))) .or. &
- (present(high_bound2) .and. (.not. present(high_value2))) ) then
-
- stop "Boundary condition provided without value."
-
- end if
-
- ! Set the lower and upper bounds for the first variable
- rhs(low_bound) = low_value
- rhs(high_bound) = high_value
-
- ! If a lower bound was given for the second variable, set it
- if( present(low_bound2) ) then
- rhs(low_bound2) = low_value2
- end if
-
- ! If an upper bound was given for the second variable, set it
- if( present(high_bound2) ) then
- rhs(high_bound2) = high_value2
- end if
-
- end subroutine set_boundary_conditions_rhs
-
-end module crmx_advance_helper_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_advance_windm_edsclrm_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_advance_windm_edsclrm_module.F90
deleted file mode 100644
index 57799743cb..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_advance_windm_edsclrm_module.F90
+++ /dev/null
@@ -1,1909 +0,0 @@
-!------------------------------------------------------------------------
-! $Id: advance_windm_edsclrm_module.F90 5960 2012-10-18 20:34:59Z janhft@uwm.edu $
-!===============================================================================
-module crmx_advance_windm_edsclrm_module
-
- implicit none
-
- private ! Set Default Scope
-
- public :: advance_windm_edsclrm, xpwp_fnc
-
- private :: windm_edsclrm_solve, &
- compute_uv_tndcy, &
- windm_edsclrm_lhs, &
- windm_edsclrm_rhs
-
-
- ! Private named constants to avoid string comparisons
- integer, parameter, private :: &
- windm_edsclrm_um = 1, & ! Named constant to handle um solves
- windm_edsclrm_vm = 2, & ! Named constant to handle vm solves
- windm_edsclrm_scalar = 3, & ! Named constant to handle scalar solves
- clip_upwp = 10, & ! Named constant for upwp clipping
- ! NOTE: This must be the same as the clip_upwp
- ! declared in clip_explicit!
- clip_vpwp = 11 ! Named constant for vpwp clipping
- ! NOTE: This must be the same as the clip_vpwp
- ! declared in clip_explicit!
-
- contains
-
- !=============================================================================
- subroutine advance_windm_edsclrm &
- ( dt, wm_zt, Kh_zm, ug, vg, um_ref, vm_ref, &
- wp2, up2, vp2, um_forcing, vm_forcing, &
- edsclrm_forcing, &
- rho_ds_zm, invrs_rho_ds_zt, &
- fcor, l_implemented, &
- um, vm, edsclrm, &
- upwp, vpwp, wpedsclrp, err_code )
-
- ! Description:
- ! Solves for both mean horizontal wind components, um and vm, and for the
- ! eddy-scalars (passive scalars that don't use the high-order closure).
-
- ! Uses the LAPACK tridiagonal solver subroutine with 2 + # of scalar(s)
- ! back substitutions (since the left hand side matrix is the same for all
- ! input variables).
-
- ! References:
- ! Eqn. 8 & 9 on p. 3545 of
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variables(s)
-
- use crmx_parameters_model, only: &
- ts_nudge, & ! Variable(s)
- edsclr_dim
-
- use crmx_parameters_tunable, only: &
- nu10_vert_res_dep ! Constant
-
- use crmx_model_flags, only: &
- l_uv_nudge, & ! Variable(s)
- l_tke_aniso
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Subroutines
- stat_end_update, &
- stat_update_var
-
- use crmx_stats_variables, only: &
- ium_ref, & ! Variables
- ivm_ref, &
- ium_sdmp, &
- ivm_sdmp, &
- ium_ndg, &
- ivm_ndg, &
- iwindm_matrix_condt_num, &
- zt, &
- l_stats_samp
-
- use crmx_clip_explicit, only: &
- clip_covar ! Procedure(s)
-
- use crmx_error_code, only: &
- clubb_at_least_debug_level, & ! Procedure(s)
- fatal_error
-
- use crmx_error_code, only: &
- clubb_no_error, & ! Constant(s)
- clubb_singular_matrix
-
- use crmx_constants_clubb, only: &
- fstderr, & ! Constant(s)
- eps
-
- use crmx_sponge_layer_damping, only: &
- uv_sponge_damp_settings, &
- uv_sponge_damp_profile, &
- sponge_damp_xm ! Procedure(s)
-
- implicit none
-
- ! External
- intrinsic :: real
-
- ! Constant Parameters
- real( kind = core_rknd ), dimension(gr%nz) :: &
- dummy_nu ! Used to feed zero values into function calls
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- Kh_zm, & ! Eddy diffusivity on momentum levels [m^2/s]
- ug, & ! u (west-to-east) geostrophic wind comp. [m/s]
- vg, & ! v (south-to-north) geostrophic wind comp. [m/s]
- um_ref, & ! Reference u wind component for nudging [m/s]
- vm_ref, & ! Reference v wind component for nudging [m/s]
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- up2, & ! u'^2 (momentum levels) [m^2/s^2]
- vp2, & ! v'^2 (momentum levels) [m^2/s^2]
- um_forcing, & ! u forcing [m/s/s]
- vm_forcing, & ! v forcing [m/s/s]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zt ! Inv. dry, static density at thermo. levels [m^3/kg]
-
- real( kind = core_rknd ), dimension(gr%nz,edsclr_dim), intent(in) :: &
- edsclrm_forcing ! Eddy scalar large-scale forcing [{units vary}/s]
-
- real( kind = core_rknd ), intent(in) :: &
- fcor ! Coriolis parameter [s^-1]
-
- logical, intent(in) :: &
- l_implemented ! Flag for CLUBB being implemented in a larger model.
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- um, & ! Mean u (west-to-east) wind component [m/s]
- vm ! Mean v (south-to-north) wind component [m/s]
-
- ! Input/Output Variable for eddy-scalars
- real( kind = core_rknd ), dimension(gr%nz,edsclr_dim), intent(inout) :: &
- edsclrm ! Mean eddy scalar quantity [units vary]
-
- ! Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- upwp, & ! u'w' (momentum levels) [m^2/s^2]
- vpwp ! v'w' (momentum levels) [m^2/s^2]
-
- ! Output Variable for eddy-scalars
- real( kind = core_rknd ), dimension(gr%nz,edsclr_dim), intent(inout) :: &
- wpedsclrp ! w'edsclr' (momentum levels) [units vary]
-
- integer, intent(inout) :: &
- err_code ! clubb_singular_matrix when matrix is singular
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- um_tndcy, & ! u wind component tendency [m/s^2]
- vm_tndcy ! v wind component tendency [m/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- upwp_chnge, & ! Net change of u'w' due to clipping [m^2/s^2]
- vpwp_chnge ! Net change of v'w' due to clipping [m^2/s^2]
-
- real( kind = core_rknd ), dimension(3,gr%nz) :: &
- lhs ! The implicit part of the tridiagonal matrix [units vary]
-
- real( kind = core_rknd ), dimension(gr%nz,max(2,edsclr_dim)) :: &
- rhs, &! The explicit part of the tridiagonal matrix [units vary]
- solution ! The solution to the tridiagonal matrix [units vary]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wind_speed ! wind speed; sqrt(u^2 + v^2) [m/s]
-
- real( kind = core_rknd ) :: &
- u_star_sqd ! Surface friction velocity, u_star, squared [m/s]
-
- logical :: &
- l_imp_sfc_momentum_flux ! Flag for implicit momentum surface fluxes.
-
- integer :: &
- err_code_windm, err_code_edsclrm, & ! Error code for each LAPACK solve
- nrhs ! Number of right hand side terms
-
- integer :: i ! Array index
-
- logical :: l_first_clip_ts, l_last_clip_ts ! flags for clip_covar
-
- !--------------------------- Begin Code ------------------------------------
-
- ! Initialize to no errors
- err_code_windm = clubb_no_error
- err_code_edsclrm = clubb_no_error
-
- dummy_nu = 0._core_rknd
-
- !----------------------------------------------------------------
- ! Prepare tridiagonal system for horizontal winds, um and vm
- !----------------------------------------------------------------
-
- ! Compute Coriolis, geostrophic, and other prescribed wind forcings for um.
- call compute_uv_tndcy( windm_edsclrm_um, fcor, vm, vg, um_forcing, & ! in
- l_implemented, & ! in
- um_tndcy ) ! out
-
- ! Compute Coriolis, geostrophic, and other prescribed wind forcings for vm.
- call compute_uv_tndcy( windm_edsclrm_vm, fcor, um, ug, vm_forcing, & ! in
- l_implemented, & ! in
- vm_tndcy ) ! out
-
- ! Momentum surface fluxes, u'w'|_sfc and v'w'|_sfc, are applied to through
- ! an implicit method, such that:
- ! x'w'|_sfc = - ( u_star(t)^2 / wind_speed(t) ) * xm(t+1).
- l_imp_sfc_momentum_flux = .true.
- ! Compute wind speed (use threshold "eps" to prevent divide-by-zero error).
- wind_speed = max( sqrt( um**2 + vm**2 ), eps )
- ! Compute u_star_sqd according to the definition of u_star.
- u_star_sqd = sqrt( upwp(1)**2 + vpwp(1)**2 )
-
- ! Compute the explicit portion of the um equation.
- ! Build the right-hand side vector.
- rhs(1:gr%nz,windm_edsclrm_um) &
- = windm_edsclrm_rhs( windm_edsclrm_um, dt, nu10_vert_res_dep, Kh_zm, um, & ! in
- um_tndcy, & ! in
- rho_ds_zm, invrs_rho_ds_zt, & ! in
- l_imp_sfc_momentum_flux, upwp(1) ) ! in
-
- ! Compute the explicit portion of the vm equation.
- ! Build the right-hand side vector.
- rhs(1:gr%nz,windm_edsclrm_vm) &
- = windm_edsclrm_rhs( windm_edsclrm_vm, dt, nu10_vert_res_dep, Kh_zm, vm, & ! in
- vm_tndcy, & ! in
- rho_ds_zm, invrs_rho_ds_zt, & ! in
- l_imp_sfc_momentum_flux, vpwp(1) ) ! in
-
-
- ! Store momentum flux (explicit component)
-
- ! The surface flux, x'w'(1) = x'w'|_sfc, is set elsewhere in the model.
-! upwp(1) = upwp_sfc
-! vpwp(1) = vpwp_sfc
-
- ! Solve for x'w' at all intermediate model levels.
- ! A Crank-Nicholson timestep is used.
-
- upwp(2:gr%nz-1) = - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1)+ &
- nu10_vert_res_dep(2:gr%nz-1), & ! in
- um(2:gr%nz-1), um(3:gr%nz), & ! in
- gr%invrs_dzm(2:gr%nz-1) )
-
- vpwp(2:gr%nz-1) = - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1)+ &
- nu10_vert_res_dep(2:gr%nz-1), & ! in
- vm(2:gr%nz-1), vm(3:gr%nz), & ! in
- gr%invrs_dzm(2:gr%nz-1) )
-
- ! A zero-flux boundary condition at the top of the model, d(xm)/dz = 0,
- ! means that x'w' at the top model level is 0,
- ! since x'w' = - K_zm * d(xm)/dz.
- upwp(gr%nz) = 0._core_rknd
- vpwp(gr%nz) = 0._core_rknd
-
-
- ! Compute the implicit portion of the um and vm equations.
- ! Build the left-hand side matrix.
- call windm_edsclrm_lhs( dt, nu10_vert_res_dep, wm_zt, Kh_zm, wind_speed, u_star_sqd, & ! in
- rho_ds_zm, invrs_rho_ds_zt, & ! in
- l_implemented, l_imp_sfc_momentum_flux, & ! in
- lhs ) ! out
-
- ! Decompose and back substitute for um and vm
- nrhs = 2
- call windm_edsclrm_solve( nrhs, iwindm_matrix_condt_num, & ! in
- lhs, rhs, & ! in/out
- solution, err_code_windm ) ! out
-
- !----------------------------------------------------------------
- ! Update zonal (west-to-east) component of mean wind, um
- !----------------------------------------------------------------
- um(1:gr%nz) = solution(1:gr%nz,windm_edsclrm_um)
-
- !----------------------------------------------------------------
- ! Update meridional (south-to-north) component of mean wind, vm
- !----------------------------------------------------------------
- vm(1:gr%nz) = solution(1:gr%nz,windm_edsclrm_vm)
-
- if ( l_stats_samp ) then
-
- ! Implicit contributions to um and vm
- call windm_edsclrm_implicit_stats( windm_edsclrm_um, um ) ! in
-
- call windm_edsclrm_implicit_stats( windm_edsclrm_vm, vm ) ! in
-
- endif ! l_stats_samp
-
- ! The values of um(1) and vm(1) are located below the model surface and do
- ! not effect the rest of the model. The values of um(1) or vm(1) are simply
- ! set to the values of um(2) and vm(2), respectively, after the equation
- ! matrices has been solved. Even though um and vm would sharply decrease
- ! to a value of 0 at the surface, this is done to avoid confusion on plots
- ! of the vertical profiles of um and vm.
- um(1) = um(2)
- vm(1) = vm(2)
-
-
- if ( uv_sponge_damp_settings%l_sponge_damping ) then
- if( l_stats_samp ) then
- call stat_begin_update( ium_sdmp, um/real( dt, kind = core_rknd ), zt )
- call stat_begin_update( ivm_sdmp, vm/real( dt, kind = core_rknd ), zt )
- endif
-
- um(1:gr%nz) = sponge_damp_xm( dt, um_ref(1:gr%nz), um(1:gr%nz), &
- uv_sponge_damp_profile )
- vm(1:gr%nz) = sponge_damp_xm( dt, vm_ref(1:gr%nz), vm(1:gr%nz), &
- uv_sponge_damp_profile )
- if( l_stats_samp ) then
- call stat_end_update( ium_sdmp, um/real( dt, kind = core_rknd ), zt )
- call stat_end_update( ivm_sdmp, vm/real( dt, kind = core_rknd ), zt )
- endif
-
- endif
-
- ! Second part of momentum (implicit component)
-
- ! Solve for x'w' at all intermediate model levels.
- ! A Crank-Nicholson timestep is used.
-
- upwp(2:gr%nz-1) = upwp(2:gr%nz-1) &
- - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1)+nu10_vert_res_dep(2:gr%nz-1), &
- um(2:gr%nz-1), um(3:gr%nz), gr%invrs_dzm(2:gr%nz-1) ) !in
-
- vpwp(2:gr%nz-1) = vpwp(2:gr%nz-1) &
- - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1)+nu10_vert_res_dep(2:gr%nz-1), &
- vm(2:gr%nz-1), vm(3:gr%nz), gr%invrs_dzm(2:gr%nz-1) ) !in
-
-
- ! Adjust um and vm if nudging is turned on.
- if ( l_uv_nudge ) then
-
- ! Reflect nudging in budget
- if( l_stats_samp ) then
- call stat_begin_update( ium_ndg, um / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_begin_update( ivm_ndg, vm / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- end if
-
- um(1:gr%nz) = um(1:gr%nz) &
- - ((um(1:gr%nz) - um_ref(1:gr%nz)) * (real( dt, kind = core_rknd )/ts_nudge))
- vm(1:gr%nz) = vm(1:gr%nz) &
- - ((vm(1:gr%nz) - vm_ref(1:gr%nz)) * (real( dt, kind = core_rknd )/ts_nudge))
- endif
-
- if( l_stats_samp ) then
-
- ! Reflect nudging in budget
- if ( l_uv_nudge ) then
- call stat_end_update( ium_ndg, um / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_end_update( ivm_ndg, vm / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- end if
-
- call stat_update_var( ium_ref, um_ref, zt )
- call stat_update_var( ivm_ref, vm_ref, zt )
- end if
-
- if ( l_tke_aniso ) then
-
- ! Clipping for u'w'
- !
- ! Clipping u'w' at each vertical level, based on the
- ! correlation of u and w at each vertical level, such that:
- ! corr_(u,w) = u'w' / [ sqrt(u'^2) * sqrt(w'^2) ];
- ! -1 <= corr_(u,w) <= 1.
- !
- ! Since u'^2, w'^2, and u'w' are each advanced in different subroutines from
- ! each other in advance_clubb_core, clipping for u'w' has to be done three
- ! times during each timestep (once after each variable has been updated).
- ! This is the third instance of u'w' clipping.
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- call clip_covar( clip_upwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, up2, & ! intent(in)
- upwp, upwp_chnge ) ! intent(inout)
-
- ! Clipping for v'w'
- !
- ! Clipping v'w' at each vertical level, based on the
- ! correlation of v and w at each vertical level, such that:
- ! corr_(v,w) = v'w' / [ sqrt(v'^2) * sqrt(w'^2) ];
- ! -1 <= corr_(v,w) <= 1.
- !
- ! Since v'^2, w'^2, and v'w' are each advanced in different subroutines from
- ! each other in advance_clubb_core, clipping for v'w' has to be done three
- ! times during each timestep (once after each variable has been updated).
- ! This is the third instance of v'w' clipping.
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- call clip_covar( clip_vpwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, vp2, & ! intent(in)
- vpwp, vpwp_chnge ) ! intent(inout)
-
- else
-
- ! In this case, it is assumed that
- ! u'^2 == v'^2 == w'^2, and the variables `up2' and `vp2' do not interact with
- ! any other variables.
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- call clip_covar( clip_upwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, wp2, & ! intent(in)
- upwp, upwp_chnge ) ! intent(inout)
-
- call clip_covar( clip_vpwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, wp2, & ! intent(in)
- vpwp, vpwp_chnge ) ! intent(inout)
-
- endif ! l_tke_aniso
-
-
- !----------------------------------------------------------------
- ! Prepare tridiagonal system for eddy-scalars
- !----------------------------------------------------------------
-
- if ( edsclr_dim > 0 ) then
-
- ! Eddy-scalar surface fluxes, x'w'|_sfc, are applied through an explicit
- ! method.
- l_imp_sfc_momentum_flux = .false.
-
- ! Compute the explicit portion of eddy scalar equation.
- ! Build the right-hand side vector.
- ! Because of statistics, we have to use a DO rather than a FORALL here
- ! -dschanen 7 Oct 2008
-!HPF$ INDEPENDENT
- do i = 1, edsclr_dim
- rhs(1:gr%nz,i) &
- = windm_edsclrm_rhs( windm_edsclrm_scalar, dt, dummy_nu, Kh_zm, & ! in
- edsclrm(:,i), edsclrm_forcing, & ! in
- rho_ds_zm, invrs_rho_ds_zt, & ! in
- l_imp_sfc_momentum_flux, wpedsclrp(1,i) ) ! in
- enddo
-
-
- ! Store momentum flux (explicit component)
-
- ! The surface flux, x'w'(1) = x'w'|_sfc, is set elsewhere in the model.
-! wpedsclrp(1,1:edsclr_dim) = wpedsclrp_sfc(1:edsclr_dim)
-
- ! Solve for x'w' at all intermediate model levels.
- ! A Crank-Nicholson timestep is used.
- ! Here we use a forall and high performance fortran directive to try to
- ! parallelize this computation. Note that FORALL is more restrictive than DO.
-!HPF$ INDEPENDENT, REDUCTION(wpedsclrp)
- forall( i = 1:edsclr_dim )
- wpedsclrp(2:gr%nz-1,i) = &
- - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1), edsclrm(2:gr%nz-1,i), & ! in
- edsclrm(3:gr%nz,i), gr%invrs_dzm(2:gr%nz-1) ) ! in
- end forall
-
- ! A zero-flux boundary condition at the top of the model, d(xm)/dz = 0,
- ! means that x'w' at the top model level is 0,
- ! since x'w' = - K_zm * d(xm)/dz.
- wpedsclrp(gr%nz,1:edsclr_dim) = 0._core_rknd
-
-
- ! Compute the implicit portion of the xm (eddy-scalar) equations.
- ! Build the left-hand side matrix.
- call windm_edsclrm_lhs( dt, dummy_nu, wm_zt, Kh_zm, wind_speed, u_star_sqd, & ! in
- rho_ds_zm, invrs_rho_ds_zt, & ! in
- l_implemented, l_imp_sfc_momentum_flux, & ! in
- lhs ) ! out
-
- ! Decompose and back substitute for all eddy-scalar variables
- call windm_edsclrm_solve( edsclr_dim, 0, & ! in
- lhs, rhs, & ! in/out
- solution, err_code_edsclrm ) ! out
-
- !----------------------------------------------------------------
- ! Update Eddy-diff. Passive Scalars
- !----------------------------------------------------------------
- edsclrm(1:gr%nz,1:edsclr_dim) = solution(1:gr%nz,1:edsclr_dim)
-
- ! The value of edsclrm(1) is located below the model surface and does not
- ! effect the rest of the model. The value of edsclrm(1) is simply set to
- ! the value of edsclrm(2) after the equation matrix has been solved.
- forall( i=1:edsclr_dim )
- edsclrm(1,i) = edsclrm(2,i)
- end forall
-
- ! Second part of momentum (implicit component)
-
- ! Solve for x'w' at all intermediate model levels.
- ! A Crank-Nicholson timestep is used.
-!HPF$ INDEPENDENT, REDUCTION(wpedsclrp)
- forall( i = 1:edsclr_dim )
- wpedsclrp(2:gr%nz-1,i) = wpedsclrp(2:gr%nz-1,i) &
- - 0.5_core_rknd * xpwp_fnc( Kh_zm(2:gr%nz-1), edsclrm(2:gr%nz-1,i), & ! in
- edsclrm(3:gr%nz,i), gr%invrs_dzm(2:gr%nz-1) ) ! in
- end forall
-
- ! Note that the w'edsclr' terms are not clipped, since we don't compute the
- ! variance of edsclr anywhere. -dschanen 7 Oct 2008
-
- endif
-
- ! Check for singular matrices and bad LAPACK arguments
- if ( fatal_error( err_code_windm ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "Fatal error solving for um/vm"
- end if
- err_code = err_code_windm
- end if
-
- if ( fatal_error( err_code_edsclrm ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "Fatal error solving for eddsclrm"
- end if
- err_code = err_code_edsclrm
- end if
-
- ! Error report
- ! Joshua Fasching February 2008
- if ( ( fatal_error( err_code_windm ) .or. fatal_error( err_code_edsclrm ) ) .and. &
- clubb_at_least_debug_level( 1 ) ) then
-
- write(fstderr,*) "Error in advance_windm_edsclrm"
-
- write(fstderr,*) "Intent(in)"
-
- write(fstderr,*) "dt = ", dt
- write(fstderr,*) "wm_zt = ", wm_zt
- write(fstderr,*) "Kh_zm = ", Kh_zm
- write(fstderr,*) "ug = ", ug
- write(fstderr,*) "vg = ", vg
- write(fstderr,*) "um_ref = ", um_ref
- write(fstderr,*) "vm_ref = ", vm_ref
- write(fstderr,*) "wp2 = ", wp2
- write(fstderr,*) "up2 = ", up2
- write(fstderr,*) "vp2 = ", vp2
- write(fstderr,*) "um_forcing = ", um_forcing
- write(fstderr,*) "vm_forcing = ", vm_forcing
- do i = 1, edsclr_dim
- write(fstderr,*) "edsclrm_forcing # = ", i, edsclrm_forcing
- end do
- write(fstderr,*) "fcor = ", fcor
- write(fstderr,*) "l_implemented = ", l_implemented
-
- write(fstderr,*) "Intent(inout)"
-
- write(fstderr,*) "um = ", um
- write(fstderr,*) "vm = ", vm
- do i = 1, edsclr_dim
- write(fstderr,*) "edsclrm # ", i, "=", edsclrm(:,i)
- end do
- write(fstderr,*) "upwp = ", upwp
- write(fstderr,*) "vpwp = ", vpwp
- write(fstderr,*) "wpedsclrp = ", wpedsclrp
-
- !write(fstderr,*) "Intent(out)"
-
- return
-
- end if
-
- return
- end subroutine advance_windm_edsclrm
-
- !=============================================================================
- subroutine windm_edsclrm_solve( nrhs, ixm_matrix_condt_num, &
- lhs, rhs, solution, err_code )
-
- ! Note: In the "Description" section of this subroutine, the variable
- ! "invrs_dzm" will be written as simply "dzm", and the variable
- ! "invrs_dzt" will be written as simply "dzt". This is being done as
- ! as device to save space and to make some parts of the description
- ! more readable. This change does not pertain to the actual code.
-
- ! Description:
- ! Solves the horizontal wind or eddy-scalar time-tendency equation, and
- ! diagnoses the turbulent flux. A Crank-Nicholson time-stepping algorithm
- ! is used in solving the turbulent advection term and in diagnosing the
- ! turbulent flux.
- !
- ! The rate of change of an eddy-scalar quantity, xm, is:
- !
- ! d(xm)/dt = - w * d(xm)/dz - (1/rho_ds) * d( rho_ds * x'w' )/dz
- ! + xm_forcings.
- !
- !
- ! The Turbulent Advection Term
- ! ----------------------------
- !
- ! The above equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * x'w' )/dz;
- !
- ! where the momentum flux, x'w', is closed using a down gradient approach:
- !
- ! x'w' = - K_zm * d(xm)/dz.
- !
- ! The turbulent advection term becomes:
- !
- ! + (1/rho_ds) * d [ rho_ds * K_zm * d(xm)/dz ] / dz;
- !
- ! which is the same as a standard eddy-diffusion term (if "rho_ds * K_zm" in
- ! the term above is substituted for "K_zm" in a standard eddy-diffusion
- ! term, and if the standard eddy-diffusion term is multiplied by
- ! "1/rho_ds"). Thus, the turbulent advection term is treated and solved in
- ! the same way that a standard eddy-diffusion term would be solved. The
- ! term is discretized as follows:
- !
- ! The values of xm are found on the thermodynamic levels, while the values
- ! of K_zm are found on the momentum levels. Additionally, the values of
- ! rho_ds_zm are found on the momentum levels, and the values of
- ! invrs_rho_ds_zt are found on the thermodynamic levels. The
- ! derivatives (d/dz) of xm are taken over the intermediate momentum levels.
- ! At the intermediate momentum levels, d(xm)/dz is multiplied by K_zm and by
- ! rho_ds_zm. Then, the derivative of the whole mathematical expression is
- ! taken over the central thermodynamic level, where it is multiplied by
- ! invrs_rho_ds_zt, which yields the desired result.
- !
- ! ---xm(kp1)----------------------------------------------------- t(k+1)
- !
- ! ===========d(xm)/dz===K_zm(k)=====rho_ds_zm(k)================= m(k)
- !
- ! ---xm(k)---invrs_rho_ds_zt---d[rho_ds_zm*K_zm*d(xm)/dz]/dz----- t(k)
- !
- ! ===========d(xm)/dz===K_zm(km1)===rho_ds_zm(km1)=============== m(k-1)
- !
- ! ---xm(km1)----------------------------------------------------- t(k-1)
- !
- ! The vertical indices t(k+1), m(k), t(k), m(k-1), and t(k-1) correspond
- ! with altitudes zt(k+1), zm(k), zt(k), zm(k-1), and zt(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! dzt(k) = 1 / ( zm(k) - zm(k-1) )
- ! dzm(k) = 1 / ( zt(k+1) - zt(k) )
- ! dzm(k-1) = 1 / ( zt(k) - zt(k-1) )
- !
- ! The vertically discretized form of the turbulent advection term (treated
- ! as an eddy diffusion term) is written out as:
- !
- ! + invrs_rho_ds_zt(k)
- ! * dzt(k)
- ! * [ rho_ds_zm(k) * K_zm(k) * dzm(k) * ( xm(k+1) - xm(k) )
- ! - rho_ds_zm(k-1) * K_zm(k-1) * dzm(k-1) * ( xm(k) - xm(k-1) ) ].
- !
- ! For this equation, a Crank-Nicholson (semi-implicit) diffusion scheme is
- ! used to solve the (1/rho_ds) * d [ rho_ds * K_zm * d(xm)/dz ] / dz
- ! eddy-diffusion term. The discretized implicit portion of the term is
- ! written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(k)
- ! * dzt(k)
- ! * [ rho_ds_zm(k) * K_zm(k)
- ! * dzm(k) * ( xm(k+1,) - xm(k,) )
- ! - rho_ds_zm(k-1) * K_zm(k-1)
- ! * dzm(k-1) * ( xm(k,) - xm(k-1,) ) ].
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "+" in front of the term
- ! is changed to a "-".
- !
- ! The discretized explicit portion of the term is written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(k)
- ! * dzt(k)
- ! * [ rho_ds_zm(k) * K_zm(k)
- ! * dzm(k) * ( xm(k+1,) - xm(k,) )
- ! - rho_ds_zm(k-1) * K_zm(k-1)
- ! * dzm(k-1) * ( xm(k,) - xm(k-1,) ) ].
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(xm)/dt equation.
- !
- !
- ! Boundary Conditions:
- !
- ! An eddy-scalar quantity is not allowed to flux out the upper boundary.
- ! Thus, a zero-flux boundary condition is used for the upper boundary in the
- ! eddy-diffusion equation.
- !
- ! The lower boundary condition is much more complicated. It is neither a
- ! zero-flux nor a fixed-point boundary condition. Rather, it is a
- ! fixed-flux boundary condition. This term is a turbulent advection term,
- ! but with the eddy-scalars, the only value of x'w' relevant in solving the
- ! d(xm)/dt equation is the value of x'w' at the surface (the first momentum
- ! level), which is written as x'w'|_sfc.
- !
- ! 1) x'w' surface flux; generalized explicit form
- !
- ! The x'w' surface flux is applied to the d(xm)/dt equation through the
- ! turbulent advection term, which is:
- !
- ! - (1/rho_ds) * d( rho_ds * x'w' )/dz.
- !
- ! At most vertical levels, a substitution can be made for x'w', such
- ! that:
- !
- ! x'w' = - K_zm * d(xm)/dz.
- !
- ! However, the same substitution cannot be made at the surface (momentum
- ! level 1), as x'w'|_sfc is a surface flux that is explicitly computed
- ! elsewhere in the model code.
- !
- ! The lower boundary condition, which in this case needs to be applied to
- ! the d(xm)/dt equation at level 2, is discretized as follows:
- !
- ! --xm(3)------------------------------------------------------- t(3)
- !
- ! ========[x'w'(2) = -K_zm(2)*d(xm)/dz]===rho_ds_zm(2)========== m(2)
- !
- ! --xm(2)---invrs_rho_ds_zt(2)---d[rho_ds_zm*K_zm*d(xm)/dz]/dz-- t(2)
- !
- ! ========[x'w'|_sfc]=====================rho_ds_zm(1)========== m(1) sfc
- !
- ! --xm(1)-------(below surface; not applicable)----------------- t(1)
- !
- ! where "sfc" is the level of the model surface or lower boundary.
- !
- ! The vertically discretized form of the turbulent advection term
- ! (treated as an eddy diffusion term), with the explicit surface flux,
- ! x'w'|_sfc, in place, is written out as:
- !
- ! - invrs_rho_ds_zt(2)
- ! * dzt(2) * [ rho_ds_zm(2) * x'w'(2) - rho_ds_zm(1) * x'w'|_sfc ];
- !
- ! which can be re-written as:
- !
- ! + invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2) * K_zm(2) * dzm(2) * ( xm(3) - xm(2) )
- ! + rho_ds_zm(1) * x'w'|_sfc ];
- !
- ! which can be re-written again as:
- !
- ! + invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(2) * K_zm(2) * dzm(2) * ( xm(3) - xm(2) )
- ! + invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(1) * x'w'|_sfc.
- !
- ! For this equation, a Crank-Nicholson (semi-implicit) diffusion scheme
- ! is used to solve the (1/rho_ds) * d [ rho_ds * K_zm * d(xm)/dz ] / dz
- ! eddy-diffusion term. The discretized implicit portion of the term is
- ! written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2) * K_zm(2)
- ! * dzm(2) * ( xm(3,) - xm(2,) ) ].
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "+" in front of the term
- ! is changed to a "-".
- !
- ! The discretized explicit portion of the term is written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2) * K_zm(2)
- ! * dzm(2) * ( xm(3,) - xm(2,) ) ]
- ! + invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(1) * x'w'|_sfc.
- !
- ! Note: The x'w'|_sfc portion of the term written above has been pulled
- ! away from the rest of the explicit form written above because
- ! the (1/2) factor due to Crank-Nicholson time_stepping does not
- ! apply to it, as there isn't an implicit portion for x'w'|_sfc.
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which
- ! is being advanced to in solving the d(xm)/dt equation.
- !
- ! 2) x'w' surface flux; implicit form for momentum fluxes u'w' and v'w'
- !
- ! The x'w' surface flux is applied to the d(xm)/dt equation through the
- ! turbulent advection term, which is:
- !
- ! - (1/rho_ds) * d( rho_ds * x'w' )/dz.
- !
- ! At most vertical levels, a substitution can be made for x'w', such
- ! that:
- !
- ! x'w' = - K_zm * d(xm)/dz.
- !
- ! However, the same substitution cannot be made at the surface (momentum
- ! level 1), as x'w'|_sfc is a surface momentum flux that is found by the
- ! following equation:
- !
- ! x'w'|_sfc = - [ u_star^2 / sqrt( um^2 + vm^2 ) ] * xm;
- !
- ! where x'w'|_sfc and xm are either u'w'|_sfc and um, respectively, or
- ! v'w'|_sfc and vm, respectively (um and vm are located at the first
- ! thermodynamic level above the surface, which is thermodynamic level 2),
- ! sqrt( um^2 + vm^2 ) is the wind speed (also at thermodynamic level 2),
- ! and u_star is defined as:
- !
- ! u_star = ( u'w'|_sfc^2 + v'w'|_sfc^2 )^(1/4);
- !
- ! and thus u_star^2 is defined as:
- !
- ! u_star^2 = sqrt( u'w'|_sfc^2 + v'w'|_sfc^2 ).
- !
- ! The value of u_star is either set to a constant value or computed
- ! (through function diag_ustar) based on the surface wind speed, the
- ! height above surface of the surface wind speed (as compared to the
- ! roughness height), and the buoyancy flux at the surface. Either way,
- ! u_star is computed elsewhere in the model, and the values of u'w'|_sfc
- ! and v'w'|_sfc are based on it and computed along with it. The values
- ! of u'w'|_sfc and v'w'|_sfc are then passed into advance_clubb_core,
- ! and are eventually passed into advance_windm_edsclrm. In subroutine
- ! advance_windm_edsclrm, the value of u_star_sqd is then recomputed
- ! based on u'w'|_sfc and v'w'|_sfc. The value of sqrt( u_star_sqd ) is
- ! consistent with the value of the original computation of u_star.
- !
- ! The equation listed above is substituted for x'w'|_sfc. The lower
- ! boundary condition, which in this case needs to be applied to the
- ! d(xm)/dt equation at level 2, is discretized as follows:
- !
- ! --xm(3)------------------------------------------------------- t(3)
- !
- ! ===[x'w'(2) = -K_zm(2)*d(xm)/dz]=================rho_ds_zm(2)= m(2)
- !
- ! --xm(2)---invrs_rho_ds_zt(2)---d[rho_ds_zm*K_zm*d(xm)/dz]/dz-- t(2)
- !
- ! ===[x'w'|_sfc = -[u_star^2/sqrt(um^2+vm^2)]*xm]==rho_ds_zm(1)= m(1) sfc
- !
- ! --xm(1)-------(below surface; not applicable)----------------- t(1)
- !
- ! where "sfc" is the level of the model surface or lower boundary.
- !
- ! The vertically discretized form of the turbulent advection term
- ! (treated as an eddy diffusion term), with the implicit surface momentum
- ! flux in place, is written out as:
- !
- ! - invrs_rho_ds_zt(2)
- ! * dzt(2) * [ rho_ds_zm(2) * x'w'(2) - rho_ds_zm(1) * x'w'|_sfc ];
- !
- ! which can be re-written as:
- !
- ! - invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2)
- ! * { - K_zm(2) * dzm(2) * ( xm(3) - xm(2) ) }
- ! - rho_ds_zm(1)
- ! * { - [ u_star^2 / sqrt( um(2)^2 + vm(2)^2 ) ] * xm(2) } ];
- !
- ! which can be re-written as:
- !
- ! + invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(2) * K_zm(2) * dzm(2) * ( xm(3) - xm(2) )
- ! - invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(1) * [ u_star^2 / sqrt( um(2)^2 + vm(2)^2 ) ] * xm(2).
- !
- ! For this equation, a Crank-Nicholson (semi-implicit) diffusion scheme
- ! is used to solve the (1/rho_ds) * d [ rho_ds * K_zm * d(xm)/dz ] / dz
- ! eddy-diffusion term. The discretized implicit portion of the term is
- ! written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2) * K_zm(2)
- ! * dzm(2) * ( xm(3,) - xm(2,) ) ]
- ! - invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * rho_ds_zm(1)
- ! * [u_star^2/sqrt( um(2,)^2 + vm(2,)^2 )] * xm(2,).
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the signs are reversed and the leading "+" in front of the first
- ! part of the term is changed to a "-", while the leading "-" in
- ! front of the second part of the term is changed to a "+".
- !
- ! Note: The x'w'|_sfc portion of the term written above has been pulled
- ! away from the rest of the implicit form written above because
- ! the (1/2) factor due to Crank-Nicholson time_stepping does not
- ! apply to it. The x'w'|_sfc portion of the term is treated
- ! completely implicitly in order to enhance numerical stability.
- !
- ! The discretized explicit portion of the term is written out as:
- !
- ! + (1/2) * invrs_rho_ds_zt(2)
- ! * dzt(2)
- ! * [ rho_ds_zm(2) * K_zm(2)
- ! * dzm(2) * ( xm(3,) - xm(2,) ) ].
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which
- ! is being advanced to in solving the d(xm)/dt equation.
- !
- !
- ! The lower boundary condition for the implicit and explicit portions of the
- ! turbulent advection term, without the x'w'|_sfc portion of the term, can
- ! easily be invoked by using the zero-flux boundary conditions found in the
- ! generalized diffusion function (function diffusion_zt_lhs), which is used
- ! for many other equations in this model. Either the generalized explicit
- ! surface flux needs to be added onto the explicit term after the diffusion
- ! function has been called from subroutine windm_edsclrm_rhs, or the
- ! implicit momentum surface flux needs to be added onto the implicit term
- ! after the diffusion function has been called from subroutine
- ! windm_edsclrm_lhs. However, all other equations in this model that use
- ! zero-flux diffusion have level 1 as the level to which the lower boundary
- ! condition needs to be applied. Thus, an adjuster will have to be used at
- ! level 2 to call diffusion_zt_lhs with level 1 as the input level (the last
- ! variable being passed in during the function call). However, the other
- ! variables passed in (rho_ds_zm*K_zm, gr%dzt, and gr%dzm variables) will
- ! have to be passed in as solving for level 2.
- !
- ! The value of xm(1) is located below the model surface and does not effect
- ! the rest of the model. Since xm can be either a horizontal wind component
- ! or a generic eddy scalar quantity, the value of xm(1) is simply set to the
- ! value of xm(2) after the equation matrix has been solved.
- !
- !
- ! Conservation Properties:
- !
- ! When a fixed-flux lower boundary condition is used (combined with a
- ! zero-flux upper boundary condition), this technique of discretizing the
- ! turbulent advection term (treated as an eddy-diffusion term) leads to
- ! conservative differencing. When the implicit momentum surface flux is
- ! either zero or not used, the column totals for each column in the
- ! left-hand side matrix (for the turbulent advection term) should be equal
- ! to 0. Otherwise, the column total for the second column will be equal to
- ! rho_ds_zm(1) * x'w'|_sfc. When the generalized explicit surface
- ! flux is either zero or not used, the column total for the right-hand side
- ! vector (for the turbulent advection term) should be equal to 0.
- ! Otherwise, the column total for the right-hand side vector (for the
- ! turbulent advection term) will be equal to rho_ds_zm(1) * x'w'|_sfc.
- ! This ensures that the total amount of quantity xm over the entire vertical
- ! domain is only changed by the surface flux (neglecting any forcing terms).
- ! The total amount of change is equal to rho_ds_zm(1) * x'w'|_sfc.
- !
- ! To see that this conservation law is satisfied by the left-hand side
- ! matrix, compute the turbulent advection (treated as eddy diffusion) of xm,
- ! neglecting any implicit momentum surface flux, multiply by rho_ds_zt, and
- ! integrate vertically. In discretized matrix notation (where "i" stands
- ! for the matrix column and "j" stands for the matrix row):
- !
- ! 0 = Sum_j Sum_i
- ! (rho_ds_zt)_i ( 1/dzt )_i
- ! ( 0.5_core_rknd * (1/rho_ds_zt) * dzt * (rho_ds_zm*K_zm*dzm) )_ij (xm)_j.
- !
- ! The left-hand side matrix,
- ! ( 0.5_core_rknd * (1/rho_ds_zt) * dzt * (rho_ds_zm*K_zm*dzm) )_ij, is partially
- ! written below. The sum over i in the above equation removes (1/rho_ds_zt)
- ! and dzt everywhere from the matrix below. The sum over j leaves the
- ! column totals that are desired, which are 0.
- !
- ! Left-hand side matrix contributions from the turbulent advection term
- ! (treated as an eddy-diffusion term using a Crank-Nicholson timestep);
- ! first five vertical levels:
- !
- ! ------------------------------------------------------------------------------->
- !k=1 | 0 0 0 0
- ! |
- !k=2 | 0 +0.5* -0.5* 0
- ! | (1/rho_ds_zt(k))* (1/rho_ds_zt(k))*
- ! | dzt(k)* dzt(k)*
- ! | rho_ds_zm(k)* rho_ds_zm(k)*
- ! | K_zm(k)*dzm(k) K_zm(k)*dzm(k)
- ! |
- !k=3 | 0 -0.5* +0.5* -0.5*
- ! | (1/rho_ds_zt(k))* (1/rho_ds_zt(k))* (1/rho_ds_zt(k))*
- ! | dzt(k)* dzt(k)* dzt(k)*
- ! | rho_ds_zm(k-1)* [ rho_ds_zm(k)* rho_ds_zm(k)*
- ! | K_zm(k-1)*dzm(k-1) K_zm(k)*dzm(k) K_zm(k)*dzm(k)
- ! | +rho_ds_zm(k-1)*
- ! | K_zm(k-1)*dzm(k-1) ]
- ! |
- !k=4 | 0 0 -0.5* +0.5*
- ! | (1/rho_ds_zt(k))* (1/rho_ds_zt(k))*
- ! | dzt(k)* dzt(k)*
- ! | rho_ds_zm(k-1)* [ rho_ds_zm(k)*
- ! | K_zm(k-1)*dzm(k-1) K_zm(k)*dzm(k)
- ! | +rho_ds_zm(k-1)*
- ! | K_zm(k-1)*dzm(k-1) ]
- ! |
- !k=5 | 0 0 0 -0.5*
- ! | (1/rho_ds_zt(k))*
- ! | dzt(k)*
- ! | rho_ds_zm(k-1)*
- ! | K_zm(k-1)*dzm(k-1)
- ! \ /
- !
- ! Note: The superdiagonal term from level 4 and both the main diagonal and
- ! superdiagonal terms from level 5 are not shown on this diagram.
- !
- ! Note: If an implicit momentum surface flux is used, an additional term,
- ! + (1/rho_ds_zt(2)) * dzt(2) * rho_ds_zm(1)
- ! * [ u_star^2 / sqrt( um(2,)^2 + vm(2,)^2 ) ], is added to
- ! row 2 (k=2), column 2.
- !
- ! To see that the above conservation law is satisfied by the right-hand side
- ! vector, compute the turbulent advection (treated as eddy diffusion) of xm,
- ! neglecting any generalized explicit surface flux, multiply by rho_ds_zt,
- ! and integrate vertically. In discretized matrix notation (where "i"
- ! stands for the matrix column and "j" stands for the matrix row):
- !
- ! 0 = Sum_j Sum_i (rho_ds_zt)_i ( 1/dzt )_i ( rhs_vector )_j.
- !
- ! The right-hand side vector, ( rhs_vector )_j, is partially written below.
- ! The sum over i in the above equation removes (1/rho_ds_zt) and dzt
- ! everywhere from the vector below. The sum over j leaves the column total
- ! that is desired, which is 0.
- !
- ! Right-hand side vector contributions from the turbulent advection term
- ! (treated as an eddy-diffusion term using a Crank-Nicholson timestep);
- ! first five vertical levels:
- !
- ! --------------------------------------------
- !k=1 | 0 |
- ! | |
- ! | |
- !k=2 | +0.5*(1/rho_ds_zt(k))* |
- ! | dzt(k)* |
- ! | [ rho_ds_zm(k)*K_zm(k)* |
- ! | dzm(k)*(xm(k+1,)-xm(k,)) ] |
- ! | |
- !k=3 | +0.5*(1/rho_ds_zt(k))* |
- ! | dzt(k)* |
- ! | [ rho_ds_zm(k)*K_zm(k)* |
- ! | dzm(k)*(xm(k+1,)-xm(k,)) |
- ! | -rho_ds_zm(k-1)*K_zm(k-1)* |
- ! | dzm(k-1)*(xm(k,)-xm(k-1,)) ] |
- ! | |
- !k=4 | +0.5*(1/rho_ds_zt(k))* |
- ! | dzt(k)* |
- ! | [ rho_ds_zm(k)*K_zm(k)* |
- ! | dzm(k)*(xm(k+1,)-xm(k,)) |
- ! | -rho_ds_zm(k-1)*K_zm(k-1)* |
- ! | dzm(k-1)*(xm(k,)-xm(k-1,)) ] |
- ! | |
- !k=5 | +0.5*(1/rho_ds_zt(k))* |
- ! | dzt(k)* |
- ! | [ rho_ds_zm(k)*K_zm(k)* |
- ! | dzm(k)*(xm(k+1,)-xm(k,)) |
- ! | -rho_ds_zm(k-1)*K_zm(k-1)* |
- ! | dzm(k-1)*(xm(k,)-xm(k-1,)) ] |
- ! \ / \ /
- !
- ! Note: If a generalized explicit surface flux is used, an additional term,
- ! + (1/rho_ds_zt(2)) * dzt(2) * rho_ds_zm(1) * x'w'|_sfc, is added to
- ! row 2 (k=2).
- !
- ! Note: Only the contributions by the turbulent advection term are shown
- ! for both the left-hand side matrix and the right-hand side vector.
- ! There are more terms in the equation, and thus more factors to be
- ! added to both the left-hand side matrix (such as time tendency and
- ! mean advection) and the right-hand side vector (such as xm
- ! forcings). The left-hand side matrix is set-up so that a singular
- ! matrix is not encountered.
-
- ! References:
- ! Eqn. 8 & 9 on p. 3545 of
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_lapack_wrap, only: &
- tridag_solve, & ! Procedure(s)
- tridag_solvex
-
- use crmx_stats_variables, only: &
- sfc, & ! Variable(s)
- l_stats_samp
-
- use crmx_stats_type, only: &
- stat_update_var_pt ! Subroutine
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
-
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2, & ! Thermodynamic main diagonal index.
- km1_tdiag = 3 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
-
- integer, intent(in) :: &
- nrhs ! Number of right-hand side (explicit) vectors & Number of solution vectors.
-
- integer, intent(in) :: &
- ixm_matrix_condt_num ! Stats index of the condition numbers
-
- real( kind = core_rknd ), dimension(3,gr%nz), intent(inout) :: &
- lhs ! Implicit contributions to um, vm, and eddy scalars [units vary]
-
- real( kind = core_rknd ), dimension(gr%nz,nrhs), intent(inout) :: &
- rhs ! Right-hand side (explicit) contributions.
-
- real( kind = core_rknd ), dimension(gr%nz,nrhs), intent(out) :: &
- solution ! Solution to the system of equations [units vary]
-
- integer, intent(out) :: &
- err_code ! clubb_singular_matrix when matrix is singular
-
- ! Local variables
- real( kind = core_rknd ) :: &
- rcond ! Estimate of the reciprocal of the condition number on the LHS matrix
-
- ! Solve tridiagonal system for xm.
- if ( l_stats_samp .and. ixm_matrix_condt_num > 0 ) then
- call tridag_solvex &
- ( "windm_edsclrm", gr%nz, nrhs, & ! Intent(in)
- lhs(kp1_tdiag,:), lhs(k_tdiag,:), lhs(km1_tdiag,:), rhs, & ! Intent(inout)
- solution, rcond, err_code ) ! Intent(out)
-
- ! Est. of the condition number of the variance LHS matrix
- call stat_update_var_pt( ixm_matrix_condt_num, 1, 1.0_core_rknd/rcond, & ! Intent(in)
- sfc ) ! Intent(inout)
- else
-
- call tridag_solve( "windm_edsclrm", gr%nz, nrhs, & ! In
- lhs(kp1_tdiag,:), lhs(k_tdiag,:), lhs(km1_tdiag,:), rhs, & ! Inout
- solution, err_code ) ! Out
- end if
-
- return
- end subroutine windm_edsclrm_solve
-
- !=============================================================================
- subroutine windm_edsclrm_implicit_stats( solve_type, xm )
-
- ! Description:
- ! Compute implicit contributions to um and vm
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_stats_variables, only: &
- ium_ma, & ! Variables
- ium_ta, &
- ivm_ma, &
- ivm_ta, &
- ztscr01, &
- ztscr02, &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- ztscr06, &
- zt
-
- use crmx_stats_type, only: &
- stat_end_update_pt, & ! Subroutines
- stat_update_var_pt
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_grid_class, only: &
- gr ! Derived type variable
-
- implicit none
-
- ! Input variables
- integer, intent(in) :: &
- solve_type ! Desc. of what is being solved for
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- xm ! Computed value um or vm at [m/s]
-
- ! Local variables
- integer :: k, kp1, km1 ! Array indices
-
- ! Budget indices
- integer :: ixm_ma, ixm_ta
-
- select case ( solve_type )
- case ( windm_edsclrm_um )
- ixm_ma = ium_ma
- ixm_ta = ium_ta
-
- case ( windm_edsclrm_vm )
- ixm_ma = ivm_ma
- ixm_ta = ivm_ta
-
- case default
- ixm_ma = 0
- ixm_ta = 0
-
- end select
-
-
- ! Finalize implicit contributions for xm
-
- do k = 2, gr%nz-1, 1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! xm mean advection
- ! xm term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixm_ma, k, &
- ztscr01(k) * xm(km1) &
- + ztscr02(k) * xm(k) &
- + ztscr03(k) * xm(kp1), zt )
-
- ! xm turbulent transport (implicit component)
- ! xm term ta has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( ixm_ta, k, &
- ztscr04(k) * xm(km1) &
- + ztscr05(k) * xm(k) &
- + ztscr06(k) * xm(kp1), zt )
-
- enddo
-
-
- ! Upper boundary conditions
- k = gr%nz
- km1 = max( k-1, 1 )
-
- ! xm mean advection
- ! xm term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixm_ma, k, &
- ztscr01(k) * xm(km1) &
- + ztscr02(k) * xm(k), zt )
-
- ! xm turbulent transport (implicit component)
- ! xm term ta has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( ixm_ta, k, &
- ztscr04(k) * xm(km1) &
- + ztscr05(k) * xm(k), zt )
-
-
- return
- end subroutine windm_edsclrm_implicit_stats
-
- !=============================================================================
- subroutine compute_uv_tndcy( solve_type, fcor, perp_wind_m, perp_wind_g, xm_forcing, &
- l_implemented, xm_tndcy )
-
- ! Description:
- ! Computes the explicit tendency for the um and vm wind components.
- !
- ! The only explicit tendency that is involved in the d(um)/dt or d(vm)/dt
- ! equations is the Coriolis tendency.
- !
- ! The d(um)/dt equation contains the term:
- !
- ! - f * ( v_g - vm );
- !
- ! where f is the Coriolis parameter and v_g is the v component of the
- ! geostrophic wind.
- !
- ! Likewise, the d(vm)/dt equation contains the term:
- !
- ! + f * ( u_g - um );
- !
- ! where u_g is the u component of the geostrophic wind.
- !
- ! This term is treated completely explicitly. The values of um, vm, u_g,
- ! and v_g are all found on the thermodynamic levels.
- !
- ! Wind forcing from the GCSS cases is also added here.
- !
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr
-
- use crmx_stats_type, only: &
- stat_update_var
-
- use crmx_stats_variables, only: &
- ium_gf, &
- ium_cf, &
- ivm_gf, &
- ivm_cf, &
- ium_f, &
- ivm_f, &
- zt, &
- l_stats_samp
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Description of what is being solved for
-
- real( kind = core_rknd ), intent(in) :: &
- fcor ! Coriolis parameter [s^-1]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- perp_wind_m, & ! Perpendicular component of the mean wind (e.g. v, for the u-eqn) [m/s]
- perp_wind_g, & ! Perpendicular component of the geostropic wind (e.g. vg) [m/s]
- xm_forcing ! Prescribed wind forcing [m/s/s]
-
- logical, intent(in) :: &
- l_implemented ! Flag for CLUBB being implemented in a larger model.
-
- ! Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(out) :: &
- xm_tndcy ! xm tendency [m/s^2]
-
- ! Local Variables
- integer :: &
- ixm_gf, &
- ixm_cf, &
- ixm_f
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- xm_gf, &
- xm_cf
-
- ! --- Begin Code ---
-
- if ( .not. l_implemented ) then
- ! Only compute the Coriolis term if the model is running on it's own,
- ! and is not part of a larger, host model.
-
- select case ( solve_type )
-
- case ( windm_edsclrm_um )
-
- ixm_gf = ium_gf
- ixm_cf = ium_cf
- ixm_f = ium_f
-
- xm_gf = - fcor * perp_wind_g(1:gr%nz)
-
- xm_cf = fcor * perp_wind_m(1:gr%nz)
-
- case ( windm_edsclrm_vm )
-
- ixm_gf = ivm_gf
- ixm_cf = ivm_cf
- ixm_f = ivm_f
-
- xm_gf = fcor * perp_wind_g(1:gr%nz)
-
- xm_cf = -fcor * perp_wind_m(1:gr%nz)
-
- case default
-
- ixm_gf = 0
- ixm_cf = 0
- ixm_f = 0
-
- xm_gf = 0._core_rknd
-
-
- xm_cf = 0._core_rknd
-
- end select
-
- xm_tndcy(1:gr%nz) = xm_gf(1:gr%nz) + xm_cf(1:gr%nz) &
- + xm_forcing(1:gr%nz)
-
- if ( l_stats_samp ) then
-
- ! xm term gf is completely explicit; call stat_update_var.
- call stat_update_var( ixm_gf, xm_gf, zt )
-
- ! xm term cf is completely explicit; call stat_update_var.
- call stat_update_var( ixm_cf, xm_cf, zt )
-
- ! xm term F
- call stat_update_var( ixm_f, xm_forcing, zt )
- endif
-
- else ! implemented in a host model.
-
- xm_tndcy = 0.0_core_rknd
-
- endif
-
-
- return
- end subroutine compute_uv_tndcy
-
-!===============================================================================
- subroutine windm_edsclrm_lhs( dt, nu, wm_zt, Kh_zm, wind_speed, u_star_sqd, &
- rho_ds_zm, invrs_rho_ds_zt, &
- l_implemented, l_imp_sfc_momentum_flux, &
- lhs )
-
- ! Description:
- ! Calculate the implicit portion of the horizontal wind or eddy-scalar
- ! time-tendency equation. See the description in subroutine
- ! windm_edsclrm_solve for more details.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_diffusion, only: &
- diffusion_zt_lhs ! Procedure(s)
-
- use crmx_mean_adv, only: &
- term_ma_zt_lhs ! Procedures
-
- use crmx_stats_variables, only: &
- ium_ma, & ! Variable(s)
- ium_ta, &
- ivm_ma, &
- ivm_ta, &
- ztscr01, &
- ztscr02, &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- ztscr06, &
- l_stats_samp
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2, & ! Thermodynamic main diagonal index.
- km1_tdiag = 3 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- nu ! Background constant coef. of eddy diffusivity [m^2/s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- Kh_zm, & ! Eddy diffusivity on momentum levels [m^2/s]
- wind_speed, & ! wind speed; sqrt( u^2 + v^2 ) [m/s]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zt ! Inv. dry, static density at thermo. levels [m^3/kg]
-
- real( kind = core_rknd ), intent(in) :: &
- u_star_sqd ! Surface friction velocity, u_*, squared [m/s]
-
- logical, intent(in) :: &
- l_implemented, & ! Flag for CLUBB being implemented in a larger model.
- l_imp_sfc_momentum_flux ! Flag for implicit momentum surface fluxes.
-
- ! Output Variable
- real( kind = core_rknd ), dimension(3,gr%nz), intent(out) :: &
- lhs ! Implicit contributions to xm (tridiagonal matrix)
-
- ! Local Variables
- integer :: k, km1 ! Array indices
- integer :: diff_k_in
-
- real( kind = core_rknd ), dimension(3) :: tmp
-
- ! --- Begin Code ---
-
- ! Initialize the LHS array to zero.
- lhs = 0.0_core_rknd
-
- do k = 2, gr%nz, 1
-
- ! Define index
- km1 = max( k-1, 1 )
-
- ! LHS mean advection term.
- if ( .not. l_implemented ) then
-
- lhs(kp1_tdiag:km1_tdiag,k) &
- = lhs(kp1_tdiag:km1_tdiag,k) &
- + term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
-
- else
- ! The host model is assumed to apply the advection term to the mean elsewhere in this case.
- lhs(kp1_tdiag:km1_tdiag,k) &
- = lhs(kp1_tdiag:km1_tdiag,k) + 0.0_core_rknd
-
- endif
-
- ! LHS turbulent advection term (solved as an eddy-diffusion term).
- if ( k == 2 ) then
- ! The lower boundary condition needs to be applied here at level 2.
- ! The lower boundary condition is a "fixed flux" boundary condition.
- ! The coding is the same as for a zero-flux boundary condition, but with
- ! an extra term added on the right-hand side at the boundary level. For
- ! the rest of the model code, a zero-flux boundary condition is applied
- ! at level 1, and thus subroutine diffusion_zt_lhs is set-up to do that.
- ! In order to apply the same boundary condition code here at level 2, an
- ! adjuster needs to be used to tell diffusion_zt_lhs to use the code at
- ! level 2 that it normally uses at level 1.
- diff_k_in = 1
- else
- diff_k_in = k
- endif
- lhs(kp1_tdiag:km1_tdiag,k) &
- = lhs(kp1_tdiag:km1_tdiag,k) &
- + 0.5_core_rknd * invrs_rho_ds_zt(k) &
- * diffusion_zt_lhs( rho_ds_zm(k) * Kh_zm(k), &
- rho_ds_zm(km1) * Kh_zm(km1), nu, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), diff_k_in )
-
- ! LHS time tendency.
- lhs(k_tdiag,k) &
- = lhs(k_tdiag,k) + 1.0_core_rknd / real( dt, kind = core_rknd )
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for um or vm.
- ! Note: we don't track these budgets for the eddy scalar variables
-
- if ( ium_ma + ivm_ma > 0 ) then
- if ( .not. l_implemented ) then
- tmp(1:3) &
- = term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
- ztscr01(k) = -tmp(3)
- ztscr02(k) = -tmp(2)
- ztscr03(k) = -tmp(1)
- else
- ztscr01(k) = 0.0_core_rknd
- ztscr02(k) = 0.0_core_rknd
- ztscr03(k) = 0.0_core_rknd
- endif
- endif
-
- if ( ium_ta + ivm_ta > 0 ) then
- tmp(1:3) &
- = 0.5_core_rknd * invrs_rho_ds_zt(k) &
- * diffusion_zt_lhs( rho_ds_zm(k) * Kh_zm(k), &
- rho_ds_zm(km1) * Kh_zm(km1), nu, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), diff_k_in )
- ztscr04(k) = -tmp(3)
- ztscr05(k) = -tmp(2)
- ztscr06(k) = -tmp(1)
- endif
-
- endif ! l_stats_samp
-
- enddo ! k = 2 .. gr%nz
-
-
- ! Boundary Conditions
-
- ! Lower Boundary
-
- ! The lower boundary condition is a fixed-flux boundary condition, which
- ! gets added into the time-tendency equation at level 2.
- ! The value of xm(1) is located below the model surface and does not effect
- ! the rest of the model. Since xm can be either a horizontal wind component
- ! or a generic eddy scalar quantity, the value of xm(1) is simply set to the
- ! value of xm(2) after the equation matrix has been solved.
-
- ! k = 1
- lhs(k_tdiag,1) = 1.0_core_rknd
-
- ! k = 2; add implicit momentum surface flux.
- if ( l_imp_sfc_momentum_flux ) then
-
- ! LHS momentum surface flux.
- lhs(k_tdiag,2) &
- = lhs(k_tdiag,2) &
- + invrs_rho_ds_zt(2) &
- * gr%invrs_dzt(2) &
- * rho_ds_zm(1) * ( u_star_sqd / wind_speed(2) )
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for um or vm.
-
- ! xm term ta is modified at level 2 to include the effects of the
- ! surface flux. In this case, this effects the implicit portion of
- ! the term (after zmscr05, which handles the main diagonal for the
- ! turbulent advection term, has already been called at level 2).
- ! Modify zmscr05 accordingly.
- if ( ium_ta + ivm_ta > 0 ) then
- ztscr05(2) &
- = ztscr05(2) &
- - invrs_rho_ds_zt(2) &
- * gr%invrs_dzt(2) &
- * rho_ds_zm(1) * ( u_star_sqd / wind_speed(2) )
- endif
-
- endif ! l_stats_samp
-
- endif ! l_imp_sfc_momentum_flux
-
-
- return
- end subroutine windm_edsclrm_lhs
-
- !=============================================================================
- function windm_edsclrm_rhs( solve_type, dt, nu, Kh_zm, xm, xm_tndcy, &
- rho_ds_zm, invrs_rho_ds_zt, &
- l_imp_sfc_momentum_flux, xpwp_sfc ) &
- result( rhs )
-
- ! Description:
- ! Calculate the explicit portion of the horizontal wind or eddy-scalar
- ! time-tendency equation. See the description in subroutine
- ! windm_edsclrm_solve for more details.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_diffusion, only: &
- diffusion_zt_lhs ! Procedure(s)
-
- use crmx_stats_variables, only: &
- ium_ta, & ! Variable(s)
- ivm_ta, &
- zt, &
- l_stats_samp
-
- use crmx_stats_type, only: &
- stat_begin_update_pt, & ! Procedure(s)
- stat_modify_pt
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: max, min, real, trim
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Description of what is being solved for
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- nu ! Background constant coef. of eddy diffusivity [m^2/s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- Kh_zm, & ! Eddy diffusivity on momentum levels [m^2/s]
- xm, & ! Eddy-scalar variable, xm (thermo. levels) [units vary]
- xm_tndcy, & ! The explicit time-tendency acting on xm [units vary]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zt ! Inv. dry, static density at thermo. levels [m^3/kg]
-
- real( kind = core_rknd ), intent(in) :: &
- xpwp_sfc ! x'w' at the surface [units vary]
-
- logical, intent(in) :: &
- l_imp_sfc_momentum_flux ! Flag for implicit momentum surface fluxes.
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz) :: &
- rhs ! Right-hand side (explicit) contributions.
-
- ! Local Variables
- integer :: k, kp1, km1 ! Array indices
- integer :: diff_k_in
-
- ! For use in Crank-Nicholson eddy diffusion.
- real( kind = core_rknd ), dimension(3) :: rhs_diff
-
- integer :: ixm_ta
-
- ! --- Begin Code ---
-
- select case ( solve_type )
- case ( windm_edsclrm_um )
- ixm_ta = ium_ta
- case ( windm_edsclrm_vm )
- ixm_ta = ivm_ta
- case default ! Eddy scalars
- ixm_ta = 0
- end select
-
-
- ! Initialize the RHS vector.
- rhs = 0.0_core_rknd
-
- do k = 2, gr%nz-1, 1
-
- ! Define indices
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! RHS turbulent advection term (solved as an eddy-diffusion term).
- if ( k == 2 ) then
- ! The lower boundary condition needs to be applied here at level 2.
- ! The lower boundary condition is a "fixed flux" boundary condition.
- ! The coding is the same as for a zero-flux boundary condition, but with
- ! an extra term added on the right-hand side at the boundary level. For
- ! the rest of the model code, a zero-flux boundary condition is applied
- ! at level 1, and thus subroutine diffusion_zt_lhs is set-up to do that.
- ! In order to apply the same boundary condition code here at level 2, an
- ! adjuster needs to be used to tell diffusion_zt_lhs to use the code at
- ! level 2 that it normally uses at level 1.
- diff_k_in = 1
- else
- diff_k_in = k
- endif
- rhs_diff(1:3) &
- = 0.5_core_rknd * invrs_rho_ds_zt(k) &
- * diffusion_zt_lhs( rho_ds_zm(k) * Kh_zm(k), &
- rho_ds_zm(km1) * Kh_zm(km1), nu, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), diff_k_in )
- rhs(k) = rhs(k) &
- - rhs_diff(3) * xm(km1) &
- - rhs_diff(2) * xm(k) &
- - rhs_diff(1) * xm(kp1)
-
- ! RHS forcings.
- rhs(k) = rhs(k) + xm_tndcy(k)
-
- ! RHS time tendency
- rhs(k) = rhs(k) + 1.0_core_rknd / real ( dt, kind = core_rknd ) * xm(k)
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for um or vm.
-
- ! xm term ta has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on right-hand side
- ! turbulent advection component.
- if ( ixm_ta > 0 ) then
- call stat_begin_update_pt( ixm_ta, k, &
- rhs_diff(3) * xm(km1) &
- + rhs_diff(2) * xm(k) &
- + rhs_diff(1) * xm(kp1), zt )
- endif
-
- endif ! l_stats_samp
-
- enddo ! 2..gr%nz-1
-
-
- ! Boundary Conditions
-
- ! Lower Boundary
-
- ! The lower boundary condition is a fixed-flux boundary condition, which
- ! gets added into the time-tendency equation at level 2.
- ! The value of xm(1) is located below the model surface and does not effect
- ! the rest of the model. Since xm can be either a horizontal wind component
- ! or a generic eddy scalar quantity, the value of xm(1) is simply set to the
- ! value of xm(2) after the equation matrix has been solved. For purposes of
- ! the matrix equation, rhs(1) is simply set to 0.
-
- ! k = 1
- rhs(1) = 0.0_core_rknd
-
- ! k = 2; add generalized explicit surface flux.
- if ( .not. l_imp_sfc_momentum_flux ) then
-
- ! RHS generalized surface flux.
- rhs(2) &
- = rhs(2) &
- + invrs_rho_ds_zt(2) &
- * gr%invrs_dzt(2) &
- * rho_ds_zm(1) * xpwp_sfc
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for um or vm.
-
- ! xm term ta is modified at level 2 to include the effects of the
- ! surface flux. In this case, this effects the explicit portion of
- ! the term (after stat_begin_update_pt has already been called at
- ! level 2); call stat_modify_pt.
- if ( ixm_ta > 0 ) then
- call stat_modify_pt( ixm_ta, 2, &
- + invrs_rho_ds_zt(2) &
- * gr%invrs_dzt(2) &
- * rho_ds_zm(1) * xpwp_sfc, &
- zt )
- endif
-
- endif ! l_stats_samp
-
- endif ! l_imp_sfc_momentum_flux
-
- ! Upper Boundary
-
- ! A zero-flux boundary condition is used at the upper boundary, meaning that
- ! xm is not allowed to exit the model through the upper boundary. This
- ! boundary condition is invoked by calling diffusion_zt_lhs at the uppermost
- ! level.
- k = gr%nz
- km1 = max( k-1, 1 )
-
- ! RHS turbulent advection term (solved as an eddy-diffusion term) at the
- ! upper boundary.
- rhs_diff(1:3) &
- = 0.5_core_rknd * invrs_rho_ds_zt(k) &
- * diffusion_zt_lhs( rho_ds_zm(k) * Kh_zm(k), &
- rho_ds_zm(km1) * Kh_zm(km1), nu, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- rhs(k) = rhs(k) &
- - rhs_diff(3) * xm(km1) &
- - rhs_diff(2) * xm(k)
-
- ! RHS forcing term at the upper boundary.
- rhs(k) = rhs(k) + xm_tndcy(k)
-
- ! RHS time tendency term at the upper boundary.
- rhs(k) = rhs(k) + 1.0_core_rknd / real( dt, kind = core_rknd ) * xm(k)
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for um or vm.
-
- ! xm term ta has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on right-hand side
- ! turbulent advection component.
- if ( ixm_ta > 0 ) then
- call stat_begin_update_pt( ixm_ta, k, &
- rhs_diff(3) * xm(km1) &
- + rhs_diff(2) * xm(k), zt )
- endif
-
- endif ! l_stats_samp
-
-
- return
- end function windm_edsclrm_rhs
-
-!===============================================================================
- elemental function xpwp_fnc( Kh_zm, xm, xmp1, invrs_dzm )
-
- ! Description:
- ! Compute x'w' from x, x, Kh and invrs_dzm
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- real( kind = core_rknd ), intent(in) :: &
- Kh_zm, & ! Eddy diff. (k momentum level) [m^2/s]
- xm, & ! x (k thermo level) [units vary]
- xmp1, & ! x (k+1 thermo level) [units vary]
- invrs_dzm ! Inverse of the grid spacing (k thermo level) [1/m]
-
- ! Output variable
- real( kind = core_rknd ) :: &
- xpwp_fnc ! x'w' [(units vary)(m/s)]
-
- !-----------------------------------------------------------------------
- ! --- Begin Code ---
-
- ! Solve for x'w' at all intermediate model levels.
- xpwp_fnc = Kh_zm * invrs_dzm * ( xmp1 - xm )
-
- return
- end function xpwp_fnc
-
-!===============================================================================
-
-end module crmx_advance_windm_edsclrm_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_advance_wp2_wp3_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_advance_wp2_wp3_module.F90
deleted file mode 100644
index cefd03f334..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_advance_wp2_wp3_module.F90
+++ /dev/null
@@ -1,4427 +0,0 @@
-!------------------------------------------------------------------------
-! $Id: advance_wp2_wp3_module.F90 6146 2013-04-05 18:02:22Z raut@uwm.edu $
-!===============================================================================
-module crmx_advance_wp2_wp3_module
-
- implicit none
-
- private ! Default Scope
-
- public :: advance_wp2_wp3
-
- private :: wp23_solve, &
- wp23_lhs, &
- wp23_rhs, &
- wp2_term_ta_lhs, &
- wp2_terms_ac_pr2_lhs, &
- wp2_term_dp1_lhs, &
- wp2_term_pr1_lhs, &
- wp2_terms_bp_pr2_rhs, &
- wp2_term_dp1_rhs, &
- wp2_term_pr3_rhs, &
- wp2_term_pr1_rhs, &
- wp3_terms_ta_tp_lhs, &
- wp3_terms_ac_pr2_lhs, &
- wp3_term_pr1_lhs, &
- wp3_terms_bp1_pr2_rhs, &
- wp3_term_pr1_rhs, &
- wp3_term_bp2_rhs
-
-! private :: wp3_terms_ta_tp_rhs
-
- ! Private named constants to avoid string comparisons
- integer, parameter, private :: &
- clip_wp2 = 12 ! Named constant for wp2 clipping.
- ! NOTE: This must be the same as the clip_wp2 declared in
- ! clip_explicit!
-
- contains
-
- !=============================================================================
- subroutine advance_wp2_wp3( dt, sfc_elevation, sigma_sqd_w, wm_zm, wm_zt, &
- a3, a3_zt, wp3_on_wp2, &
- wpthvp, wp2thvp, um, vm, upwp, vpwp, &
- up2, vp2, Kh_zm, Kh_zt, tau_zm, tau_zt, &
- Skw_zm, Skw_zt, rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_rho_ds_zt, radf, &
- thv_ds_zm, thv_ds_zt, mixt_frac, &
- wp2, wp3, wp3_zm, wp2_zt, err_code )
-
- ! Description:
- ! Advance w'^2 and w'^3 one timestep.
-
- ! References:
- ! Eqn. 12 & 18 on p. 3545--3546 of
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
-
- ! See also
- ! ``Equations for CLUBB'', Section 6:
- ! /Implict solution for the vertical velocity moments/
- !------------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr, & ! Variable(s)
- zt2zm, & ! Procedure(s)
- zm2zt
-
- use crmx_parameters_tunable, only: &
- C11c, & ! Variable(s)
- C11b, &
- C11, &
- C1c, &
- C1b, &
- C1, &
- c_K1, &
- c_K8
-
- use crmx_stats_type, only: &
- stat_update_var
-
- use crmx_stats_variables, only: &
- iC1_Skw_fnc, &
- iC11_Skw_fnc, &
- zm, &
- zt, &
- l_stats_samp
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_model_flags, only: &
- l_hyper_dfsn ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_error_code, only: &
- fatal_error, & ! Procedure(s)
- clubb_at_least_debug_level
-
- use crmx_error_code, only: &
- clubb_var_out_of_range ! Constant(s)
-
- implicit none
-
- intrinsic :: exp
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- sigma_sqd_w, & ! sigma_sqd_w (momentum levels) [-]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- a3, & ! a_3 (momentum levels); See eqn. 25 in `Equations for CLUBB' [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- wpthvp, & ! w'th_v' (momentum levels) [K m/s]
- wp2thvp, & ! w'^2th_v' (thermodynamic levels) [K m^2/s^2]
- um, & ! u wind component (thermodynamic levels) [m/s]
- vm, & ! v wind component (thermodynamic levels) [m/s]
- upwp, & ! u'w' (momentum levels) [m^2/s^2]
- vpwp, & ! v'w' (momentum levels) [m^2/s^2]
- up2, & ! u'^2 (momentum levels) [m^2/s^2]
- vp2, & ! v'^2 (momentum levels) [m^2/s^2]
- Kh_zm, & ! Eddy diffusivity on momentum levels [m^2/s]
- Kh_zt, & ! Eddy diffusivity on thermodynamic levels [m^2/s]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- tau_zt, & ! Time-scale tau on thermodynamic levels [s]
- Skw_zm, & ! Skewness of w on momentum levels [-]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- radf, & ! Buoyancy production at the CL top [m^2/s^3]
- thv_ds_zm, & ! Dry, base-state theta_v on momentum levs. [K]
- thv_ds_zt, & ! Dry, base-state theta_v on thermo. levs. [K]
- mixt_frac ! Weight of 1st normal distribution [-]
-
- ! Input/Output
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp3, & ! w'^3 (thermodynamic levels) [m^3/s^3]
- wp3_zm ! w'^3 interpolated to momentum levels [m^3/s^3]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp2_zt ! w'^2 interpolated to thermodyamic levels [m^2/s^2]
-
- integer, intent(inout) :: err_code ! Diagnostic
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- tauw3t ! Currently just tau_zt [s]
-
- ! Eddy Diffusion for w'^2 and w'^3.
- real( kind = core_rknd ), dimension(gr%nz) :: Kw1 ! w'^2 coef. eddy diff. [m^2/s]
- real( kind = core_rknd ), dimension(gr%nz) :: Kw8 ! w'^3 coef. eddy diff. [m^2/s]
-
- ! Internal variables for C11 function, Vince Larson 13 Mar 2005
- ! Brian added C1 function.
- real( kind = core_rknd ), dimension(gr%nz) :: &
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc ! C_11 parameter with Sk_w applied [-]
- ! End Vince Larson's addition.
-
- integer :: &
- nsub, & ! Number of subdiagonals in the LHS matrix.
- nsup ! Number of superdiagonals in the LHS matrix.
-
- integer :: k ! Array indices
-
- integer :: wp2_wp3_err_code ! Error code from solving for wp2/wp3
-
-
- !-----------------------------------------------------------------------
-
-
-
-! Define tauw
-
-! tauw3t = tau_zt
-! . / ( 1.
-! . + 3.0_core_rknd * max(
-! . min(1.-(mixt_frac-0.01_core_rknd)/(0.05_core_rknd-0.01_core_rknd)
-! . ,1.)
-! . ,0.)
-! . + 3.0_core_rknd * max(
-! . min(1.-(mixt_frac-0.99_core_rknd)/(0.95_core_rknd-0.99_core_rknd)
-! . ,1.)
-! . ,0.)
-! . )
-
-! do k=1,gr%nz
-!
-! Skw = abs( wp3(k)/max(wp2(k),1.e-8)**1.5_core_rknd )
-! Skw = min( 5.0_core_rknd, Skw )
-! tauw3t(k) = tau_zt(k) / ( 0.005_core_rknd*Skw**4 + 1.0_core_rknd )
-!
-! end do
-
- tauw3t = tau_zt
-
- ! Vince Larson added code to make C11 function of Skw. 13 Mar 2005
- ! If this code is used, C11 is no longer relevant, i.e. constants
- ! are hardwired.
-
- ! Calculate C_{1} and C_{11} as functions of skewness of w.
- ! The if..then here is only for computational efficiency -dschanen 2 Sept 08
- if ( C11 /= C11b ) then
- C11_Skw_fnc(1:gr%nz) = &
- C11b + (C11-C11b)*EXP( -(1.0_core_rknd/2.0_core_rknd) * (Skw_zt(1:gr%nz)/C11c)**2 )
- else
- C11_Skw_fnc(1:gr%nz) = C11b
- end if
-
- ! The if..then here is only for computational efficiency -dschanen 2 Sept 08
- if ( C1 /= C1b ) then
- C1_Skw_fnc(1:gr%nz) = &
- C1b + (C1-C1b)*EXP( -(1.0_core_rknd/2.0_core_rknd) * (Skw_zm(1:gr%nz)/C1c)**2 )
- else
- C1_Skw_fnc(1:gr%nz) = C1b
- end if
-
- !C11_Skw_fnc = C11
- !C1_Skw_fnc = C1
-
- if ( clubb_at_least_debug_level( 2 ) ) then
- ! Assertion check for C11_Skw_fnc
- if ( any( C11_Skw_fnc(:) > 1._core_rknd ) .or. any( C11_Skw_fnc(:) < 0._core_rknd ) ) then
- write(fstderr,*) "The C11_Skw_fnc is outside the valid range for this variable"
- err_code = clubb_var_out_of_range
- return
- end if
- end if
-
- if ( l_stats_samp ) then
- call stat_update_var( iC11_Skw_fnc, C11_Skw_fnc, zt )
- call stat_update_var( iC1_Skw_fnc, C1_Skw_fnc, zm )
- endif
-
- ! Define the Coefficent of Eddy Diffusivity for the wp2 and wp3.
- do k = 1, gr%nz, 1
-
- ! Kw1 is used for wp2, which is located on momentum levels.
- ! Kw1 is located on thermodynamic levels.
- ! Kw1 = c_K1 * Kh_zt
- Kw1(k) = c_K1 * Kh_zt(k)
-
- ! Kw8 is used for wp3, which is located on thermodynamic levels.
- ! Kw8 is located on momentum levels.
- ! Note: Kw8 is usually defined to be 1/2 of Kh_zm.
- ! Kw8 = c_K8 * Kh_zm
- Kw8(k) = c_K8 * Kh_zm(k)
-
- enddo
-
- ! Declare the number of subdiagonals and superdiagonals in the LHS matrix.
- if ( l_hyper_dfsn ) then
- ! There are nine overall diagonals (including four subdiagonals
- ! and four superdiagonals).
- nsub = 4
- nsup = 4
- else
- ! There are five overall diagonals (including two subdiagonals
- ! and two superdiagonals).
- nsub = 2
- nsup = 2
- endif
-
- ! Solve semi-implicitly
- call wp23_solve( dt, sfc_elevation, sigma_sqd_w, wm_zm, wm_zt, & ! Intent(in)
- a3, a3_zt, wp3_on_wp2, & ! Intent(in)
- wpthvp, wp2thvp, um, vm, upwp, vpwp, & ! Intent(in)
- up2, vp2, Kw1, Kw8, Kh_zt, Skw_zt, tau_zm, tauw3t, & ! Intent(in)
- C1_Skw_fnc, C11_Skw_fnc, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, radf, thv_ds_zm, & ! Intent(in)
- thv_ds_zt, nsub, nsup, & ! Intent(in)
- wp2, wp3, wp3_zm, wp2_zt, wp2_wp3_err_code ) ! Intent(inout)
-
-! Error output
-! Joshua Fasching Feb 2008
- if ( fatal_error( wp2_wp3_err_code ) ) then
-
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "Errors in advance_wp2_wp3"
-
- write(fstderr,*) "Intent(in)"
-
- write(fstderr,*) "dt = ", dt
- write(fstderr,*) "sfc_elevation = ", sfc_elevation
- write(fstderr,*) "sigma_sqd_w = ", sigma_sqd_w
- write(fstderr,*) "wm_zm = ", wm_zm
- write(fstderr,*) "wm_zt = ", wm_zt
- write(fstderr,*) "wpthvp = ", wpthvp
- write(fstderr,*) "wp2thvp = ", wp2thvp
- write(fstderr,*) "um = ", um
- write(fstderr,*) "vm = ", vm
- write(fstderr,*) "upwp = ", upwp
- write(fstderr,*) "vpwp = ", vpwp
- write(fstderr,*) "up2 = ", up2
- write(fstderr,*) "vp2 = ", vp2
- write(fstderr,*) "Kh_zm = ", Kh_zm
- write(fstderr,*) "Kh_zt = ", Kh_zt
- write(fstderr,*) "tau_zm = ", tau_zm
- write(fstderr,*) "tau_zt = ", tau_zt
- write(fstderr,*) "Skw_zm = ", Skw_zm
- write(fstderr,*) "Skw_zt = ", Skw_zt
- write(fstderr,*) "mixt_frac = ", mixt_frac
- write(fstderr,*) "wp2zt = ", wp2_zt
-
- write(fstderr,*) "Intent(in/out)"
-
- write(fstderr,*) "wp2 = ", wp2
- write(fstderr,*) "wp3 = ", wp3
-
- end if
-
- err_code = wp2_wp3_err_code
- end if ! fatal error
-
- return
-
- end subroutine advance_wp2_wp3
-
- !=============================================================================
- subroutine wp23_solve( dt, sfc_elevation, sigma_sqd_w, wm_zm, wm_zt, &
- a3, a3_zt, wp3_on_wp2, &
- wpthvp, wp2thvp, um, vm, upwp, vpwp, &
- up2, vp2, Kw1, Kw8, Kh_zt, Skw_zt, tau1m, tauw3t, &
- C1_Skw_fnc, C11_Skw_fnc, rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_rho_ds_zt, radf, thv_ds_zm, &
- thv_ds_zt, nsub, nsup, &
- wp2, wp3, wp3_zm, wp2_zt, err_code )
-
- ! Description:
- ! Decompose, and back substitute the matrix for wp2/wp3
-
- ! References:
- ! _Equations for CLUBB_ section 6.3
- !------------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_grid_class, only: &
- zm2zt, & ! Function(s)
- zt2zm, &
- ddzt
-
- use crmx_constants_clubb, only: &
- w_tol_sqd, & ! Variables(s)
- eps, &
- zero_threshold, &
- fstderr
-
- use crmx_model_flags, only: &
- l_tke_aniso, & ! Variable(s)
- l_hyper_dfsn, &
- l_hole_fill, &
- l_gmres
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_lapack_wrap, only: &
- band_solve, & ! Procedure(s)
- band_solvex
-
- use crmx_fill_holes, only: &
- fill_holes_driver
-
- use crmx_clip_explicit, only: &
- clip_variance, & ! Procedure(s)
- clip_skewness
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_update_var_pt, &
- stat_end_update, &
- stat_end_update_pt
-
- use crmx_stats_variables, only: &
- zm, & ! Variable(s)
- zt, &
- sfc, &
- l_stats_samp, &
- iwp2_ta, &
- iwp2_ma, &
- iwp2_pd, &
- iwp2_ac, &
- iwp2_dp1, &
- iwp2_dp2, &
- iwp2_pr1, &
- iwp2_pr2, &
- iwp2_4hd, &
- iwp3_ta, &
- iwp3_ma, &
- iwp3_tp, &
- iwp3_ac, &
- iwp3_dp1, &
- iwp3_pr1, &
- iwp3_pr2, &
- iwp3_4hd, &
- iwp23_matrix_condt_num
-
- use crmx_stats_variables, only: &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr10, &
- zmscr11, &
- zmscr12, &
- zmscr13, &
- zmscr14, &
- zmscr15, &
- zmscr16, &
- zmscr17, &
- ztscr01, &
- ztscr02
-
- use crmx_stats_variables, only: &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- ztscr06, &
- ztscr07, &
- ztscr08, &
- ztscr09, &
- ztscr10, &
- ztscr11, &
- ztscr12, &
- ztscr13, &
- ztscr14, &
- ztscr15, &
- ztscr16, &
- ztscr17, &
- ztscr18, &
- ztscr19, &
- ztscr20, &
- ztscr21
-
- implicit none
-
- ! External
- intrinsic :: max, min, sqrt
-
- ! Parameter Constants
- integer, parameter :: &
- nrhs = 1 ! Number of RHS vectors
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- sigma_sqd_w, & ! sigma_sqd_w (momentum levels) [-]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- a3, & ! a_3 (momentum levels); See eqn. 25 in `Equations for CLUBB' [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- wpthvp, & ! w'th_v' (momentum levels) [K m/s]
- wp2thvp, & ! w'^2th_v' (thermodynamic levels) [K m^2/s^2]
- um, & ! u wind component (thermodynamic levels) [m/s]
- vm, & ! v wind component (thermodynamic levels) [m/s]
- upwp, & ! u'w' (momentum levels) [m^2/s^2]
- vpwp, & ! v'w' (momentum levels) [m^2/s^2]
- up2, & ! u'^2 (momentum levels) [m^2/s^2]
- vp2, & ! v'^2 (momentum levels) [m^2/s^2]
- Kw1, & ! Coefficient of eddy diffusivity for w'^2 [m^2/s]
- Kw8, & ! Coefficient of eddy diffusivity for w'^3 [m^2/s]
- Kh_zt, & ! Eddy diffusivity on thermodynamic levels [m^2/s]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- tau1m, & ! Time-scale tau on momentum levels [s]
- tauw3t, & ! Time-scale tau on thermodynamic levels [s]
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- radf, & ! Buoyancy production at CL top [m^2/s^3]
- thv_ds_zm, & ! Dry, base-state theta_v on momentum levs. [K]
- thv_ds_zt ! Dry, base-state theta_v on thermo. levs. [K]
-
- integer, intent(in) :: &
- nsub, & ! Number of subdiagonals in the LHS matrix.
- nsup ! Number of superdiagonals in the LHS matrix.
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp3, & ! w'^3 (thermodynamic levels) [m^3/s^3]
- wp3_zm ! w'^3 interpolated to momentum levels [m^3/s^3]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp2_zt ! w'^2 interpolated to thermodyamic levels [m^2/s^2]
-
- integer, intent(inout) :: err_code ! Have any errors occured?
-
- ! Local Variables
- real( kind = core_rknd ), dimension(nsup+nsub+1,2*gr%nz) :: &
- lhs ! Implicit contributions to wp2/wp3 (band diag. matrix)
-
- real( kind = core_rknd ), dimension(2*gr%nz) :: &
- rhs ! RHS of band matrix
-
-! real, target, dimension(2*gr%nz) ::
- real( kind = core_rknd ), dimension(2*gr%nz) :: &
- solut ! Solution to band diagonal system.
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- a1, & ! a_1 (momentum levels); See eqn. 23 in `Equations for CLUBB' [-]
- a1_zt ! a_1 interpolated to thermodynamic levels [-]
-
-! real, dimension(gr%nz) :: &
-! wp2_n ! w'^2 at the previous timestep [m^2/s^2]
-
- real( kind = core_rknd ) :: &
- rcond ! Est. of the reciprocal of the condition #
-
- ! Array indices
- integer :: k, km1, km2, kp1, kp2, k_wp2, k_wp3
-
- ! Set logical to true for Crank-Nicholson diffusion scheme
- ! or to false for completely implicit diffusion scheme.
- ! Note: Although Crank-Nicholson diffusion has usually been used for wp2
- ! and wp3 in the past, we found that using completely implicit
- ! diffusion stabilized the deep convective cases more while having
- ! almost no effect on the boundary layer cases. Brian; 1/4/2008.
-! logical, parameter :: l_crank_nich_diff = .true.
- logical, parameter :: l_crank_nich_diff = .false.
-
- ! Define a_1 and a_3 (both are located on momentum levels).
- ! They are variables that are both functions of sigma_sqd_w (where
- ! sigma_sqd_w is located on momentum levels).
-
- a1 = 1.0_core_rknd / ( 1.0_core_rknd - sigma_sqd_w )
-
- ! Interpolate a_1 from momentum levels to thermodynamic
- ! levels. This will be used for the w'^3 turbulent advection
- ! (ta) and turbulent production (tp) combined term.
- a1_zt = max( zm2zt( a1 ), zero_threshold ) ! Positive definite quantity
-
- ! Compute the explicit portion of the w'^2 and w'^3 equations.
- ! Build the right-hand side vector.
- call wp23_rhs( dt, wp2, wp3, a1, a1_zt, &
- a3, a3_zt, wp3_on_wp2, wpthvp, wp2thvp, um, vm, &
- upwp, vpwp, up2, vp2, Kw1, Kw8, Kh_zt, &
- Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, invrs_rho_ds_zt, radf, &
- thv_ds_zm, thv_ds_zt, l_crank_nich_diff, &
- rhs )
-
- if (l_gmres) then
- call wp23_gmres( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsup, nsub, nrhs, &
- rhs, &
- solut, err_code )
- else
- ! Compute the implicit portion of the w'^2 and w'^3 equations.
- ! Build the left-hand side matrix.
- call wp23_lhs( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsub, nsup, &
- lhs )
-
- ! Solve the system with LAPACK
- if ( l_stats_samp .and. iwp23_matrix_condt_num > 0 ) then
-
- ! Perform LU decomp and solve system (LAPACK with diagnostics)
- ! Note that this can change the answer slightly
- call band_solvex( "wp2_wp3", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solut, rcond, err_code )
-
- ! Est. of the condition number of the w'^2/w^3 LHS matrix
- call stat_update_var_pt( iwp23_matrix_condt_num, 1, 1.0_core_rknd / rcond, sfc )
-
- else
- ! Perform LU decomp and solve system (LAPACK)
- call band_solve( "wp2_wp3", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solut, err_code )
- end if
-
- end if ! l_gmres
-
- ! Copy result into output arrays and clip
-
- do k = 1, gr%nz
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
- ! wp2_n(k) = wp2(k) ! For the positive definite scheme
-
- wp2(k) = solut(k_wp2)
- wp3(k) = solut(k_wp3)
-
- end do
-
- if (l_stats_samp) then
-
- ! Finalize implicit contributions for wp2
-
- do k = 2, gr%nz-1
-
- km1 = max( k-1, 1 )
- km2 = max( k-2, 1 )
- kp1 = min( k+1, gr%nz )
- kp2 = min( k+2, gr%nz )
-
- ! w'^2 term dp1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp2_dp1, k, &
- zmscr01(k) * wp2(k), zm )
-
- ! w'^2 term dp2 has both implicit and explicit components (if the
- ! Crank-Nicholson scheme is selected); call stat_end_update_pt.
- ! If Crank-Nicholson diffusion is not selected, then w'^3 term dp1 is
- ! completely implicit; call stat_update_var_pt.
- if ( l_crank_nich_diff ) then
- call stat_end_update_pt( iwp2_dp2, k, &
- zmscr02(k) * wp2(km1) &
- + zmscr03(k) * wp2(k) &
- + zmscr04(k) * wp2(kp1), zm )
- else
- call stat_update_var_pt( iwp2_dp2, k, &
- zmscr02(k) * wp2(km1) &
- + zmscr03(k) * wp2(k) &
- + zmscr04(k) * wp2(kp1), zm )
- endif
-
- ! w'^2 term ta is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp2_ta, k, &
- zmscr05(k) * wp3(k) &
- + zmscr06(k) * wp3(kp1), zm )
-
- ! w'^2 term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp2_ma, k, &
- zmscr07(k) * wp2(km1) &
- + zmscr08(k) * wp2(k) &
- + zmscr09(k) * wp2(kp1), zm )
-
- ! w'^2 term ac is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp2_ac, k, &
- zmscr10(k) * wp2(k), zm )
-
- ! w'^2 term pr1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- if ( l_tke_aniso ) then
- call stat_end_update_pt( iwp2_pr1, k, &
- zmscr12(k) * wp2(k), zm )
- endif
-
- ! w'^2 term pr2 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp2_pr2, k, &
- zmscr11(k) * wp2(k), zm )
-
- ! w'^2 term 4hd is completely implicit; call stat_update_var_pt.
- if ( l_hyper_dfsn ) then
- call stat_update_var_pt( iwp2_4hd, k, &
- zmscr13(k) * wp2(km2) &
- + zmscr14(k) * wp2(km1) &
- + zmscr15(k) * wp2(k) &
- + zmscr16(k) * wp2(kp1) &
- + zmscr17(k) * wp2(kp2), zm )
- endif
- enddo
-
- ! Finalize implicit contributions for wp3
-
- do k = 2, gr%nz-1, 1
-
- km1 = max( k-1, 1 )
- km2 = max( k-2, 1 )
- kp1 = min( k+1, gr%nz )
- kp2 = min( k+2, gr%nz )
-
- ! w'^3 term pr1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp3_pr1, k, &
- ztscr01(k) * wp3(k), zt )
-
- ! w'^3 term dp1 has both implicit and explicit components (if the
- ! Crank-Nicholson scheme is selected); call stat_end_update_pt.
- ! If Crank-Nicholson diffusion is not selected, then w'^3 term dp1 is
- ! completely implicit; call stat_update_var_pt.
- if ( l_crank_nich_diff ) then
- call stat_end_update_pt( iwp3_dp1, k, &
- ztscr02(k) * wp3(km1) &
- + ztscr03(k) * wp3(k) &
- + ztscr04(k) * wp3(kp1), zt )
- else
- call stat_update_var_pt( iwp3_dp1, k, &
- ztscr02(k) * wp3(km1) &
- + ztscr03(k) * wp3(k) &
- + ztscr04(k) * wp3(kp1), zt )
- endif
-
- ! w'^3 term ta has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp3_ta, k, &
- ztscr05(k) * wp3(km1) &
- + ztscr06(k) * wp2(km1) &
- + ztscr07(k) * wp3(k) &
- + ztscr08(k) * wp2(k) &
- + ztscr09(k) * wp3(kp1), zt )
-
- ! w'^3 term tp has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp3_tp, k, &
- ztscr10(k) * wp2(km1) &
- + ztscr11(k) * wp2(k), zt )
-
- ! w'^3 term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp3_ma, k, &
- ztscr12(k) * wp3(km1) &
- + ztscr13(k) * wp3(k) &
- + ztscr14(k) * wp3(kp1), zt )
-
- ! w'^3 term ac is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp3_ac, k, &
- ztscr15(k) * wp3(k), zt )
-
- ! w'^3 term pr2 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwp3_pr2, k, &
- ztscr16(k) * wp3(k), zt )
-
- ! w'^3 term 4hd is completely implicit; call stat_update_var_pt.
- if ( l_hyper_dfsn ) then
- call stat_update_var_pt( iwp3_4hd, k, &
- ztscr17(k) * wp3(km2) &
- + ztscr18(k) * wp3(km1) &
- + ztscr19(k) * wp3(k) &
- + ztscr20(k) * wp3(kp1) &
- + ztscr21(k) * wp3(kp2), zt )
- endif
- enddo
-
- endif ! l_stats_samp
-
-
- if ( l_stats_samp ) then
- ! Store previous value for effect of the positive definite scheme
- call stat_begin_update( iwp2_pd, wp2 / real( dt, kind = core_rknd ), zm )
- endif
-
- if ( l_hole_fill .and. any( wp2 < w_tol_sqd ) ) then
-
- ! Use a simple hole filling algorithm
- call fill_holes_driver( 2, w_tol_sqd, "zm", &
- rho_ds_zt, rho_ds_zm, &
- wp2 )
-
- endif ! wp2
-
- ! Here we attempt to clip extreme values of wp2 to prevent a crash of the
- ! type found on the Climate Process Team ticket #49. Chris Golaz found that
- ! instability caused by large wp2 in CLUBB led unrealistic results in AM3.
- ! -dschanen 11 Apr 2011
- where ( wp2 > 1000._core_rknd ) wp2 = 1000._core_rknd
-
- if ( l_stats_samp ) then
- ! Store updated value for effect of the positive definite scheme
- call stat_end_update( iwp2_pd, wp2 / real( dt, kind = core_rknd ), zm )
- endif
-
-
- ! Clip w'^2 at a minimum threshold.
- call clip_variance( clip_wp2, dt, w_tol_sqd, wp2 )
-
- ! Interpolate w'^2 from momentum levels to thermodynamic levels.
- ! This is used for the clipping of w'^3 according to the value
- ! of Sk_w now that w'^2 and w'^3 have been advanced one timestep.
- wp2_zt = max( zm2zt( wp2 ), w_tol_sqd ) ! Positive definite quantity
-
- ! Clip w'^3 by limiting skewness.
- call clip_skewness( dt, sfc_elevation, wp2_zt, wp3 )
-
- ! Compute wp3_zm for output purposes
- wp3_zm = zt2zm( wp3 )
-
- return
- end subroutine wp23_solve
-
- subroutine wp23_gmres( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsup, nsub, nrhs, &
- rhs, &
- solut, err_code )
- ! Description:
- ! Perform all GMRES-specific matrix generation and solving for the
- ! wp2/wp3 matrices.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
-#ifdef MKL
- use crmx_error_code, only: &
- fatal_error ! Procedure(s)
-
- use crmx_stats_variables, only: &
- iwp23_matrix_condt_num, & ! Variable(s)
- l_stats_samp, &
- sfc
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_lapack_wrap, only: &
- band_solve, & ! Procedure(s)
- band_solvex
-
- use crmx_stats_type, only: &
- stat_update_var_pt ! Procedure(s)
-
- use crmx_csr_matrix_class, only: &
- csr_intlc_5b_5b_ia, & ! Variables
- csr_intlc_5b_5b_ja, &
- intlc_5d_5d_ja_size
-
- use crmx_gmres_wrap, only: &
- gmres_solve ! Subroutine
-
- use crmx_gmres_cache, only: &
- gmres_cache_soln, & ! Subroutine
- gmres_prev_soln, & ! Variables
- gmres_prev_precond_a, &
- l_gmres_soln_ok, &
- gmres_idx_wp2wp3, &
- gmres_temp_intlc, &
- gmres_tempsize_intlc
-#endif /* MKL */
-
- implicit none
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2 ! w'^2 (momentum levels) [m^2/s^2]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- a1, & ! a_1 (momentum levels); See eqn. 23 in `Equations for CLUBB' [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- a3, & ! a_3 (momentum levels); See eqn. 25 in `Equations for CLUBB' [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- Kw1, & ! Coefficient of eddy diffusivity for w'^2 [m^2/s]
- Kw8, & ! Coefficient of eddy diffusivity for w'^3 [m^2/s]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- tau1m, & ! Time-scale tau on momentum levels [s]
- tauw3t, & ! Time-scale tau on thermodynamic levels [s]
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt ! Inv. dry, static density @ thermo. levs. [m^3/kg]
-
- logical, intent(in) :: &
- l_crank_nich_diff ! Turns on/off Crank-Nicholson diffusion.
-
- integer, intent(in) :: &
- nsub, & ! Number of subdiagonals in the LHS matrix.
- nsup, & ! Number of superdiagonals in the LHS matrix.
- nrhs ! Number of right-hand side vectors
- ! (GMRES currently only supports 1)
-
- ! Input/Output variables
- real( kind = core_rknd ), dimension(2*gr%nz), intent(inout) :: &
- rhs ! Right hand side vector
-
- ! Output variables
- real( kind = core_rknd ), dimension(2*gr%nz), intent(out) :: &
- solut ! Solution to band diagonal system
-
- integer, intent(out) :: err_code ! Have any errors occured?
-
-#ifdef MKL
- ! Local variables
- real( kind = core_rknd ), dimension(nsup+nsub+1,2*gr%nz) :: &
- lhs, & ! Implicit contributions to wp2/wp3 (band diag. matrix)
- lhs_cache ! Backup cache of LHS matrix
-
- real( kind = core_rknd ), dimension(intlc_5d_5d_ja_size) :: &
- lhs_a_csr ! Implicit contributions to wp2/wp3 (CSR format)
-
- real( kind = core_rknd ), dimension(2*gr%nz) :: &
- rhs_cache ! Backup cache of RHS vector
-
- real( kind = core_rknd ):: &
- rcond ! Est. of the reciprocal of the condition #
-
- ! Begin code
-
- if (nsup > 2) then
- write (fstderr, *) "WARNING: CSR-format solvers currently do not", &
- "support solving with hyper diffusion", &
- "at this time. l_hyper_dfsn ignored."
- end if
- call wp23_lhs_csr( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, &
- lhs_a_csr )
-
- if ( .not. l_gmres_soln_ok(gmres_idx_wp2wp3) ) then
- call wp23_lhs( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsub, nsup, &
- lhs )
-
- ! Solve system with LAPACK to give us our first solution vector
- lhs_cache = lhs
- rhs_cache = rhs
- call band_solve( "wp2_wp3", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solut, err_code )
-
- ! Use gmres_cache_wp2wp3_soln to set cache this solution for GMRES
- call gmres_cache_soln( gr%nz * 2, gmres_idx_wp2wp3, solut )
- lhs = lhs_cache
- rhs = rhs_cache
- end if ! .not. l_gmres_soln_ok(gmres_idx_wp2wp3)
-
- call gmres_solve( intlc_5d_5d_ja_size, (gr%nz * 2), &
- lhs_a_csr, csr_intlc_5b_5b_ia, csr_intlc_5b_5b_ja, &
- gmres_tempsize_intlc, &
- gmres_prev_soln(:,gmres_idx_wp2wp3), &
- gmres_prev_precond_a(:,gmres_idx_wp2wp3), rhs, &
- gmres_temp_intlc, &
- solut, err_code )
- ! Fall back to LAPACK if GMRES returned any errors
- if ( fatal_error( err_code ) ) then
- write(fstderr,*) "Errors encountered in GMRES solve."
- write(fstderr,*) "Falling back to LAPACK solver."
-
- ! Generate the LHS in LAPACK format
- call wp23_lhs( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsub, nsup, &
- lhs )
-
- ! Note: The RHS does not need to be re-generated.
-
- ! Solve the system with LAPACK as a fall-back.
- if ( l_stats_samp .and. iwp23_matrix_condt_num > 0 ) then
-
- ! Perform LU decomp and solve system (LAPACK with diagnostics)
- ! Note that this can change the answer slightly
- call band_solvex( "wp2_wp3", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solut, rcond, err_code )
-
- ! Est. of the condition number of the w'^2/w^3 LHS matrix
- call stat_update_var_pt( iwp23_matrix_condt_num, 1, 1.0_core_rknd / rcond, sfc )
-
- else
- ! Perform LU decomp and solve system (LAPACK)
- call band_solve( "wp2_wp3", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solut, err_code )
- end if
-
- end if ! fatal_error
-
-#else
- stop "This build was not compiled with PARDISO/GMRES support."
-
- ! These prevent compiler warnings when -DMKL not set.
- if ( l_crank_nich_diff .or. .true. ) print *, "This should be unreachable"
- solut = rhs
- solut(1:gr%nz) = a1
- solut(1:gr%nz) = a1_zt
- solut(1:gr%nz) = a3
- solut(1:gr%nz) = a3_zt
- solut(1:gr%nz) = C11_Skw_fnc
- solut(1:gr%nz) = C1_Skw_fnc
- solut(1:gr%nz) = invrs_rho_ds_zm
- solut(1:gr%nz) = invrs_rho_ds_zt
- solut(1:gr%nz) = rho_ds_zm
- solut(1:gr%nz) = rho_ds_zt
- solut(1:gr%nz) = Kw1
- solut(1:gr%nz) = Kw8
- solut(1:gr%nz) = Skw_zt
- solut(1:gr%nz) = tau1m
- solut(1:gr%nz) = tauw3t
- solut(1:gr%nz) = wm_zt
- solut(1:gr%nz) = wm_zm
- solut(1:gr%nz) = wp2
- solut(1:gr%nz) = wp3_on_wp2
- err_code = int( dt )
- err_code = nsup
- err_code = nsub
- err_code = nrhs
-
-#endif /* MKL */
-
- end subroutine wp23_gmres
-
- !=============================================================================
- subroutine wp23_lhs( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, nsub, nsup, &
- lhs )
-
- ! Description:
- ! Compute LHS band diagonal matrix for w'^2 and w'^3.
- ! This subroutine computes the implicit portion
- ! of the w'^2 and w'^3 equations.
- !
- ! NOTE: If changes are made to this subroutine, ensure that the CSR
- ! version of the subroutine is updated as well! If the two are different,
- ! the results will be inconsistent between LAPACK and PARDISO/GMRES!
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable
-
- use crmx_parameters_tunable, only: &
- C4, & ! Variables
- C5, &
- C8, &
- C8b, &
- C12, &
- nu1_vert_res_dep, &
- nu8_vert_res_dep, &
- nu_hd_vert_res_dep
-
- use crmx_constants_clubb, only: &
- eps, & ! Variable(s)
- three_halves, &
- gamma_over_implicit_ts
-
- use crmx_model_flags, only: &
- l_tke_aniso, & ! Variable(s)
- l_hyper_dfsn
-
- use crmx_diffusion, only: &
- diffusion_zm_lhs, & ! Procedures
- diffusion_zt_lhs
-
- use crmx_mean_adv, only: &
- term_ma_zm_lhs, & ! Procedures
- term_ma_zt_lhs
-
- use crmx_hyper_diffusion_4th_ord, only: &
- hyper_dfsn_4th_ord_zm_lhs, &
- hyper_dfsn_4th_ord_zt_lhs
-
- use crmx_clubb_precision, only: &
- time_precision, &
- core_rknd
-
- use crmx_stats_variables, only: &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr11, &
- zmscr10, &
- zmscr12, &
- zmscr13, &
- zmscr14, &
- zmscr15, &
- zmscr16, &
- zmscr17, &
- ztscr01, &
- ztscr02
-
- use crmx_stats_variables, only: &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- ztscr06, &
- ztscr07, &
- ztscr08, &
- ztscr09, &
- ztscr10, &
- ztscr11, &
- ztscr12, &
- ztscr13, &
- ztscr14, &
- ztscr15, &
- ztscr16, &
- ztscr17, &
- ztscr18, &
- ztscr19, &
- ztscr20, &
- ztscr21
-
- use crmx_stats_variables, only: &
- l_stats_samp, &
- iwp2_dp1, &
- iwp2_dp2, &
- iwp2_ta, &
- iwp2_ma, &
- iwp2_ac, &
- iwp2_pr2, &
- iwp2_pr1, &
- iwp2_4hd, &
- iwp3_ta, &
- iwp3_tp, &
- iwp3_ma, &
- iwp3_ac, &
- iwp3_pr2, &
- iwp3_pr1, &
- iwp3_dp1, &
- iwp3_4hd
-
- use crmx_advance_helper_module, only: set_boundary_conditions_lhs ! Procedure(s)
-
- implicit none
-
- ! Parameter Constants
- ! Left-hand side matrix diagonal identifiers for
- ! momentum-level variable, w'^2.
- integer, parameter :: &
- m_kp2_mdiag = 1, & ! Momentum super-super diagonal index for w'^2.
- !m_kp2_tdiag = 2, & ! Thermodynamic super-super diagonal index for w'^2.
- m_kp1_mdiag = 3, & ! Momentum super diagonal index for w'^2.
- m_kp1_tdiag = 4, & ! Thermodynamic super diagonal index for w'^2.
- m_k_mdiag = 5, & ! Momentum main diagonal index for w'^2.
- m_k_tdiag = 6, & ! Thermodynamic sub diagonal index for w'^2.
- m_km1_mdiag = 7, & ! Momentum sub diagonal index for w'^2.
- !m_km1_tdiag = 8, & ! Thermodynamic sub-sub diagonal index for w'^2.
- m_km2_mdiag = 9 ! Momentum sub-sub diagonal index for w'^2.
-
- ! Left-hand side matrix diagonal identifiers for
- ! thermodynamic-level variable, w'^3.
- integer, parameter :: &
- t_kp2_tdiag = 1, & ! Thermodynamic super-super diagonal index for w'^3.
- !t_kp1_mdiag = 2, & ! Momentum super-super diagonal index for w'^3.
- t_kp1_tdiag = 3, & ! Thermodynamic super diagonal index for w'^3.
- !t_k_mdiag = 4, & ! Momentum super diagonal index for w'^3.
- t_k_tdiag = 5, & ! Thermodynamic main diagonal index for w'^3.
- !t_km1_mdiag = 6, & ! Momentum sub diagonal index for w'^3.
- t_km1_tdiag = 7, & ! Thermodynamic sub diagonal index for w'^3.
- !t_km2_mdiag = 8, & ! Momentum sub-sub diagonal index for w'^3.
- t_km2_tdiag = 9 ! Thermodynamic sub-sub diagonal index for w'^3.
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep length [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- a3, & ! sigma_sqd_w term a_3 (momentum levels) [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- Kw1, & ! Coefficient of eddy diffusivity for w'^2 [m^2/s]
- Kw8, & ! Coefficient of eddy diffusivity for w'^3 [m^2/s]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- tau1m, & ! Time-scale tau on momentum levels [s]
- tauw3t, & ! Time-scale tau on thermodynamic levels [s]
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt ! Inv. dry, static density @ thermo. levs. [m^3/kg]
-
- logical, intent(in) :: &
- l_crank_nich_diff ! Turns on/off Crank-Nicholson diffusion.
-
- integer, intent(in) :: &
- nsub, & ! Number of subdiagonals in the LHS matrix.
- nsup ! Number of superdiagonals in the LHS matrix.
-
- ! Output Variable
- real( kind = core_rknd ), dimension(5-nsup:5+nsub,2*gr%nz), intent(out) :: &
- lhs ! Implicit contributions to wp2/wp3 (band diag. matrix)
-
- ! Local Variables
-
- ! Array indices
- integer :: k, km1, km2, kp1, kp2, k_wp2, k_wp3, k_wp2_low, k_wp2_high, &
- k_wp3_low, k_wp3_high
-
- real( kind = core_rknd ), dimension(5) :: tmp
-
-
- ! Initialize the left-hand side matrix to 0.
- lhs = 0.0_core_rknd
-
- do k = 2, gr%nz-1, 1
-
- ! Define indices
-
- km1 = max( k-1, 1 )
- km2 = max( k-2, 1 )
- kp1 = min( k+1, gr%nz )
- kp2 = min( k+2, gr%nz )
-
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
-
- !!!!!***** w'^2 *****!!!!!
-
- ! w'^2: Left-hand side (implicit w'^2 portion of the code).
- !
- ! Momentum sub-sub diagonal (lhs index: m_km2_mdiag)
- ! [ x wp2(k-2,) ]
- ! Thermodynamic sub-sub diagonal (lhs index: m_km1_tdiag)
- ! [ x wp3(k-1,) ]
- ! Momentum sub diagonal (lhs index: m_km1_mdiag)
- ! [ x wp2(k-1,) ]
- ! Thermodynamic sub diagonal (lhs index: m_k_tdiag)
- ! [ x wp3(k,) ]
- ! Momentum main diagonal (lhs index: m_k_mdiag)
- ! [ x wp2(k,) ]
- ! Thermodynamic super diagonal (lhs index: m_kp1_tdiag)
- ! [ x wp3(k+1,) ]
- ! Momentum super diagonal (lhs index: m_kp1_mdiag)
- ! [ x wp2(k+1,) ]
- ! Thermodynamic super-super diagonal (lhs index: m_kp2_tdiag)
- ! [ x wp3(k+2,) ]
- ! Momentum super-super diagonal (lhs index: m_kp2_mdiag)
- ! [ x wp2(k+2,) ]
-
- ! LHS time tendency.
- lhs(m_k_mdiag,k_wp2) &
- = + 1.0_core_rknd / real( dt, kind = core_rknd )
-
- ! LHS mean advection (ma) term.
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
-
- ! LHS turbulent advection (ta) term.
- lhs((/m_kp1_tdiag,m_k_tdiag/),k_wp2) &
- = lhs((/m_kp1_tdiag,m_k_tdiag/),k_wp2) &
- + wp2_term_ta_lhs( rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), gr%invrs_dzm(k) )
-
- ! LHS accumulation (ac) term and pressure term 2 (pr2).
- lhs(m_k_mdiag,k_wp2) &
- = lhs(m_k_mdiag,k_wp2) &
- + wp2_terms_ac_pr2_lhs( C5, wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
-
- ! LHS dissipation term 1 (dp1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the term
- ! more numerically stable (see note below for w'^3 LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs(m_k_mdiag,k_wp2) &
- = lhs(m_k_mdiag,k_wp2) &
- + gamma_over_implicit_ts &
- * wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
-
- ! LHS eddy diffusion term: dissipation term 2 (dp2).
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp2 using a Crank-Nicholson time step.
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- + (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- else
- ! Eddy diffusion for wp2 using a completely implicit time step.
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wp2) &
- + diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- endif
-
- ! LHS pressure term 1 (pr1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the term
- ! more numerically stable (see note below for w'^3 LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( l_tke_aniso ) then
- ! Add in this term if we're not assuming tke = 1.5 * wp2
- lhs(m_k_mdiag,k_wp2) &
- = lhs(m_k_mdiag,k_wp2) &
- + gamma_over_implicit_ts &
- * wp2_term_pr1_lhs( C4, tau1m(k) )
- endif
-
- ! LHS 4th-order hyper-diffusion (4hd).
- if ( l_hyper_dfsn ) then
- ! Note: w'^2 uses fixed-point boundary conditions.
- lhs( (/m_kp2_mdiag,m_kp1_mdiag,m_k_mdiag,m_km1_mdiag,m_km2_mdiag/), &
- k_wp2 ) &
- = lhs( (/m_kp2_mdiag,m_kp1_mdiag,m_k_mdiag,m_km1_mdiag,m_km2_mdiag/), &
- k_wp2 ) &
- + hyper_dfsn_4th_ord_zm_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzm(k), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp2), gr%invrs_dzt(km1), k )
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for wp2.
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 LHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- if ( iwp2_dp1 > 0 ) then
- zmscr01(k) &
- = - gamma_over_implicit_ts &
- * wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
- endif
-
- if ( iwp2_dp2 > 0 ) then
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp2 using a Crank-Nicholson time step.
- tmp(1:3) &
- = (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- else
- ! Eddy diffusion for wp2 using a completely implicit time step.
- tmp(1:3) &
- = diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- endif
-
- zmscr02(k) = -tmp(3)
- zmscr03(k) = -tmp(2)
- zmscr04(k) = -tmp(1)
-
- endif
-
- if ( iwp2_ta > 0 ) then
- tmp(1:2) = &
- + wp2_term_ta_lhs( rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), gr%invrs_dzm(k) )
- zmscr05(k) = -tmp(2)
- zmscr06(k) = -tmp(1)
- endif
-
- if ( iwp2_ma > 0 ) then
- tmp(1:3) = &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
- zmscr07(k) = -tmp(3)
- zmscr08(k) = -tmp(2)
- zmscr09(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^2 term ac, substitute 0 for the
- ! C_5 input to function wp2_terms_ac_pr2_lhs.
- if ( iwp2_ac > 0 ) then
- zmscr10(k) = &
- - wp2_terms_ac_pr2_lhs( 0.0_core_rknd, wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
- endif
-
- ! Note: To find the contribution of w'^2 term pr2, add 1 to the
- ! C_5 input to function wp2_terms_ac_pr2_lhs.
- if ( iwp2_pr2 > 0 ) then
- zmscr11(k) = &
- - wp2_terms_ac_pr2_lhs( (1.0_core_rknd+C5), wm_zt(kp1), wm_zt(k), &
- gr%invrs_dzm(k) )
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 LHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- if ( iwp2_pr1 > 0 .and. l_tke_aniso ) then
- zmscr12(k) &
- = - gamma_over_implicit_ts &
- * wp2_term_pr1_lhs( C4, tau1m(k) )
- endif
-
- if ( iwp2_4hd > 0 .and. l_hyper_dfsn ) then
- tmp(1:5) = &
- hyper_dfsn_4th_ord_zm_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzm(k), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp2), gr%invrs_dzt(km1), k )
- zmscr13(k) = -tmp(5)
- zmscr14(k) = -tmp(4)
- zmscr15(k) = -tmp(3)
- zmscr16(k) = -tmp(2)
- zmscr17(k) = -tmp(1)
- endif
-
- endif
-
-
-
- !!!!!***** w'^3 *****!!!!!
-
- ! w'^3: Left-hand side (implicit w'^3 portion of the code).
- !
- ! Thermodynamic sub-sub diagonal (lhs index: t_km2_tdiag)
- ! [ x wp3(k-2,) ]
- ! Momentum sub-sub diagonal (lhs index: t_km2_mdiag)
- ! [ x wp2(k-2,) ]
- ! Thermodynamic sub diagonal (lhs index: t_km1_tdiag)
- ! [ x wp3(k-1,) ]
- ! Momentum sub diagonal (lhs index: t_km1_mdiag)
- ! [ x wp2(k-1,) ]
- ! Thermodynamic main diagonal (lhs index: t_k_tdiag)
- ! [ x wp3(k,) ]
- ! Momentum super diagonal (lhs index: t_k_mdiag)
- ! [ x wp2(k,) ]
- ! Thermodynamic super diagonal (lhs index: t_kp1_tdiag)
- ! [ x wp3(k+1,) ]
- ! Momentum super-super diagonal (lhs index: t_kp1_mdiag)
- ! [ x wp2(k+1,) ]
- ! Thermodynamic super-super diagonal (lhs index: t_kp2_tdiag)
- ! [ x wp3(k+2,) ]
-
- ! LHS time tendency.
- lhs(t_k_tdiag,k_wp3) &
- = + 1.0_core_rknd / real( dt, kind = core_rknd )
-
- ! LHS mean advection (ma) term.
- lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- = lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- + term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(k-1) )
-
- ! LHS turbulent advection (ta) and turbulent production (tp) terms.
- ! Note: An "over-implicit" weighted time step is applied to these terms.
- ! The weight of the implicit portion of these terms is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (w'^3 in this case), y(t) is the linearized portion of
- ! the terms that gets treated implicitly, and RHS is the portion of
- ! the terms that is always treated explicitly. A weight of greater
- ! than 1 can be applied to make the terms more numerically stable.
- lhs(t_kp1_tdiag:t_km1_tdiag,k_wp3) &
- = lhs(t_kp1_tdiag:t_km1_tdiag,k_wp3) &
- + gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k), a3_zt(k), a3(km1), &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
-
- ! LHS accumulation (ac) term and pressure term 2 (pr2).
- lhs(t_k_tdiag,k_wp3) &
- = lhs(t_k_tdiag,k_wp3) &
- + wp3_terms_ac_pr2_lhs( C11_Skw_fnc(k), &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
-
- ! LHS pressure term 1 (pr1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs(t_k_tdiag,k_wp3) &
- = lhs(t_k_tdiag,k_wp3) &
- + gamma_over_implicit_ts &
- * wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
-
- ! LHS eddy diffusion term: dissipation term 1 (dp1).
- ! Added a new constant, C12.
- ! Initially, this new constant will be set to 1.0 -dschanen 9/19/05
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp3 using a Crank-Nicholson time step.
- lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- = lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- + C12 * (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- else
- ! Eddy diffusion for wp3 using a completely implicit time step.
- lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- = lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_wp3) &
- + C12 &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- endif
-
- ! LHS 4th-order hyper-diffusion (4hd).
- if ( l_hyper_dfsn ) then
- ! Note: w'^3 uses fixed-point boundary conditions.
- lhs( (/t_kp2_tdiag,t_kp1_tdiag,t_k_tdiag,t_km1_tdiag,t_km2_tdiag/), &
- k_wp3 ) &
- = lhs( (/t_kp2_tdiag,t_kp1_tdiag,t_k_tdiag,t_km1_tdiag,t_km2_tdiag/), &
- k_wp3 ) &
- + hyper_dfsn_4th_ord_zt_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzt(k), &
- gr%invrs_dzm(k), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(km1), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km2), k )
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for wp3.
-
- ! Note: To find the contribution of w'^3 term ta, add 3 to all of
- ! the a_3 inputs and substitute 0 for the three_halves input to
- ! function wp3_terms_ta_tp_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_ta > 0 ) then
- tmp(1:5) &
- = gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k)+3.0_core_rknd, a3_zt(k)+3.0_core_rknd, &
- a3(km1)+3.0_core_rknd, &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- 0.0_core_rknd, &
- gr%invrs_dzt(k), k )
- ztscr05(k) = -tmp(5)
- ztscr06(k) = -tmp(4)
- ztscr07(k) = -tmp(3)
- ztscr08(k) = -tmp(2)
- ztscr09(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^3 term tp, substitute 0 for all
- ! of the a_1 and a_3 inputs and subtract 3 from all of the a_3
- ! inputs to function wp3_terms_ta_tp_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_tp > 0 ) then
- tmp(1:5) &
- = gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- 0.0_core_rknd, 0.0_core_rknd, 0.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd, 0.0_core_rknd, &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
- ztscr10(k) = -tmp(4)
- ztscr11(k) = -tmp(2)
- endif
-
- if ( iwp3_ma > 0 ) then
- tmp(1:3) = &
- term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
- ztscr12(k) = -tmp(3)
- ztscr13(k) = -tmp(2)
- ztscr14(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^3 term ac, substitute 0 for the
- ! C_ll skewness function input to function wp3_terms_ac_pr2_lhs.
- if ( iwp3_ac > 0 ) then
- ztscr15(k) = &
- - wp3_terms_ac_pr2_lhs( 0.0_core_rknd, &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
- endif
-
- ! Note: To find the contribution of w'^3 term pr2, add 1 to the
- ! C_ll skewness function input to function wp3_terms_ac_pr2_lhs.
- if ( iwp3_pr2 > 0 ) then
- ztscr16(k) = &
- - wp3_terms_ac_pr2_lhs( (1.0_core_rknd+C11_Skw_fnc(k)), &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_pr1 > 0 ) then
- ztscr01(k) &
- = - gamma_over_implicit_ts &
- * wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
- endif
-
- if ( iwp3_dp1 > 0 ) then
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp3 using a Crank-Nicholson time step.
- tmp(1:3) &
- = C12 * (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- else
- ! Eddy diffusion for wp3 using a completely implicit time step.
- tmp(1:3) &
- = C12 &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- endif
-
- ztscr02(k) = -tmp(3)
- ztscr03(k) = -tmp(2)
- ztscr04(k) = -tmp(1)
-
- endif
-
- if ( iwp3_4hd > 0 .and. l_hyper_dfsn ) then
- tmp(1:5) = &
- hyper_dfsn_4th_ord_zt_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzt(k), &
- gr%invrs_dzm(k), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(km1), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km2), k )
- ztscr17(k) = -tmp(5)
- ztscr18(k) = -tmp(4)
- ztscr19(k) = -tmp(3)
- ztscr20(k) = -tmp(2)
- ztscr21(k) = -tmp(1)
- endif
-
- endif
-
- enddo ! k = 2, gr%nz-1, 1
-
-
- ! Boundary conditions
-
- ! Both wp2 and wp3 used fixed-point boundary conditions.
- ! Therefore, anything set in the above loop at both the upper
- ! and lower boundaries would be overwritten here. However, the
- ! above loop does not extend to the boundary levels. An array
- ! with a value of 1 at the main diagonal on the left-hand side
- ! and with values of 0 at all other diagonals on the left-hand
- ! side will preserve the right-hand side value at that level.
- !
- ! wp3(1) wp2(1) ... wp3(nzmax) wp2(nzmax)
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 1.0 1.0 ... 1.0 1.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
-
- ! Lower boundary
- k = 1
- k_wp3_low = 2*k - 1
- k_wp2_low = 2*k
-
- ! Upper boundary
- k = gr%nz
- k_wp3_high = 2*k - 1
- k_wp2_high = 2*k
-
- ! t_k_tdiag and m_k_mdiag need to be adjusted because the dimensions of lhs
- ! are offset
- call set_boundary_conditions_lhs( t_k_tdiag - nsup, k_wp3_low, k_wp3_high, lhs, &
- m_k_mdiag - nsup, k_wp2_low, k_wp2_high)
-
- return
-
- end subroutine wp23_lhs
-
-#ifdef MKL
- !=============================================================================
- subroutine wp23_lhs_csr( dt, wp2, wm_zm, wm_zt, a1, a1_zt, a3, a3_zt, &
- wp3_on_wp2, &
- Kw1, Kw8, Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, l_crank_nich_diff, &
- lhs_a_csr )
-
- ! Description:
- ! Compute LHS band diagonal matrix for w'^2 and w'^3.
- ! This subroutine computes the implicit portion
- ! of the w'^2 and w'^3 equations.
- !
- ! This version of the subroutine computes the LHS in CSR (compressed
- ! sparse row) format.
- ! NOTE: This subroutine must be kept up to date with the non CSR version
- ! of the subroutine! If the two are different, the results will be
- ! inconsistent between LAPACK and PARDISO/GMRES results!
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable
-
- use crmx_parameters_tunable, only: &
- C4, & ! Variables
- C5, &
- C8, &
- C8b, &
- C12, &
- nu1_vert_res_dep, &
- nu8_vert_res_dep, &
- nu_hd_vert_res_dep
-
- use crmx_constants_clubb, only: &
- eps, & ! Variable(s)
- three_halves, &
- gamma_over_implicit_ts
-
- use crmx_model_flags, only: &
- l_tke_aniso, & ! Variable(s)
- l_hyper_dfsn
-
- use crmx_diffusion, only: &
- diffusion_zm_lhs, & ! Procedures
- diffusion_zt_lhs
-
- use crmx_mean_adv, only: &
- term_ma_zm_lhs, & ! Procedures
- term_ma_zt_lhs
-
- use crmx_hyper_diffusion_4th_ord, only: &
- hyper_dfsn_4th_ord_zm_lhs, &
- hyper_dfsn_4th_ord_zt_lhs
-
- use crmx_clubb_precision, only: &
- time_precision, &
- core_rknd
-
- use crmx_stats_variables, only: &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr11, &
- zmscr10, &
- zmscr12, &
- zmscr13, &
- zmscr14, &
- zmscr15, &
- zmscr16, &
- zmscr17, &
- ztscr01, &
- ztscr02
-
- use crmx_stats_variables, only: &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- ztscr06, &
- ztscr07, &
- ztscr08, &
- ztscr09, &
- ztscr10, &
- ztscr11, &
- ztscr12, &
- ztscr13, &
- ztscr14, &
- ztscr15, &
- ztscr16, &
- ztscr17, &
- ztscr18, &
- ztscr19, &
- ztscr20, &
- ztscr21
-
- use crmx_stats_variables, only: &
- l_stats_samp, &
- iwp2_dp1, &
- iwp2_dp2, &
- iwp2_ta, &
- iwp2_ma, &
- iwp2_ac, &
- iwp2_pr2, &
- iwp2_pr1, &
- iwp2_4hd, &
- iwp3_ta, &
- iwp3_tp, &
- iwp3_ma, &
- iwp3_ac, &
- iwp3_pr2, &
- iwp3_pr1, &
- iwp3_dp1, &
- iwp3_4hd
-
- use crmx_csr_matrix_class, only: &
- intlc_5d_5d_ja_size ! Variable
-
- implicit none
-
- ! Left-hand side matrix diagonal identifiers for
- ! momentum-level variable, w'^2.
- ! These are updated for each diagonal of the matrix as the
- ! LHS of the matrix is created.
- integer :: &
- !m_kp2_mdiag, & ! Momentum super-super diagonal index for w'^2.
- !m_kp2_tdiag, & ! Thermodynamic super-super diagonal index for w'^2.
- m_kp1_mdiag, & ! Momentum super diagonal index for w'^2.
- m_kp1_tdiag, & ! Thermodynamic super diagonal index for w'^2.
- m_k_mdiag , & ! Momentum main diagonal index for w'^2.
- m_k_tdiag , & ! Thermodynamic sub diagonal index for w'^2.
- m_km1_mdiag ! Momentum sub diagonal index for w'^2.
- !m_km1_tdiag, & ! Thermodynamic sub-sub diagonal index for w'^2.
- !m_km2_mdiag ! Momentum sub-sub diagonal index for w'^2.
-
- ! Left-hand side matrix diagonal identifiers for
- ! thermodynamic-level variable, w'^3.
- ! These are updated for each diagonal of the matrix as the
- ! LHS of the matrix is created
- integer :: &
- !t_kp2_tdiag, & ! Thermodynamic super-super diagonal index for w'^3.
- !t_kp1_mdiag, & ! Momentum super-super diagonal index for w'^3.
- t_kp1_tdiag, & ! Thermodynamic super diagonal index for w'^3.
- !t_k_mdiag , & ! Momentum super diagonal index for w'^3.
- t_k_tdiag , & ! Thermodynamic main diagonal index for w'^3.
- !t_km1_mdiag, & ! Momentum sub diagonal index for w'^3.
- t_km1_tdiag ! Thermodynamic sub diagonal index for w'^3.
- !t_km2_mdiag, & ! Momentum sub-sub diagonal index for w'^3.
- !t_km2_tdiag ! Thermodynamic sub-sub diagonal index for w'^3.
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep length [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- a3, & ! sigma_sqd_w term a_3 (momentum levels) [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- Kw1, & ! Coefficient of eddy diffusivity for w'^2 [m^2/s]
- Kw8, & ! Coefficient of eddy diffusivity for w'^3 [m^2/s]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- tau1m, & ! Time-scale tau on momentum levels [s]
- tauw3t, & ! Time-scale tau on thermodynamic levels [s]
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt ! Inv. dry, static density @ thermo. levs. [m^3/kg]
-
- logical, intent(in) :: &
- l_crank_nich_diff ! Turns on/off Crank-Nicholson diffusion.
-
-! integer, intent(in) :: &
-! nsub, & ! Number of subdiagonals in the LHS matrix.
-! nsup ! Number of superdiagonals in the LHS matrix.
-
- ! Output Variable
- real( kind = core_rknd ), dimension(intlc_5d_5d_ja_size), intent(out) :: &
- lhs_a_csr ! Implicit contributions to wp2/wp3 (band diag. matrix)
-
- ! Local Variables
-
- ! Array indices
- integer :: k, km1, km2, kp1, kp2, k_wp2, k_wp3, wp2_cur_row, wp3_cur_row
-
- real( kind = core_rknd ), dimension(5) :: tmp
-
-
- ! Initialize the left-hand side matrix to 0.
- lhs_a_csr = 0.0_core_rknd
-
- do k = 2, gr%nz-1, 1
-
- ! Define indices
-
- km1 = max( k-1, 1 )
- km2 = max( k-2, 1 )
- kp1 = min( k+1, gr%nz )
- kp2 = min( k+2, gr%nz )
-
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
- wp2_cur_row = ((k_wp2 - 3) * 5) + 8
- wp3_cur_row = ((k_wp3 - 3) * 5) + 8
-
- !!!!!***** w'^2 *****!!!!!
-
- ! w'^2: Left-hand side (implicit w'^2 portion of the code).
- !
- ! Momentum sub-sub diagonal (lhs index: m_km2_mdiag)
- ! [ x wp2(k-2,) ]
- ! Thermodynamic sub-sub diagonal (lhs index: m_km1_tdiag)
- ! [ x wp3(k-1,) ]
- ! Momentum sub diagonal (lhs index: m_km1_mdiag)
- ! [ x wp2(k-1,) ]
- ! Thermodynamic sub diagonal (lhs index: m_k_tdiag)
- ! [ x wp3(k,) ]
- ! Momentum main diagonal (lhs index: m_k_mdiag)
- ! [ x wp2(k,) ]
- ! Thermodynamic super diagonal (lhs index: m_kp1_tdiag)
- ! [ x wp3(k+1,) ]
- ! Momentum super diagonal (lhs index: m_kp1_mdiag)
- ! [ x wp2(k+1,) ]
- ! Thermodynamic super-super diagonal (lhs index: m_kp2_tdiag)
- ! [ x wp3(k+2,) ]
- ! Momentum super-super diagonal (lhs index: m_kp2_mdiag)
- ! [ x wp2(k+2,) ]
-
- ! NOTES FOR CSR-FORMAT MATRICES
- ! The various diagonals are referenced through the following
- ! array indices:
- ! (m_kp1_mdiag, k_wp2) ==> (wp2_cur_row + 4)
- ! (m_kp1_tdiag, k_wp2) ==> (wp2_cur_row + 3)
- ! (m_k_mdiag, k_wp2) ==> (wp2_cur_row + 2)
- ! (m_k_tdiag, k_wp2) ==> (wp2_cur_row + 1)
- ! (m_km1_mdiag, k_wp2) ==> (wp2_cur_row)
- ! For readability, these values are updated here.
- ! This means that to update the CSR version of the LHS subroutine,
- ! all that must be done is remove the ,k_wp2 from the array indices,
- ! as the CSR-format matrix is one-dimensional.
-
- ! NOTE: All references to lhs will need to be changed to lhs_a_csr
-
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- ! WARNING: If you have array indices that go from m_kp1_mdiag to
- ! m_km1_mdiag, you will need to set it to span by -1. This is because
- ! in the CSR-format arrays, the indices descend as you go from m_kp1_mdiag
- ! to m_km1_mdiag!
- !
- ! EXAMPLE: lhs((m_kp1_mdiag:m_km1_mdiag),wp2) would become
- ! lhs_a_csr((m_kp1_mdiag:m_km1_mdiag:-1))
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- m_kp1_mdiag = wp2_cur_row + 4
- m_kp1_tdiag = wp2_cur_row + 3
- m_k_mdiag = wp2_cur_row + 2
- m_k_tdiag = wp2_cur_row + 1
- m_km1_mdiag = wp2_cur_row
-
- ! LHS time tendency.
- lhs_a_csr(m_k_mdiag) &
- = real( + 1.0_core_rknd / dt )
-
- ! LHS mean advection (ma) term.
- lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- = lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
-
- ! LHS turbulent advection (ta) term.
- lhs_a_csr((/m_kp1_tdiag,m_k_tdiag/)) &
- = lhs_a_csr((/m_kp1_tdiag,m_k_tdiag/)) &
- + wp2_term_ta_lhs( rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), gr%invrs_dzm(k) )
-
- ! LHS accumulation (ac) term and pressure term 2 (pr2).
- lhs_a_csr(m_k_mdiag) &
- = lhs_a_csr(m_k_mdiag) &
- + wp2_terms_ac_pr2_lhs( C5, wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
-
- ! LHS dissipation term 1 (dp1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the term
- ! more numerically stable (see note below for w'^3 LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs_a_csr(m_k_mdiag) &
- = lhs_a_csr(m_k_mdiag) &
- + gamma_over_implicit_ts &
- * wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
-
- ! LHS eddy diffusion term: dissipation term 2 (dp2).
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp2 using a Crank-Nicholson time step.
- lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- = lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- + (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- else
- ! Eddy diffusion for wp2 using a completely implicit time step.
- lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- = lhs_a_csr((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/)) &
- + diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- endif
-
- ! LHS pressure term 1 (pr1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the term
- ! more numerically stable (see note below for w'^3 LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( l_tke_aniso ) then
- ! Add in this term if we're not assuming tke = 1.5 * wp2
- lhs_a_csr(m_k_mdiag) &
- = lhs_a_csr(m_k_mdiag) &
- + gamma_over_implicit_ts &
- * wp2_term_pr1_lhs( C4, tau1m(k) )
- endif
-
- ! LHS 4th-order hyper-diffusion (4hd).
- ! NOTE: 4th-order hyper-diffusion is not yet supported in CSR-format.
- ! As such, this needs to remain commented out.
- !if ( l_hyper_dfsn ) then
- ! ! Note: w'^2 uses fixed-point boundary conditions.
- ! lhs( (/m_kp2_mdiag,m_kp1_mdiag,m_k_mdiag,m_km1_mdiag,m_km2_mdiag/), &
- ! k_wp2) &
- ! = lhs( (/m_kp2_mdiag,m_kp1_mdiag,m_k_mdiag,m_km1_mdiag,m_km2_mdiag/), &
- ! k_wp2) &
- ! + hyper_dfsn_4th_ord_zm_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzm(k), &
- ! gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- ! gr%invrs_dzm(kp1), gr%invrs_dzm(km1), &
- ! gr%invrs_dzt(kp2), gr%invrs_dzt(km1), k )
- !endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for wp2.
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 LHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- if ( iwp2_dp1 > 0 ) then
- zmscr01(k) &
- = - gamma_over_implicit_ts &
- * wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
- endif
-
- if ( iwp2_dp2 > 0 ) then
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp2 using a Crank-Nicholson time step.
- tmp(1:3) &
- = (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- else
- ! Eddy diffusion for wp2 using a completely implicit time step.
- tmp(1:3) &
- = diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- endif
-
- zmscr02(k) = -tmp(3)
- zmscr03(k) = -tmp(2)
- zmscr04(k) = -tmp(1)
-
- endif
-
- if ( iwp2_ta > 0 ) then
- tmp(1:2) = &
- + wp2_term_ta_lhs( rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), gr%invrs_dzm(k) )
- zmscr05(k) = -tmp(2)
- zmscr06(k) = -tmp(1)
- endif
-
- if ( iwp2_ma > 0 ) then
- tmp(1:3) = &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
- zmscr07(k) = -tmp(3)
- zmscr08(k) = -tmp(2)
- zmscr09(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^2 term ac, substitute 0 for the
- ! C_5 input to function wp2_terms_ac_pr2_lhs.
- if ( iwp2_ac > 0 ) then
- zmscr10(k) = &
- - wp2_terms_ac_pr2_lhs( 0.0_core_rknd, wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
- endif
-
- ! Note: To find the contribution of w'^2 term pr2, add 1 to the
- ! C_5 input to function wp2_terms_ac_pr2_lhs.
- if ( iwp2_pr2 > 0 ) then
- zmscr11(k) = &
- - wp2_terms_ac_pr2_lhs( (1.0_core_rknd+C5), wm_zt(kp1), wm_zt(k), &
- gr%invrs_dzm(k) )
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 LHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- if ( iwp2_pr1 > 0 .and. l_tke_aniso ) then
- zmscr12(k) &
- = - gamma_over_implicit_ts &
- * wp2_term_pr1_lhs( C4, tau1m(k) )
- endif
-
- if ( iwp2_4hd > 0 .and. l_hyper_dfsn ) then
- tmp(1:5) = &
- hyper_dfsn_4th_ord_zm_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzm(k), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp2), gr%invrs_dzt(km1), k )
- zmscr13(k) = -tmp(5)
- zmscr14(k) = -tmp(4)
- zmscr15(k) = -tmp(3)
- zmscr16(k) = -tmp(2)
- zmscr17(k) = -tmp(1)
- endif
-
- endif
-
-
-
- !!!!!***** w'^3 *****!!!!!
-
- ! w'^3: Left-hand side (implicit w'^3 portion of the code).
- !
- ! Thermodynamic sub-sub diagonal (lhs index: t_km2_tdiag)
- ! [ x wp3(k-2,) ]
- ! Momentum sub-sub diagonal (lhs index: t_km2_mdiag)
- ! [ x wp2(k-2,) ]
- ! Thermodynamic sub diagonal (lhs index: t_km1_tdiag)
- ! [ x wp3(k-1,) ]
- ! Momentum sub diagonal (lhs index: t_km1_mdiag)
- ! [ x wp2(k-1,) ]
- ! Thermodynamic main diagonal (lhs index: t_k_tdiag)
- ! [ x wp3(k,) ]
- ! Momentum super diagonal (lhs index: t_k_mdiag)
- ! [ x wp2(k,) ]
- ! Thermodynamic super diagonal (lhs index: t_kp1_tdiag)
- ! [ x wp3(k+1,) ]
- ! Momentum super-super diagonal (lhs index: t_kp1_mdiag)
- ! [ x wp2(k+1,) ]
- ! Thermodynamic super-super diagonal (lhs index: t_kp2_tdiag)
- ! [ x wp3(k+2,) ]
-
- ! NOTES FOR CSR-FORMAT MATRICES
- ! The various diagonals are referenced through the following
- ! array indices:
- ! (t_kp1_tdiag, k_wp3) ==> (wp3_cur_row + 4)
- ! (t_kp1_mdiag, k_wp3) ==> (wp3_cur_row + 3)
- ! (t_k_tdiag, k_wp3) ==> (wp3_cur_row + 2)
- ! (t_k_mdiag, k_wp3) ==> (wp3_cur_row + 1)
- ! (t_km1_tdiag, k_wp3) ==> (wp3_cur_row)
- ! For readability, these values are updated here.
- ! This means that to update the CSR version of the LHS subroutine,
- ! all that must be done is remove the ,k_wp2 from the array indices,
- ! as the CSR-format matrix is one-dimensional.
-
- ! NOTE: All references to lhs will need to be changed to lhs_a_csr
-
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- ! WARNING: If you have array indices that go from t_kp1_tdiag to
- ! t_km1_tdiag, you will need to set it to span by -1. This is because
- ! in the CSR-format arrays, the indices descend as you go from t_kp1_tdiag
- ! to t_km1_tdiag!
- !
- ! EXAMPLE: lhs((t_kp1_tdiag:t_km1_tdiag),wp3) would become
- ! lhs_a_csr((t_kp1_tdiag:t_km1_tdiag:-1))
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- t_kp1_tdiag = wp3_cur_row + 4
- !t_kp1_mdiag = wp3_cur_row + 3
- t_k_tdiag = wp3_cur_row + 2
- !t_k_mdiag = wp3_cur_row + 1
- t_km1_tdiag = wp3_cur_row
-
- ! LHS time tendency.
- lhs_a_csr(t_k_tdiag) &
- = real( + 1.0_core_rknd / dt )
-
- ! LHS mean advection (ma) term.
- lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- = lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- + term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
-
- ! LHS turbulent advection (ta) and turbulent production (tp) terms.
- ! Note: An "over-implicit" weighted time step is applied to these terms.
- ! The weight of the implicit portion of these terms is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (w'^3 in this case), y(t) is the linearized portion of
- ! the terms that gets treated implicitly, and RHS is the portion of
- ! the terms that is always treated explicitly. A weight of greater
- ! than 1 can be applied to make the terms more numerically stable.
- lhs_a_csr(t_kp1_tdiag:t_km1_tdiag:-1) &
- = lhs_a_csr(t_kp1_tdiag:t_km1_tdiag:-1) &
- + gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k), a3_zt(k), a3(km1), &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
-
- ! LHS accumulation (ac) term and pressure term 2 (pr2).
- lhs_a_csr(t_k_tdiag) &
- = lhs_a_csr(t_k_tdiag) &
- + wp3_terms_ac_pr2_lhs( C11_Skw_fnc(k), &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
-
- ! LHS pressure term 1 (pr1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs_a_csr(t_k_tdiag) &
- = lhs_a_csr(t_k_tdiag) &
- + gamma_over_implicit_ts &
- * wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
-
- ! LHS eddy diffusion term: dissipation term 1 (dp1).
- ! Added a new constant, C12.
- ! Initially, this new constant will be set to 1.0 -dschanen 9/19/05
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp3 using a Crank-Nicholson time step.
- lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- = lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- + C12 * (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- else
- ! Eddy diffusion for wp3 using a completely implicit time step.
- lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- = lhs_a_csr((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/)) &
- + C12 &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- endif
-
- ! LHS 4th-order hyper-diffusion (4hd).
- ! NOTE: 4th-order hyper-diffusion is not yet supported in CSR-format.
- ! As such, this needs to remain commented out.
- !if ( l_hyper_dfsn ) then
- ! ! Note: w'^3 uses fixed-point boundary conditions.
- ! lhs( (/t_kp2_tdiag,t_kp1_tdiag,t_k_tdiag,t_km1_tdiag,t_km2_tdiag/), &
- ! k_wp3) &
- ! = lhs( (/t_kp2_tdiag,t_kp1_tdiag,t_k_tdiag,t_km1_tdiag,t_km2_tdiag/), &
- ! k_wp3) &
- ! + hyper_dfsn_4th_ord_zt_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzt(k), &
- ! gr%invrs_dzm(k), gr%invrs_dzm(km1), &
- ! gr%invrs_dzt(kp1), gr%invrs_dzt(km1), &
- ! gr%invrs_dzm(kp1), gr%invrs_dzm(km2), k )
- !endif
-
- if (l_stats_samp) then
-
- ! Statistics: implicit contributions for wp3.
-
- ! Note: To find the contribution of w'^3 term ta, add 3 to all of
- ! the a_3 inputs and substitute 0 for the three_halves input to
- ! function wp3_terms_ta_tp_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_ta > 0 ) then
- tmp(1:5) &
- = gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k)+3.0_core_rknd, a3_zt(k)+3.0_core_rknd, &
- a3(km1)+3.0_core_rknd, &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- 0.0_core_rknd, &
- gr%invrs_dzt(k), k )
- ztscr05(k) = -tmp(5)
- ztscr06(k) = -tmp(4)
- ztscr07(k) = -tmp(3)
- ztscr08(k) = -tmp(2)
- ztscr09(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^3 term tp, substitute 0 for all
- ! of the a_1 and a_3 inputs and subtract 3 from all of the a_3
- ! inputs to function wp3_terms_ta_tp_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_tp > 0 ) then
- tmp(1:5) &
- = gamma_over_implicit_ts &
- * wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- 0.0_core_rknd, 0.0_core_rknd, 0.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd, 0.0_core_rknd, &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
- ztscr10(k) = -tmp(4)
- ztscr11(k) = -tmp(2)
- endif
-
- if ( iwp3_ma > 0 ) then
- tmp(1:3) = &
- term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
- ztscr12(k) = -tmp(3)
- ztscr13(k) = -tmp(2)
- ztscr14(k) = -tmp(1)
- endif
-
- ! Note: To find the contribution of w'^3 term ac, substitute 0 for the
- ! C_ll skewness function input to function wp3_terms_ac_pr2_lhs.
- if ( iwp3_ac > 0 ) then
- ztscr15(k) = &
- - wp3_terms_ac_pr2_lhs( 0.0_core_rknd, &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
- endif
-
- ! Note: To find the contribution of w'^3 term pr2, add 1 to the
- ! C_ll skewness function input to function wp3_terms_ac_pr2_lhs.
- if ( iwp3_pr2 > 0 ) then
- ztscr16(k) = &
- - wp3_terms_ac_pr2_lhs( (1.0_core_rknd+C11_Skw_fnc(k)), &
- wm_zm(k), wm_zm(km1), gr%invrs_dzt(k) )
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- if ( iwp3_pr1 > 0 ) then
- ztscr01(k) &
- = - gamma_over_implicit_ts &
- * wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
- endif
-
- if ( iwp3_dp1 > 0 ) then
- if ( l_crank_nich_diff ) then
- ! Eddy diffusion for wp3 using a Crank-Nicholson time step.
- tmp(1:3) &
- = C12 * (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- else
- ! Eddy diffusion for wp3 using a completely implicit time step.
- tmp(1:3) &
- = C12 &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- endif
-
- ztscr02(k) = -tmp(3)
- ztscr03(k) = -tmp(2)
- ztscr04(k) = -tmp(1)
-
- endif
-
- if ( iwp3_4hd > 0 .and. l_hyper_dfsn ) then
- tmp(1:5) = &
- hyper_dfsn_4th_ord_zt_lhs( 'fixed-point', nu_hd_vert_res_dep, gr%invrs_dzt(k), &
- gr%invrs_dzm(k), gr%invrs_dzm(km1), &
- gr%invrs_dzt(kp1), gr%invrs_dzt(km1), &
- gr%invrs_dzm(kp1), gr%invrs_dzm(km2), k )
- ztscr17(k) = -tmp(5)
- ztscr18(k) = -tmp(4)
- ztscr19(k) = -tmp(3)
- ztscr20(k) = -tmp(2)
- ztscr21(k) = -tmp(1)
- endif
-
- endif
-
- enddo ! k = 2, gr%nz-1, 1
-
-
- ! Boundary conditions
-
- ! Both wp2 and wp3 used fixed-point boundary conditions.
- ! Therefore, anything set in the above loop at both the upper
- ! and lower boundaries would be overwritten here. However, the
- ! above loop does not extend to the boundary levels. An array
- ! with a value of 1 at the main diagonal on the left-hand side
- ! and with values of 0 at all other diagonals on the left-hand
- ! side will preserve the right-hand side value at that level.
- !
- ! wp3(1) wp2(1) ... wp3(nzmax) wp2(nzmax)
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 1.0 1.0 ... 1.0 1.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 0.0 ]
-
- ! Lower boundary
- k = 1
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
- wp3_cur_row = 1
- wp2_cur_row = 4
-
- ! w'^2
- lhs_a_csr(wp2_cur_row:wp2_cur_row + 3) = 0.0_core_rknd
- lhs_a_csr(wp2_cur_row + 1) = 1.0_core_rknd
-
- ! w'^3
- lhs_a_csr(wp3_cur_row:wp3_cur_row + 2) = 0.0_core_rknd
- lhs_a_csr(wp3_cur_row) = 1.0_core_rknd
-
- ! w'^2
- !lhs(:,k_wp2) = 0.0_core_rknd
- !lhs(m_k_mdiag,k_wp2) = 1.0_core_rknd
- ! w'^3
- !lhs(:,k_wp3) = 0.0_core_rknd
- !lhs(t_k_tdiag,k_wp3) = 1.0_core_rknd
-
- ! Upper boundary
- k = gr%nz
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
- ! w'^2
- lhs_a_csr(intlc_5d_5d_ja_size - 2:intlc_5d_5d_ja_size) = 0.0_core_rknd
- lhs_a_csr(intlc_5d_5d_ja_size) = 1.0_core_rknd
-
- ! w'^3
- lhs_a_csr(intlc_5d_5d_ja_size - 6:intlc_5d_5d_ja_size - 3) = 0.0_core_rknd
- lhs_a_csr(intlc_5d_5d_ja_size - 4) = 1.0_core_rknd
-
- ! w'^2
- !lhs(:,k_wp2) = 0.0_core_rknd
- !lhs(m_k_mdiag,k_wp2) = 1.0_core_rknd
- ! w'^3
- !lhs(:,k_wp3) = 0.0_core_rknd
- !lhs(t_k_tdiag,k_wp3) = 1.0_core_rknd
-
-
- return
- end subroutine wp23_lhs_csr
-#endif /* MKL */
-
- !=============================================================================
- subroutine wp23_rhs( dt, wp2, wp3, a1, a1_zt, &
- a3, a3_zt, wp3_on_wp2, wpthvp, wp2thvp, um, vm, &
- upwp, vpwp, up2, vp2, Kw1, Kw8, Kh_zt, &
- Skw_zt, tau1m, tauw3t, C1_Skw_fnc, &
- C11_Skw_fnc, rho_ds_zm, invrs_rho_ds_zt, radf, &
- thv_ds_zm, thv_ds_zt, l_crank_nich_diff, &
- rhs )
-
- ! Description:
- ! Compute RHS vector for w'^2 and w'^3.
- ! This subroutine computes the explicit portion of
- ! the w'^2 and w'^3 equations.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable
-
- use crmx_grid_class, only: &
- ddzt ! Procedure
-
- use crmx_parameters_tunable, only: &
- C4, & ! Variables
- C5, &
- C8, &
- C8b, &
- C12, &
- C15, &
- nu1_vert_res_dep, &
- nu8_vert_res_dep
-
- use crmx_constants_clubb, only: &
- w_tol_sqd, & ! Variable(s)
- eps, &
- three_halves, &
- gamma_over_implicit_ts
-
- use crmx_model_flags, only: &
- l_tke_aniso ! Variable
-
- use crmx_diffusion, only: &
- diffusion_zm_lhs, & ! Procedures
- diffusion_zt_lhs
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable
- core_rknd
-
- use crmx_stats_variables, only: &
- l_stats_samp, iwp2_dp1, iwp2_dp2, zm, iwp2_bp, & ! Variable(s)
- iwp2_pr1, iwp2_pr2, iwp2_pr3, iwp3_ta, zt, &
- iwp3_tp, iwp3_bp1, iwp3_pr2, iwp3_pr1, iwp3_dp1, iwp3_bp2
-
- use crmx_stats_type, only: &
- stat_update_var_pt, & ! Procedure(s)
- stat_begin_update_pt, &
- stat_modify_pt
-
- use crmx_advance_helper_module, only: set_boundary_conditions_rhs
-
-
- implicit none
-
- ! Constant parameters
- logical, parameter :: &
- l_wp3_2nd_buoyancy_term = .true.
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep length [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp3, & ! w'^3 (thermodynamic levels) [m^3/s^3]
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- a3, & ! sigma_sqd_w term a_3 (momentum levels) [-]
- a3_zt, & ! a_3 interpolated to thermodynamic levels [-]
- wp3_on_wp2, & ! Smoothed version of wp3 / wp2 [m/s]
- wpthvp, & ! w'th_v' (momentum levels) [K m/s]
- wp2thvp, & ! w'^2th_v' (thermodynamic levels) [K m^2/s^2]
- um, & ! u wind component (thermodynamic levels) [m/s]
- vm, & ! v wind component (thermodynamic levels) [m/s]
- upwp, & ! u'w' (momentum levels) [m^2/s^2]
- vpwp, & ! v'w' (momentum levels) [m^2/s^2]
- up2, & ! u'^2 (momentum levels) [m^2/s^2]
- vp2, & ! v'^2 (momentum levels) [m^2/s^2]
- Kw1, & ! Coefficient of eddy diffusivity for w'^2 [m^2/s]
- Kw8, & ! Coefficient of eddy diffusivity for w'^3 [m^2/s]
- Kh_zt, & ! Eddy diffusivity on thermodynamic levels [m^2/s]
- Skw_zt, & ! Skewness of w on thermodynamic levels [-]
- tau1m, & ! Time-scale tau on momentum levels [s]
- tauw3t, & ! Time-scale tau on thermodynamic levels [s]
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied [-]
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- radf, & ! Buoyancy production at the CL top [m^2/s^3]
- thv_ds_zm, & ! Dry, base-state theta_v on momentum levs. [K]
- thv_ds_zt ! Dry, base-state theta_v on thermo. levs. [K]
-
- logical, intent(in) :: &
- l_crank_nich_diff ! Turns on/off Crank-Nicholson diffusion.
-
- ! Output Variable
- real( kind = core_rknd ), dimension(2*gr%nz), intent(out) :: &
- rhs ! RHS of band matrix
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- dum_dz, dvm_dz ! Vertical derivatives of um and vm
-
- ! Array indices
- integer :: k, km1, kp1, k_wp2, k_wp3, k_wp2_low, k_wp2_high, &
- k_wp3_low, k_wp3_high
-
- ! For "over-implicit" weighted time step.
- ! This vector holds output from the LHS (implicit) portion of a term at a
- ! given vertical level. This output is weighted and applied to the RHS.
- ! This is used if the implicit portion of the term is "over-implicit", which
- ! means that the LHS contribution is given extra weight (>1) in order to
- ! increase numerical stability. A weighted factor must then be applied to
- ! the RHS in order to balance the weight.
- real( kind = core_rknd ), dimension(5) :: lhs_fnc_output
-
- real( kind = core_rknd ), dimension(3) :: &
- rhs_diff ! For use in Crank-Nicholson eddy diffusion.
-
- real( kind = core_rknd ) :: temp
-
-
- ! Initialize the right-hand side vector to 0.
- rhs = 0.0_core_rknd
-
- if ( l_wp3_2nd_buoyancy_term ) then
- ! Compute the vertical derivative of the u and v winds
- dum_dz = ddzt( um )
- dvm_dz = ddzt( vm )
- else
- dum_dz = -999._core_rknd
- dvm_dz = -999._core_rknd
- end if
-
- do k = 2, gr%nz-1, 1
-
-
- ! Define indices
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- k_wp3 = 2*k - 1
- k_wp2 = 2*k
-
-
- !!!!!***** w'^2 *****!!!!!
-
- ! w'^2: Right-hand side (explicit w'^2 portion of the code).
-
- ! RHS time tendency.
- rhs(k_wp2) &
- = + ( 1.0_core_rknd / real( dt, kind = core_rknd ) ) * wp2(k)
-
- ! RHS buoyancy production (bp) term and pressure term 2 (pr2).
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + wp2_terms_bp_pr2_rhs( C5, thv_ds_zm(k), wpthvp(k) )
-
- ! RHS buoyancy production at CL top due to LW radiative cooling
- rhs(k_wp2) = rhs(k_wp2) + radf(k)
-
- ! RHS pressure term 3 (pr3).
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + wp2_term_pr3_rhs( C5, thv_ds_zm(k), wpthvp(k), upwp(k), um(kp1), &
- um(k), vpwp(k), vm(kp1), vm(k), gr%invrs_dzm(k) )
-
- ! RHS dissipation term 1 (dp1).
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + wp2_term_dp1_rhs( C1_Skw_fnc(k), tau1m(k), w_tol_sqd )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS dissipation term 1 (dp1).
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the term
- ! more numerically stable (see note below for w'^3 RHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1) &
- = wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp2(k) )
-
- ! RHS eddy diffusion term: dissipation term 2 (dp2).
- if ( l_crank_nich_diff ) then
- ! These lines are for the diffusional term with a Crank-Nicholson
- ! time step. They are not used for completely implicit diffusion.
- rhs_diff(1:3) &
- = (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zm_lhs( Kw1(k), Kw1(kp1), nu1_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- rhs(k_wp2) = rhs(k_wp2) &
- - rhs_diff(3) * wp2(km1) &
- - rhs_diff(2) * wp2(k) &
- - rhs_diff(1) * wp2(kp1)
- endif
-
- ! RHS pressure term 1 (pr1).
- if ( l_tke_aniso ) then
-
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + wp2_term_pr1_rhs( C4, up2(k), vp2(k), tau1m(k) )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS dissipation term 1 (dp1).
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 RHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1) &
- = wp2_term_pr1_lhs( C4, tau1m(k) )
- rhs(k_wp2) &
- = rhs(k_wp2) &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp2(k) )
-
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for wp2.
-
- ! w'^2 term dp2 has both implicit and explicit components (if the
- ! Crank-Nicholson scheme is selected); call stat_begin_update_pt.
- ! Since stat_begin_update_pt automatically subtracts the value sent in,
- ! reverse the sign on right-hand side diffusion component. If
- ! Crank-Nicholson diffusion is not selected, the stat_begin_update_pt
- ! will not be called.
- if ( l_crank_nich_diff ) then
- call stat_begin_update_pt( iwp2_dp2, k, &
- rhs_diff(3) * wp2(km1) &
- + rhs_diff(2) * wp2(k) &
- + rhs_diff(1) * wp2(kp1), zm )
- endif
-
- ! w'^2 term bp is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of w'^2 term bp, substitute 0 for the
- ! C_5 input to function wp2_terms_bp_pr2_rhs.
- call stat_update_var_pt( iwp2_bp, k, &
- wp2_terms_bp_pr2_rhs( 0.0_core_rknd, thv_ds_zm(k), wpthvp(k) ), zm )
-
- ! w'^2 term pr1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp2_term_pr1_rhs.
- if ( l_tke_aniso ) then
- call stat_begin_update_pt( iwp2_pr1, k, &
- -wp2_term_pr1_rhs( C4, up2(k), vp2(k), tau1m(k) ), zm )
-
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note below for
- ! w'^3 RHS turbulent advection (ta) and turbulent
- ! production (tp) terms).
- lhs_fnc_output(1) &
- = wp2_term_pr1_lhs( C4, tau1m(k) )
- call stat_modify_pt( iwp2_pr1, k, &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp2(k) ), zm )
- endif
-
- ! w'^2 term pr2 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp2_terms_bp_pr2_rhs.
- ! Note: To find the contribution of w'^2 term pr2, add 1 to the
- ! C_5 input to function wp2_terms_bp_pr2_rhs.
- call stat_begin_update_pt( iwp2_pr2, k, &
- -wp2_terms_bp_pr2_rhs( (1.0_core_rknd+C5), thv_ds_zm(k), wpthvp(k) ), zm )
-
- ! w'^2 term dp1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp2_term_dp1_rhs.
- call stat_begin_update_pt( iwp2_dp1, k, &
- -wp2_term_dp1_rhs( C1_Skw_fnc(k), tau1m(k), w_tol_sqd ), zm )
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note below for w'^3 RHS
- ! turbulent advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1) &
- = wp2_term_dp1_lhs( C1_Skw_fnc(k), tau1m(k) )
- call stat_modify_pt( iwp2_dp1, k, &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp2(k) ), zm )
-
- ! w'^2 term pr3 is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( iwp2_pr3, k, &
- wp2_term_pr3_rhs( C5, thv_ds_zm(k), wpthvp(k), upwp(k), um(kp1), &
- um(k), vpwp(k), vm(kp1), vm(k), gr%invrs_dzm(k) ), &
- zm )
-
- endif
-
-
-
- !!!!!***** w'^3 *****!!!!!
-
- ! w'^3: Right-hand side (explicit w'^3 portion of the code).
-
- ! RHS time tendency.
- rhs(k_wp3) = &
- + ( 1.0_core_rknd / real( dt, kind = core_rknd ) * wp3(k) )
-
- ! RHS turbulent advection (ta) and turbulent production (tp) terms.
-! rhs(k_wp3) &
-! = rhs(k_wp3) &
-! + wp3_terms_ta_tp_rhs( wp3_zm(k), wp3_zm(km1), &
-! wp2(k), wp2(km1), &
-! a1(k), a1_zt(k), a1(km1), &
-! a3(k), a3_zt(k), a3(km1), &
-! wp3_on_wp2(k), wp3_on_wp2(km1), &
-! rho_ds_zm(k), rho_ds_zm(km1), &
-! invrs_rho_ds_zt(k), &
-! three_halves, &
-! gr%invrs_dzt(k) )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS turbulent advection (ta) and turbulent production (tp) terms.
- !
- ! Note: An "over-implicit" weighted time step is applied to these terms.
- ! The weight of the implicit portion of these terms is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (w'^3 in this case), y(t) is the linearized portion of
- ! the terms that gets treated implicitly, and RHS is the portion of
- ! the terms that is always treated explicitly. A weight of greater
- ! than 1 can be applied to make the terms more numerically stable.
- lhs_fnc_output(1:5) &
- = wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k), a3_zt(k), a3(km1), &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
- rhs(k_wp3) &
- = rhs(k_wp3) &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp3(kp1) &
- - lhs_fnc_output(2) * wp2(k) &
- - lhs_fnc_output(3) * wp3(k) &
- - lhs_fnc_output(4) * wp2(km1) &
- - lhs_fnc_output(5) * wp3(km1) )
-
- ! RHS buoyancy production (bp) term and pressure term 2 (pr2).
- rhs(k_wp3) &
- = rhs(k_wp3) &
- + wp3_terms_bp1_pr2_rhs( C11_Skw_fnc(k), thv_ds_zt(k), wp2thvp(k) )
-
- ! RHS pressure term 1 (pr1).
- rhs(k_wp3) &
- = rhs(k_wp3) &
- + wp3_term_pr1_rhs( C8, C8b, tauw3t(k), Skw_zt(k), wp3(k) )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS pressure term 1 (pr1).
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs_fnc_output(1) &
- = wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
- rhs(k_wp3) &
- = rhs(k_wp3) &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp3(k) )
-
- ! RHS eddy diffusion term: dissipation term 1 (dp1).
- if ( l_crank_nich_diff ) then
- ! These lines are for the diffusional term with a Crank-Nicholson
- ! time step. They are not used for completely implicit diffusion.
- rhs_diff(1:3) &
- = C12 * (1.0_core_rknd/2.0_core_rknd) &
- * diffusion_zt_lhs( Kw8(k), Kw8(km1), nu8_vert_res_dep, &
- gr%invrs_dzm(km1), gr%invrs_dzm(k), &
- gr%invrs_dzt(k), k )
- rhs(k_wp3) = rhs(k_wp3) &
- - rhs_diff(3) * wp3(km1) &
- - rhs_diff(2) * wp3(k) &
- - rhs_diff(1) * wp3(kp1)
- endif
-
- if ( l_wp3_2nd_buoyancy_term ) then
- ! RHS 2nd bouyancy term
- rhs(k_wp3) = rhs(k_wp3) &
- + wp3_term_bp2_rhs( C15, Kh_zt(k), wpthvp(k), wpthvp(km1), &
- dum_dz(k), dum_dz(km1), dvm_dz(k), dvm_dz(km1), &
- upwp(k), upwp(km1), vpwp(k), vpwp(km1), &
- thv_ds_zt(k), gr%invrs_dzt(k) )
- end if
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for wp3.
-
- ! w'^3 term ta has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp3_terms_ta_tp_rhs.
- ! Note: To find the contribution of w'^3 term ta, add 3 to all of the
- ! a_3 inputs and substitute 0 for the three_halves input to
- ! function wp3_terms_ta_tp_rhs.
-! call stat_begin_update_pt( iwp3_ta, k, &
-! -wp3_terms_ta_tp_rhs( wp3_zm(k), wp3_zm(km1), &
-! wp2(k), wp2(km1), &
-! a1(k), a1_zt(k), a1(km1), &
-! a3(k)+3.0_core_rknd, a3_zt(k)+3.0_core_rknd,
-! a3(km1)+3.0_core_rknd, &
-! wp3_on_wp2(k), wp3_on_wp2(km1), &
-! rho_ds_zm(k), rho_ds_zm(km1), &
-! invrs_rho_ds_zt(k), &
-! 0.0_core_rknd, &
-! gr%invrs_dzt(k) ), &
-! zt )
- call stat_begin_update_pt( iwp3_ta, k, 0.0_core_rknd, zt )
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1:5) &
- = wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- a1(k), a1_zt(k), a1(km1), &
- a3(k)+3.0_core_rknd, a3_zt(k)+3.0_core_rknd, &
- a3(km1)+3.0_core_rknd, &
- wp3_on_wp2(k), wp3_on_wp2(km1), &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- 0.0_core_rknd, &
- gr%invrs_dzt(k), k )
- call stat_modify_pt( iwp3_ta, k, &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp3(kp1) &
- - lhs_fnc_output(2) * wp2(k) &
- - lhs_fnc_output(3) * wp3(k) &
- - lhs_fnc_output(4) * wp2(km1) &
- - lhs_fnc_output(5) * wp3(km1) ), zt )
-
- ! w'^3 term tp has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp3_terms_ta_tp_rhs.
- ! Note: To find the contribution of w'^3 term tp, substitute 0 for all
- ! of the a_1 and a_3 inputs and subtract 3 from all of the a_3
- ! inputs to function wp3_terms_ta_tp_rhs.
-! call stat_begin_update_pt( iwp3_tp, k, &
-! -wp3_terms_ta_tp_rhs( wp3_zm(k), wp3_zm(km1), &
-! wp2(k), wp2(km1), &
-! 0.0_core_rknd, 0.0_core_rknd, 0.0_core_rknd, &
-! 0.0_core_rknd-3.0_core_rknd, 0.0_core_rknd-3.0_core_rknd,
-! 0.0_core_rknd-3.0_core_rknd, &
-! 0.0_core_rknd, 0.0_core_rknd, &
-! rho_ds_zm(k), rho_ds_zm(km1), &
-! invrs_rho_ds_zt(k), &
-! three_halves, &
-! gr%invrs_dzt(k) ), &
-! zt )
- call stat_begin_update_pt( iwp3_tp, k, 0.0_core_rknd, zt )
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1:5) &
- = wp3_terms_ta_tp_lhs( wp2(k), wp2(km1), &
- 0.0_core_rknd, 0.0_core_rknd, 0.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd-3.0_core_rknd, &
- 0.0_core_rknd, 0.0_core_rknd, &
- rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), &
- three_halves, &
- gr%invrs_dzt(k), k )
- call stat_modify_pt( iwp3_tp, k, &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(2) * wp2(k) &
- - lhs_fnc_output(4) * wp2(km1) ), zt )
-
- ! w'^3 term bp is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of w'^3 term bp, substitute 0 for the
- ! C_11 skewness function input to function wp3_terms_bp1_pr2_rhs.
- call stat_update_var_pt( iwp3_bp1, k, &
- wp3_terms_bp1_pr2_rhs( 0.0_core_rknd, thv_ds_zt(k), wp2thvp(k) ), zt )
-
- ! w'^3 term pr2 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp3_terms_bp1_pr2_rhs.
- ! Note: To find the contribution of w'^3 term pr2, add 1 to the
- ! C_11 skewness function input to function wp3_terms_bp1_pr2_rhs.
- call stat_begin_update_pt( iwp3_pr2, k, &
- -wp3_terms_bp1_pr2_rhs( (1.0_core_rknd+C11_Skw_fnc(k)), thv_ds_zt(k), &
- wp2thvp(k) ), &
- zt )
-
- ! w'^3 term pr1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on wp3_term_pr1_rhs.
- call stat_begin_update_pt( iwp3_pr1, k, &
- -wp3_term_pr1_rhs( C8, C8b, tauw3t(k), Skw_zt(k), wp3(k) ), &
- zt )
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) and turbulent production (tp) terms).
- lhs_fnc_output(1) &
- = wp3_term_pr1_lhs( C8, C8b, tauw3t(k), Skw_zt(k) )
- call stat_modify_pt( iwp3_pr1, k, &
- + ( 1.0_core_rknd - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wp3(k) ), zt )
-
- ! w'^3 term dp1 has both implicit and explicit components (if the
- ! Crank-Nicholson scheme is selected); call stat_begin_update_pt.
- ! Since stat_begin_update_pt automatically subtracts the value sent in,
- ! reverse the sign on right-hand side diffusion component. If
- ! Crank-Nicholson diffusion is not selected, the stat_begin_update_pt
- ! will not be called.
- if ( l_crank_nich_diff ) then
- call stat_begin_update_pt( iwp3_dp1, k, &
- rhs_diff(3) * wp3(km1) &
- + rhs_diff(2) * wp3(k) &
- + rhs_diff(1) * wp3(kp1), zt )
- endif
-
- if ( l_wp3_2nd_buoyancy_term ) then
- temp = wp3_term_bp2_rhs( C15, Kh_zt(k), wpthvp(k), wpthvp(km1), &
- dum_dz(k), dum_dz(km1), dvm_dz(k), dvm_dz(km1), &
- upwp(k), upwp(km1), vpwp(k), vpwp(km1), &
- thv_ds_zt(k), gr%invrs_dzt(k) )
- call stat_update_var_pt( iwp3_bp2, k, temp, zt )
- end if
-
- endif ! l_stats_samp
-
- enddo ! k = 2..gr%nz-1
-
-
- ! Boundary conditions
-
- ! Both wp2 and wp3 used fixed-point boundary conditions.
- ! Therefore, anything set in the above loop at both the upper
- ! and lower boundaries would be overwritten here. However, the
- ! above loop does not extend to the boundary levels. An array
- ! with a value of 1 at the main diagonal on the left-hand side
- ! and with values of 0 at all other diagonals on the left-hand
- ! side will preserve the right-hand side value at that level.
-
- ! Lower boundary
- k = 1
- k_wp3_low = 2*k - 1
- k_wp2_low = 2*k
-
- ! Upper boundary
- k = gr%nz
- k_wp3_high = 2*k - 1
- k_wp2_high = 2*k
-
-
- ! The value of w'^2 at the lower boundary will remain the same.
- ! When the lower boundary is at the surface, the surface value of
- ! w'^2 is set in subroutine surface_varnce (surface_varnce_module.F).
-
- ! The value of w'^3 at the lower boundary will be 0.
-
- ! The value of w'^2 at the upper boundary will be set to the threshold
- ! minimum value of w_tol_sqd.
-
- ! The value of w'^3 at the upper boundary will be set to 0.
- call set_boundary_conditions_rhs( &
- wp2(1), k_wp2_low, w_tol_sqd, k_wp2_high, & ! Intent(in)
- rhs, & ! Intent(inout)
- 0.0_core_rknd, k_wp3_low, 0.0_core_rknd, k_wp3_high )
-
- return
-
- end subroutine wp23_rhs
-
- !=============================================================================
- pure function wp2_term_ta_lhs( rho_ds_ztp1, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_dzm ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection term for w'^2: implicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'^3 )/dz.
- !
- ! The term is solved for completely implicitly, such that:
- !
- ! - (1/rho_ds) * d( rho_ds * w'^3(t+1) )/dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'^3 being used is from
- ! the next timestep, which is being advanced to in solving the d(w'^2)/dt
- ! and d(w'^3)/dt equations.
- !
- ! This term is discretized as follows:
- !
- ! While the values of w'^2 are found on the momentum levels, the values of
- ! w'^3 are found on the thermodynamic levels. Additionally, the values of
- ! rho_ds_zt are found on the thermodynamic levels, and the values of
- ! invrs_rho_ds_zm are found on the momentum levels. On the thermodynamic
- ! levels, the values of rho_ds_zt are multiplied by the values of w'^3. The
- ! derivative of (rho_ds_zt * w'^3) is taken over the intermediate (central)
- ! momentum level, where it is multiplied by invrs_rho_ds_zm, yielding the
- ! desired results.
- !
- ! -----rho_ds_ztp1--------wp3p1---------------------------- t(k+1)
- !
- ! ========invrs_rho_ds_zm==========d(rho_ds*wp3)/dz======== m(k)
- !
- ! -----rho_ds_zt----------wp3------------------------------ t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- rho_ds_ztp1, & ! Dry, static density at thermo. level (k+1) [kg/m^3]
- rho_ds_zt, & ! Dry, static density at thermo. level (k) [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ moment. lev. (k) [m^3/kg]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(2) :: lhs
-
- ! Thermodynamic superdiagonal: [ x wp3(k+1,) ]
- lhs(kp1_tdiag) &
- = + invrs_rho_ds_zm * invrs_dzm * rho_ds_ztp1
-
- ! Thermodynamic subdiagonal: [ x wp3(k,) ]
- lhs(k_tdiag) &
- = - invrs_rho_ds_zm * invrs_dzm * rho_ds_zt
-
- return
-
- end function wp2_term_ta_lhs
-
- !=============================================================================
- pure function wp2_terms_ac_pr2_lhs( C5, wm_ztp1, wm_zt, invrs_dzm ) &
- result( lhs )
-
- ! Description:
- ! Accumulation of w'^2 and w'^2 pressure term 2: implicit portion of the
- ! code.
- !
- ! The d(w'^2)/dt equation contains an accumulation term:
- !
- ! - 2 w'^2 dw/dz;
- !
- ! and pressure term 2:
- !
- ! - C_5 ( -2 w'^2 dw/dz + 2 (g/th_0) w'th_v' ).
- !
- ! The w'^2 accumulation term is completely implicit, while w'^2 pressure
- ! term 2 has both implicit and explicit components. The accumulation term
- ! and the implicit portion of pressure term 2 are combined and solved
- ! together as:
- !
- ! + ( 1 - C_5 ) ( -2 w'^2(t+1) dw/dz ).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the "2" is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'^2 being used is from
- ! the next timestep, which is being advanced to in solving the d(w'^2)/dt
- ! equation.
- !
- ! The terms are discretized as follows:
- !
- ! The values of w'^2 are found on the momentum levels, while the values of
- ! wm_zt (mean vertical velocity on thermodynamic levels) are found on the
- ! thermodynamic levels. The vertical derivative of wm_zt is taken over the
- ! intermediate (central) momentum level. It is then multiplied by w'^2
- ! (implicitly calculated at timestep (t+1)) and the coefficients to yield
- ! the desired results.
- !
- ! -------wm_ztp1------------------------------------------- t(k+1)
- !
- ! ===============d(wm_zt)/dz============wp2================ m(k)
- !
- ! -------wm_zt--------------------------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C5, & ! Model parameter C_5 [-]
- wm_ztp1, & ! w wind component at t:hermodynamic levels (k+1) [m/s]
- wm_zt, & ! w wind component at thermodynamic levels (k) [m/s]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Momentum main diagonal: [ x wp2(k,) ]
- lhs &
- = + ( 1.0_core_rknd - C5 ) * 2.0_core_rknd * invrs_dzm * ( wm_ztp1 - wm_zt )
-
- return
-
- end function wp2_terms_ac_pr2_lhs
-
- !=============================================================================
- pure function wp2_term_dp1_lhs( C1_Skw_fnc, tau1m ) &
- result( lhs )
-
- ! Description:
- ! Dissipation term 1 for w'^2: implicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains dissipation term 1:
- !
- ! - ( C_1 / tau_1m ) w'^2.
- !
- ! Since w'^2 has a minimum threshold, the term should be damped only to that
- ! threshold. The term becomes:
- !
- ! - ( C_1 / tau_1m ) * ( w'^2 - threshold ).
- !
- ! This term is broken into implicit and explicit portions. The implicit
- ! portion of this term is:
- !
- ! - ( C_1 / tau_1m ) w'^2(t+1).
- !
- ! Note: When the implicit term is brought over to the left-hand side, the
- ! sign is reversed and the leading "-" in front of the term is
- ! changed to a "+".
- !
- ! The timestep index (t+1) means that the value of w'^2 being used is from
- ! the next timestep, which is being advanced to in solving the d(w'^2)/dt
- ! equation.
- !
- ! The values of w'^2 are found on the momentum levels. The values of the
- ! C_1 skewness function and time-scale tau1m are also found on the momentum
- ! levels.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied (k) [-]
- tau1m ! Time-scale tau at momentum levels (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Momentum main diagonal: [ x wp2(k,) ]
- lhs &
- = + C1_Skw_fnc / tau1m
-
- return
- end function wp2_term_dp1_lhs
-
- !=============================================================================
- pure function wp2_term_pr1_lhs( C4, tau1m ) &
- result( lhs )
-
- ! Description
- ! Pressure term 1 for w'^2: implicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains pressure term 1:
- !
- ! - ( C_4 / tau_1m ) * ( w'^2 - (2/3)*em ),
- !
- ! where em = (1/2) * ( w'^2 + u'^2 + v'^2 ).
- !
- ! This simplifies to:
- !
- ! - ( C_4 / tau_1m ) * (2/3) * w'^2
- ! + ( C_4 / tau_1m ) * (1/3) * ( u'^2 + v'^2 ).
- !
- ! Pressure term 1 has both implicit and explicit components. The implicit
- ! portion is:
- !
- ! - ( C_4 / tau_1m ) * (2/3) * w'^2(t+1);
- !
- ! and is computed in this function.
- !
- ! Note: When the implicit term is brought over to the left-hand side, the
- ! sign is reversed and the leading "-" in front of the term is
- ! changed to a "+".
- !
- ! The timestep index (t+1) means that the value of w'^2 being used is from
- ! the next timestep, which is being advanced to in solving the d(w'^2)/dt
- ! equation.
- !
- ! The values of w'^2 are found on momentum levels, as are the values of tau1m.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C4, & ! Model parameter C_4 [-]
- tau1m ! Time-scale tau at momentum levels (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Momentum main diagonal: [ x wp2(k,) ]
- lhs &
- = + ( 2.0_core_rknd * C4 ) / ( 3.0_core_rknd * tau1m )
-
- return
- end function wp2_term_pr1_lhs
-
- !=============================================================================
- pure function wp2_terms_bp_pr2_rhs( C5, thv_ds_zm, wpthvp ) &
- result( rhs )
-
- ! Description:
- ! Buoyancy production of w'^2 and w'^2 pressure term 2: explicit portion of
- ! the code.
- !
- ! The d(w'^2)/dt equation contains a buoyancy production term:
- !
- ! + 2 (g/thv_ds) w'th_v';
- !
- ! and pressure term 2:
- !
- ! - C_5 ( -2 w'^2 dw/dz + 2 (g/thv_ds) w'th_v' ).
- !
- ! The w'^2 buoyancy production term is completely explicit, while w'^2
- ! pressure term 2 has both implicit and explicit components. The buoyancy
- ! production term and the explicit portion of pressure term 2 are combined
- ! and solved together as:
- !
- ! + ( 1 - C_5 ) ( 2 (g/thv_ds) w'th_v' ).
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: &
- ! Variable(s)
- grav ! Gravitational acceleration [m/s^2]
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C5, & ! Model parameter C_5 [-]
- thv_ds_zm, & ! Dry, base-state theta_v at momentum level (k) [K]
- wpthvp ! w'th_v'(k) [K m/s]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( 1.0_core_rknd - C5 ) * 2.0_core_rknd * ( grav / thv_ds_zm ) * wpthvp
-
- return
- end function wp2_terms_bp_pr2_rhs
-
- !=============================================================================
- pure function wp2_term_dp1_rhs( C1_Skw_fnc, tau1m, threshold ) &
- result( rhs )
-
- ! Description:
- ! Dissipation term 1 for w'^2: explicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains dissipation term 1:
- !
- ! - ( C_1 / tau_1m ) w'^2.
- !
- ! Since w'^2 has a minimum threshold, the term should be damped only to that
- ! threshold. The term becomes:
- !
- ! - ( C_1 / tau_1m ) * ( w'^2 - threshold ).
- !
- ! This term is broken into implicit and explicit portions. The explicit
- ! portion of this term is:
- !
- ! + ( C_1 / tau_1m ) * threshold.
- !
- ! The values of the C_1 skewness function, time-scale tau1m, and the
- ! threshold are found on the momentum levels.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C1_Skw_fnc, & ! C_1 parameter with Sk_w applied (k) [-]
- tau1m, & ! Time-scale tau at momentum levels (k) [s]
- threshold ! Minimum allowable value of w'^2 [m^2/s^2]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( C1_Skw_fnc / tau1m ) * threshold
-
- return
- end function wp2_term_dp1_rhs
-
- !=============================================================================
- pure function wp2_term_pr3_rhs( C5, thv_ds_zm, wpthvp, upwp, ump1, &
- um, vpwp, vmp1, vm, invrs_dzm ) &
- result( rhs )
-
- ! Description:
- ! Pressure term 3 for w'^2: explicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains pressure term 3:
- !
- ! + (2/3) C_5 [ (g/thv_ds) w'th_v' - u'w' du/dz - v'w' dv/dz ].
- !
- ! This term is solved for completely explicitly and is discretized as
- ! follows:
- !
- ! The values of w'th_v', u'w', and v'w' are found on the momentum levels,
- ! whereas the values of um and vm are found on the thermodynamic levels.
- ! Additionally, the values of thv_ds_zm are found on the momentum levels.
- ! The derivatives of both um and vm are taken over the intermediate
- ! (central) momentum level. All the remaining mathematical operations take
- ! place at the central momentum level, yielding the desired result.
- !
- ! -----ump1------------vmp1-------------------------------------- t(k+1)
- !
- ! =upwp====d(um)/dz========d(vm)/dz==vpwp===thv_ds_zm==wpthvp==== m(k)
- !
- ! -----um--------------vm---------------------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: & ! Variables
- grav, & ! Gravitational acceleration [m/s^2]
- zero_threshold
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C5, & ! Model parameter C_5 [-]
- thv_ds_zm, & ! Dry, base-state theta_v at momentum level (k) [K]
- wpthvp, & ! w'th_v'(k) [K m/s]
- upwp, & ! u'w'(k) [m^2/s^2]
- ump1, & ! um(k+1) [m/s]
- um, & ! um(k) [m/s]
- vpwp, & ! v'w'(k) [m^2/s^2]
- vmp1, & ! vm(k+1) [m/s]
- vm, & ! vm(k) [m/s]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- ! Michael Falk, 2 August 2007
- ! Use the following code for standard mixing, with c_k=0.548:
- = + (2.0_core_rknd/3.0_core_rknd) * C5 &
- * ( ( grav / thv_ds_zm ) * wpthvp &
- - upwp * invrs_dzm * ( ump1 - um ) &
- - vpwp * invrs_dzm * ( vmp1 - vm ) &
- )
- ! Use the following code for alternate mixing, with c_k=0.1 or 0.2
-! = + (2.0_core_rknd/3.0_core_rknd) * C5 &
-! * ( ( grav / thv_ds_zm ) * wpthvp &
-! - 0. * upwp * invrs_dzm * ( ump1 - um ) &
-! - 0. * vpwp * invrs_dzm * ( vmp1 - vm ) &
-! )
-! eMFc
-
- ! Added by dschanen for ticket #36
- ! We have found that when shear generation is zero this term will only be
- ! offset by hole-filling (wp2_pd) and reduces turbulence
- ! unrealistically at lower altitudes to make up the difference.
- rhs = max( rhs, zero_threshold )
-
- return
- end function wp2_term_pr3_rhs
-
- !=============================================================================
- pure function wp2_term_pr1_rhs( C4, up2, vp2, tau1m ) &
- result( rhs )
-
- ! Description:
- ! Pressure term 1 for w'^2: explicit portion of the code.
- !
- ! The d(w'^2)/dt equation contains pressure term 1:
- !
- ! - ( C_4 / tau_1m ) * ( w'^2 - (2/3)*em );
- !
- ! where em = (1/2) * ( w'^2 + u'^2 + v'^2 ).
- !
- ! This simplifies to:
- !
- ! - ( C_4 / tau_1m ) * (2/3) * w'^2
- ! + ( C_4 / tau_1m ) * (1/3) * ( u'^2 + v'^2 ).
- !
- ! Pressure term 1 has both implicit and explicit components.
- ! The explicit portion is:
- !
- ! + ( C_4 / tau_1m ) * (1/3) * ( u'^2 + v'^2 );
- !
- ! and is computed in this function.
- !
- ! The values of u'^2 and v'^2 are found on momentum levels, as are the
- ! values of tau1m.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C4, & ! Model parameter C_4 [-]
- up2, & ! u'^2(k) [m^2/s^2]
- vp2, & ! v'^2(k) [m^2/s^2]
- tau1m ! Time-scale tau at momentum levels (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( C4 * ( up2 + vp2 ) ) / ( 3.0_core_rknd * tau1m )
-
- return
- end function wp2_term_pr1_rhs
-
- !=============================================================================
- pure function wp3_terms_ta_tp_lhs( wp2, wp2m1, &
- a1, a1_zt, a1m1, &
- a3, a3_zt, a3m1, &
- wp3_on_wp2, wp3_on_wp2_m1, &
- rho_ds_zm, rho_ds_zmm1, &
- invrs_rho_ds_zt, &
- const_three_halves, &
- invrs_dzt, level ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection and turbulent production of w'^3: implicit portion of
- ! the code.
- !
- ! The d(w'^3)/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'^4 )/dz;
- !
- ! and a turbulent production term:
- !
- ! + 3 * ( w'^2 / rho_ds ) * d( rho_ds * w'^2 )/dz.
- !
- ! A substitution is made in order to close the turbulent advection term,
- ! such that:
- !
- ! w'^4 = coef_sig_sqd_w * (w'^2)^2 + a_1 * ( (w'^3)^2 / w'^2 );
- !
- ! where both a_1 and coef_sig_sqd_w are variables that are functions of
- ! sigma_sqd_w, such that:
- !
- ! coef_sig_sqd_w = 3*(sigma_sqd_w)^2 + 6*(1 - sigma_sqd_w)*sigma_sqd_w
- ! + (1 - sigma_sqd_w)^2; and
- !
- ! a_1 = 1 / (1 - sigma_sqd_w).
- !
- ! Since the turbulent advection and turbulent production terms are being
- ! combined, a further substitution is made, such that:
- !
- ! a_3 = coef_sig_sqd_w - 3;
- !
- ! and thus:
- !
- ! w'^4 = (a_3 + 3) * (w'^2)^2 + a_1 * ( (w'^3)^2 / w'^2 ).
- !
- ! The turbulent production term is rewritten as:
- !
- ! + 3 * ( w'^2 / rho_ds ) * d[ rho_ds * w'^2 ]/dz
- ! = + (3/rho_ds) * d[ rho_ds * (w'^2)^2 ]/dz - (3/2) * d[ (w'^2)^2 ]/dz.
- !
- ! The turbulent advection and turbulent production terms are combined as:
- !
- ! - (1/rho_ds) * d [ rho_ds * a_3 * (w'^2)^2 ] / dz
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( (w'^3)^2 / w'^2 ) ] / dz
- ! - (3/2) * d [ (w'^2)^2 ] / dz.
- !
- ! The (w'^2)^2 and (w'^3)^2 terms are both linearized, such that:
- !
- ! ( w'^2(t+1) )^2 = - ( w'^2(t) )^2 + 2 * w'^2(t) * w'^2(t+1);
- ! ( w'^3(t+1) )^2 = - ( w'^3(t) )^2 + 2 * w'^3(t) * w'^3(t+1);
- !
- ! which produces implicit and explicit portions of these terms. The
- ! implicit portion of these terms is:
- !
- ! - (1/rho_ds) * d [ rho_ds * a_3 * 2 * w'^2(t) * w'^2(t+1) ] / dz
- ! - (1/rho_ds) * d [ rho_ds * a_1
- ! * ( 2 * w'^3(t) * w'^3(t+1) ) / w'^2(t) ] / dz
- ! - (3/2) * d [ 2 * w'^2(t) * w'^2(t+1) ] /dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign is
- ! reversed and the leading "-" in front of all d[ ] / dz terms is
- ! changed to a "+".
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(w'^3)/dt and d(w'^2)/dt equations.
- !
- ! The implicit portion of these terms is discretized as follows:
- !
- ! The values of w'^3 are found on the thermodynamic levels, while the values
- ! of w'^2, a_1, and a_3 are found on the momentum levels. Additionally, the
- ! values of rho_ds_zm are found on the momentum levels, and the values of
- ! invrs_rho_ds_zt are found on the thermodynamic levels. The variable w'^3
- ! is interpolated to the intermediate momentum levels. The values of the
- ! mathematical expressions (called F, G, and H here) within the dF/dz,
- ! dG/dz, and dH/dz terms are computed on the momentum levels. Then, the
- ! derivatives (d/dz) of the expressions (F, G, and H) are taken over the
- ! central thermodynamic level, where dF/dz and dG/dz are multiplied by
- ! invrs_rho_ds_zt, and where dH/dz is multiplied by 3/2. This yields the
- ! desired results. In this function, the values of F, G, and H are as
- ! follows:
- !
- ! F = rho_ds_zm * a_3(t) * 2 * w'^2(t) * w'^2(t+1);
- !
- ! G = rho_ds_zm * a_1(t) * ( 2 * w'^3(t) * w'^3(t+1) ) / w'^2(t); and
- !
- ! H = 2 * w'^2(t) * w'^2(t+1).
- !
- !
- ! ------------------------------------------------wp3p1-------------- t(k+1)
- !
- ! ===a3====wp2====rho_ds_zm====a1======================wp3(interp)=== m(k)
- !
- ! ---dH/dz---dF/dz----invrs_rho_ds_zt----dG/dz----wp3---------------- t(k)
- !
- ! ===a3m1==wp2m1==rho_ds_zmm1==a1m1====================wp3(interp)=== m(k-1)
- !
- ! ------------------------------------------------wp3m1-------------- t(k-1)
- !
- ! The vertical indices t(k+1), m(k), t(k), m(k-1), and t(k-1) correspond
- ! with altitudes zt(k+1), zm(k), zt(k), zm(k-1), and zt(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is
- ! used for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_grid_class, only: &
- gr ! Variable gr%weights_zt2zm
-
- use crmx_constants_clubb, only: &
- w_tol_sqd
-
- use crmx_model_flags, only: &
- l_standard_term_ta
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_mdiag = 2, & ! Momentum superdiagonal index.
- k_tdiag = 3, & ! Thermodynamic main diagonal index.
- km1_mdiag = 4, & ! Momentum subdiagonal index.
- km1_tdiag = 5 ! Thermodynamic subdiagonal index.
-
- integer, parameter :: &
- t_above = 1, & ! Index for upper thermodynamic level grid weight.
- t_below = 2 ! Index for lower thermodynamic level grid weight.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- wp2, & ! w'^2(k) [m^2/s^2]
- wp2m1, & ! w'^2(k-1) [m^2/s^2]
- a1, & ! a_1(k) [-]
- a1_zt, & ! a_1 interpolated to thermo. level (k) [-]
- a1m1, & ! a_1(k-1) [-]
- a3, & ! a_3(k) [-]
- a3_zt, & ! a_3 interpolated to thermo. level (k) [-]
- a3m1, & ! a_3(k-1) [-]
- wp3_on_wp2, & ! wp3 / wp2 (k) [m/s]
- wp3_on_wp2_m1, & ! wp3 / wp2 (k-1) [m/s]
- rho_ds_zm, & ! Dry, static density at moment. lev (k) [kg/m^3]
- rho_ds_zmm1, & ! Dry, static density at moment. lev (k-1) [kg/m^3]
- invrs_rho_ds_zt, & ! Inv dry, static density @ thermo lev (k) [m^3/kg]
- const_three_halves, & ! "3/2" ("0" is sent in for wp3_ta budget) [-]
- invrs_dzt ! Inverse of grid spacing (k) [1/m]
-
- integer, intent(in) :: &
- level ! Central thermodynamic level (on which calculation occurs).
-
- ! Return Variable
- real( kind = core_rknd ), dimension(5) :: lhs
-
- ! Local Variables
- integer :: &
- mk, & ! Momentum level directly above central thermodynamic level.
- mkm1 ! Momentum level directly below central thermodynamic level.
-
-
- ! Momentum level (k) is between thermodynamic level (k+1)
- ! and thermodynamic level (k).
- mk = level
-
- ! Momentum level (k-1) is between thermodynamic level (k)
- ! and thermodynamic level (k-1).
- mkm1 = level - 1
-
- if ( l_standard_term_ta ) then
-
- ! The turbulent advection term is discretized normally, in accordance
- ! with the model equations found in the documentation and the description
- ! listed above.
-
- ! Thermodynamic superdiagonal: [ x wp3(k+1,) ]
- lhs(kp1_tdiag) &
- = + invrs_rho_ds_zt &
- * invrs_dzt &
- * rho_ds_zm * a1 &
- * wp3_on_wp2 &
- * gr%weights_zt2zm(t_above,mk)
-
- ! Momentum superdiagonal: [ x wp2(k,) ]
- lhs(k_mdiag) &
- = + invrs_rho_ds_zt &
- * invrs_dzt * rho_ds_zm * a3 * wp2 &
- + const_three_halves &
- * invrs_dzt * wp2
-
- ! Thermodynamic main diagonal: [ x wp3(k,) ]
- lhs(k_tdiag) &
- = + invrs_rho_ds_zt &
- * invrs_dzt &
- * ( rho_ds_zm * a1 &
- * wp3_on_wp2 &
- * gr%weights_zt2zm(t_below,mk) &
- - rho_ds_zmm1 * a1m1 &
- * wp3_on_wp2_m1 &
- * gr%weights_zt2zm(t_above,mkm1) &
- )
-
- ! Momentum subdiagonal: [ x wp2(k-1,) ]
- lhs(km1_mdiag) &
- = - invrs_rho_ds_zt &
- * invrs_dzt * rho_ds_zmm1 * a3m1 * wp2m1 &
- - const_three_halves &
- * invrs_dzt * wp2m1
-
- ! Thermodynamic subdiagonal: [ x wp3(k-1,) ]
- lhs(km1_tdiag) &
- = - invrs_rho_ds_zt &
- * invrs_dzt &
- * rho_ds_zmm1 * a1m1 &
- * wp3_on_wp2_m1 &
- * gr%weights_zt2zm(t_below,mkm1)
-
- else
-
- ! Brian tried a new discretization for the turbulent advection term,
- ! which contains the term:
- ! - (1/rho_ds) * d [ rho_ds * a_1 * (w'^3)^2 / w'^2 ] / dz. In order
- ! to help stabilize w'^3, a_1 has been pulled outside of the derivative.
- ! On the left-hand side of the equation, this effects the thermodynamic
- ! superdiagonal (kp1_tdiag), the thermodynamic main diagonal (k_tdiag),
- ! and the thermodynamic subdiagonal (km1_tdiag).
-
- ! Additionally, the discretization of the turbulent advection term, which
- ! contains the term:
- ! - (1/rho_ds) * d [ rho_ds * (a_3 + 3) * (w'^2)^2 ] / dz, has been
- ! altered to pull (a_3 + 3) outside of the derivative. This was done in
- ! order to help stabilize w'^3. On the left-hand side of the equation,
- ! this effects the momentum superdiagonal (k_mdiag) and the momentum
- ! subdiagonal (km1_mdiag).
-
- ! Thermodynamic superdiagonal: [ x wp3(k+1,) ]
- lhs(kp1_tdiag) &
- = + invrs_rho_ds_zt &
- * a1_zt * invrs_dzt &
- * rho_ds_zm &
- * wp3_on_wp2 &
- * gr%weights_zt2zm(t_above,mk)
-
- ! Momentum superdiagonal: [ x wp2(k,) ]
- lhs(k_mdiag) &
- = + invrs_rho_ds_zt &
- * a3_zt * invrs_dzt * rho_ds_zm * wp2 &
- + const_three_halves &
- * invrs_dzt * wp2
-
- ! Thermodynamic main diagonal: [ x wp3(k,) ]
- lhs(k_tdiag) &
- = + invrs_rho_ds_zt &
- * a1_zt * invrs_dzt &
- * ( rho_ds_zm &
- * wp3_on_wp2 &
- * gr%weights_zt2zm(t_below,mk) &
- - rho_ds_zmm1 &
- * wp3_on_wp2_m1 &
- * gr%weights_zt2zm(t_above,mkm1) &
- )
-
- ! Momentum subdiagonal: [ x wp2(k-1,) ]
- lhs(km1_mdiag) &
- = - invrs_rho_ds_zt &
- * a3_zt * invrs_dzt * rho_ds_zmm1 * wp2m1 &
- - const_three_halves &
- * invrs_dzt * wp2m1
-
- ! Thermodynamic subdiagonal: [ x wp3(k-1,) ]
- lhs(km1_tdiag) &
- = - invrs_rho_ds_zt &
- * a1_zt * invrs_dzt &
- * rho_ds_zmm1 &
- * wp3_on_wp2_m1 &
- * gr%weights_zt2zm(t_below,mkm1)
-
- ! End of code that pulls out a3.
- ! End of Brian's a1 change. Feb. 14, 2008.
-
- end if ! l_standard_term_ta
-
-
- return
- end function wp3_terms_ta_tp_lhs
-
- !=============================================================================
- pure function wp3_terms_ac_pr2_lhs( C11_Skw_fnc, &
- wm_zm, wm_zmm1, invrs_dzt ) &
- result( lhs )
-
- ! Description:
- ! Accumulation of w'^3 and w'^3 pressure term 2: implicit portion of the
- ! code.
- !
- ! The d(w'^3)/dt equation contains an accumulation term:
- !
- ! - 3 w'^3 dw/dz;
- !
- ! and pressure term 2:
- !
- ! - C_11 ( -3 w'^3 dw/dz + 3 (g/th_0) w'^2th_v' ).
- !
- ! The w'^3 accumulation term is completely implicit, while w'^3 pressure
- ! term 2 has both implicit and explicit components. The accumulation term
- ! and the implicit portion of pressure term 2 are combined and solved
- ! together as:
- !
- ! + ( 1 - C_11 ) ( -3 w'^3(t+1) dw/dz ).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the "3" is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'^3 being used is from
- ! the next timestep, which is being advanced to in solving the d(w'^3)/dt
- ! equation.
- !
- ! The terms are discretized as follows:
- !
- ! The values of w'^3 are found on thermodynamic levels, while the values of
- ! wm_zm (mean vertical velocity on momentum levels) are found on momentum
- ! levels. The vertical derivative of wm_zm is taken over the intermediate
- ! (central) thermodynamic level. It is then multiplied by w'^3 (implicitly
- ! calculated at timestep (t+1)) and the coefficients to yield the desired
- ! results.
- !
- ! =======wm_zm============================================= m(k)
- !
- ! ---------------d(wm_zm)/dz------------wp3---------------- t(k)
- !
- ! =======wm_zmm1=========================================== m(k-1)
- !
- ! The vertical indices m(k), t(k), and m(k-1) correspond with altitudes
- ! zm(k), zt(k), and zm(k-1), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied (k) [-]
- wm_zm, & ! w wind component at momentum levels (k) [m/s]
- wm_zmm1, & ! w wind component at momentum levels (k-1) [m/s]
- invrs_dzt ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Thermodynamic main diagonal: [ x wp3(k,) ]
- lhs &
- = + ( 1.0_core_rknd - C11_Skw_fnc ) &
- * 3.0_core_rknd * invrs_dzt * ( wm_zm - wm_zmm1 )
-
- return
- end function wp3_terms_ac_pr2_lhs
-
- !=============================================================================
- pure function wp3_term_pr1_lhs( C8, C8b, tauw3t, Skw_zt ) &
- result( lhs )
-
- ! Description:
- ! Pressure term 1 for w'^3: implicit portion of the code.
- !
- ! Pressure term 1 is the term:
- !
- ! - (C_8/tau_w3t) * ( C_8b * Sk_wt^4 + 1 ) * w'^3;
- !
- ! where Sk_wt = w'^3 / (w'^2)^(3/2).
- !
- ! This term needs to be linearized, so function L(w'^3) is defined to be
- ! equal to this term (pressure term 1), such that:
- !
- ! L(w'^3) = - (C_8/tau_w3t) * ( C_8b * (w'^3)^5 / (w'^2)^6 + w'^3 ).
- !
- ! A Taylor Series expansion (truncated after the first derivative term) of
- ! L(w'^3) around w'^3 = w'^3(t) is used to linearize pressure term 1.
- ! Evaluating L(w'^3) at w'^3(t+1):
- !
- ! L( w'^3(t+1) ) = L( w'^3(t) )
- ! + ( d L(w'^3) / d w'^3 )|_(w'^3=w'^3(t))
- ! * ( w'^3(t+1) - w'^3(t) ).
- !
- ! After evaluating the expression above, the term has become linearized. It
- ! is broken down into implicit (LHS) and explicit (RHS) components.
- ! The implicit portion is:
- !
- ! - (C_8/tau_w3t) * ( 5 * C_8b * Sk_wt^4 + 1 ) * w'^3(t+1).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(w'^3)/dt equation.
- !
- ! The values of w'^3 are found on the thermodynamic levels, as are the
- ! values of tau_w3t and Sk_wt (in Sk_wt, w'^3 is found on thermodynamic
- ! levels and w'^2 is interpolated to thermodynamic levels).
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C8, & ! Model parameter C_8 [-]
- C8b, & ! Model parameter C_8b [-]
- tauw3t, & ! Time-scale tau at thermodynamic levels (k) [s]
- Skw_zt ! Skewness of w at thermodynamic levels (k) [-]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Thermodynamic main diagonal: [ x wp3(k,) ]
- lhs &
- = + ( C8 / tauw3t ) * ( 5.0_core_rknd * C8b * Skw_zt**4 + 1.0_core_rknd )
-
- return
- end function wp3_term_pr1_lhs
-
- !=============================================================================
-! pure function wp3_terms_ta_tp_rhs( wp3_zm, wp3_zmm1, &
-! wp2, wp2m1, &
-! a1, a1_zt, a1m1, &
-! a3, a3_zt, a3m1, &
-! wp3_on_wp2, wp3_on_wp2_m1, &
-! rho_ds_zm, rho_ds_zmm1, &
-! invrs_rho_ds_zt, &
-! const_three_halves, &
-! invrs_dzt ) &
-! result( rhs )
-
- ! Description:
- ! Turbulent advection and turbulent production of wp3: explicit portion of
- ! the code.
- !
- ! The d(w'^3)/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'^4 )/dz;
- !
- ! and a turbulent production term:
- !
- ! + 3 * ( w'^2 / rho_ds ) * d( rho_ds * w'^2 )/dz.
- !
- ! A substitution is made in order to close the turbulent advection term,
- ! such that:
- !
- ! w'^4 = coef_sig_sqd_w * (w'^2)^2 + a_1 * ( (w'^3)^2 / w'^2 );
- !
- ! where both a_1 and coef_sig_sqd_w are variables that are functions of
- ! sigma_sqd_w, such that:
- !
- ! coef_sig_sqd_w = 3*(sigma_sqd_w)^2 + 6*(1 - sigma_sqd_w)*sigma_sqd_w
- ! + (1 - sigma_sqd_w)^2; and
- !
- ! a_1 = 1 / (1 - sigma_sqd_w).
- !
- ! Since the turbulent advection and turbulent production terms are being
- ! combined, a further substitution is made, such that:
- !
- ! a_3 = coef_sig_sqd_w - 3;
- !
- ! and thus:
- !
- ! w'^4 = (a_3 + 3) * (w'^2)^2 + a_1 * ( (w'^3)^2 / w'^2 ).
- !
- ! The turbulent production term is rewritten as:
- !
- ! + 3 * ( w'^2 / rho_ds ) * d[ rho_ds * w'^2 ]/dz
- ! = + (3/rho_ds) * d[ rho_ds * (w'^2)^2 ]/dz - (3/2) * d[ (w'^2)^2 ]/dz.
- !
- ! The turbulent advection and turbulent production terms are combined as:
- !
- ! - (1/rho_ds) * d [ rho_ds * a_3 * (w'^2)^2 ] / dz
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( (w'^3)^2 / w'^2 ) ] / dz
- ! - (3/2) * d [ (w'^2)^2 ] / dz.
- !
- ! The (w'^2)^2 and (w'^3)^2 terms are both linearized, such that:
- !
- ! ( w'^2(t+1) )^2 = - ( w'^2(t) )^2 + 2 * w'^2(t) * w'^2(t+1);
- ! ( w'^3(t+1) )^2 = - ( w'^3(t) )^2 + 2 * w'^3(t) * w'^3(t+1);
- !
- ! which produces implicit and explicit portions of these terms. The
- ! explicit portion of these terms is:
- !
- ! + (1/rho_ds) * d [ rho_ds * a_3 * ( w'^2(t) )^2 ] / dz
- ! + (1/rho_ds) * d [ rho_ds * a_1 * ( w'^3(t) )^2 / w'^2(t) ] / dz
- ! + (3/2) * d [ ( w'^2(t) )^2 ] / dz.
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(w'^3)/dt and d(w'^2)/dt equations.
- !
- ! The explicit portion of these terms is discretized as follows:
- !
- ! The values of w'^3 are found on the thermodynamic levels, while the values
- ! of w'^2, a_1, and a_3 are found on the momentum levels. Additionally, the
- ! values of rho_ds_zm are found on the momentum levels, and the values of
- ! invrs_rho_ds_zt are found on the thermodynamic levels. The variable w'^3
- ! is interpolated to the intermediate momentum levels. The values of the
- ! mathematical expressions (called F, G, and H here) within the dF/dz,
- ! dG/dz, and dH/dz terms are computed on the momentum levels. Then, the
- ! derivatives (d/dz) of the expressions (F, G, and H) are taken over the
- ! central thermodynamic level, where dF/dz and dG/dz are multiplied by
- ! invrs_rho_ds_zt, and where dH/dz is multiplied by 3/2. This yields the
- ! desired results. In this function, the values of F, G, and H are as
- ! follows:
- !
- ! F = rho_ds_zm * a_3(t) * ( w'^2(t) )^2;
- !
- ! G = rho_ds_zm * a_1(t) * ( w'^3(t) )^2 / w'^2(t); and
- !
- ! H = ( w'^2(t) )^2.
- !
- !
- ! ------------------------------------------------wp3p1-------------- t(k+1)
- !
- ! ===a3====wp2====rho_ds_zm====a1======================wp3(interp)=== m(k)
- !
- ! ---dH/dz---dF/dz----invrs_rho_ds_zt----dG/dz----wp3---------------- t(k)
- !
- ! ===a3m1==wp2m1==rho_ds_zmm1==a1m1====================wp3(interp)=== m(k-1)
- !
- ! ------------------------------------------------wp3m1-------------- t(k-1)
- !
- ! The vertical indices t(k+1), m(k), t(k), m(k-1), and t(k-1) correspond
- ! with altitudes zt(k+1), zm(k), zt(k), zm(k-1), and zt(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
-
- ! References:
- !-----------------------------------------------------------------------
-
-! use constants_clubb, only: &
-! w_tol_sqd
-
-! use model_flags, only: &
-! l_standard_term_ta
-
-! implicit none
-
- ! Input Variables
-! real, intent(in) :: &
-! wp3_zm, & ! w'^3 interpolated to momentum lev. (k) [m^3/s^3]
-! wp3_zmm1, & ! w'^3 interpolated to momentum lev. (k-1) [m^3/s^3]
-! wp2, & ! w'^2(k) [m^2/s^2]
-! wp2m1, & ! w'^2(k-1) [m^2/s^2]
-! a1, & ! a_1(k) [-]
-! a1_zt, & ! a_1 interpolated to thermo. level (k) [-]
-! a1m1, & ! a_1(k-1) [-]
-! a3, & ! a_3(k) [-]
-! a3_zt, & ! a_3 interpolated to thermo. level (k) [-]
-! a3m1, & ! a_3(k-1) [-]
-! wp3_on_wp2, & ! (k) [m/s]
-! wp3_on_wp2_m1, & ! (k-1) [m/s]
-! rho_ds_zm, & ! Dry, static density at moment. lev (k) [kg/m^3]
-! rho_ds_zmm1, & ! Dry, static density at moment. lev (k-1) [kg/m^3]
-! invrs_rho_ds_zt, & ! Inv dry, static density @ thermo lev (k) [m^3/kg]
-! const_three_halves, & ! "3/2" ("0" is sent in for wp3_ta budget) [-]
-! invrs_dzt ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
-! real :: rhs
-
-
-! if ( l_standard_term_ta ) then
-
- ! The turbulent advection term is discretized normally, in accordance
- ! with the model equations found in the documentation and the description
- ! listed above.
-
-! rhs &
-! = + invrs_rho_ds_zt &
-! * invrs_dzt &
-! * ( rho_ds_zm * a3 * wp2**2 &
-! - rho_ds_zmm1 * a3m1 * wp2m1**2 &
-! ) &
-! + invrs_rho_ds_zt &
-! * invrs_dzt &
-! * ( rho_ds_zm * a1 &
-! * wp3_zm * wp3_on_wp2 &
-! - rho_ds_zmm1 * a1m1 &
-! * wp3_zmm1 * wp3_on_wp2_m1 &
-! ) &
-! + const_three_halves &
-! * invrs_dzt * ( wp2**2 - wp2m1**2 )
-
-! else
-
- ! Brian tried a new discretization for the turbulent advection term,
- ! which contains the term:
- ! - (1/rho_ds) * d [ rho_ds * a_1 * (w'^3)^2 / w'^2 ] / dz. In order
- ! to help stabilize w'^3, a_1 has been pulled outside of the derivative.
- ! This effects the right-hand side of the equation, as well as the
- ! left-hand side.
-
- ! Additionally, the discretization of the turbulent advection term, which
- ! contains the term:
- ! - (1/rho_ds) * d [ rho_ds * (a_3 + 3) * (w'^2)^2 ] / dz, has been
- ! altered to pull (a_3 + 3) outside of the derivative. This was done in
- ! order to help stabilize w'^3. This effects the right-hand side of the
- ! equation, as well as the left-hand side.
-
-! rhs &
-! = + invrs_rho_ds_zt &
-! * a3_zt * invrs_dzt &
-! * ( rho_ds_zm * wp2**2 &
-! - rho_ds_zmm1 * wp2m1**2 ) &
-! + invrs_rho_ds_zt &
-! * a1_zt * invrs_dzt &
-! * ( rho_ds_zm &
-! * ( wp3_zm * wp3_on_wp2 ) &
-! - rho_ds_zmm1 &
-! * ( wp3_zmm1 * wp3_on_wp2_m1 ) &
-! ) &
-! + const_three_halves &
-! * invrs_dzt * ( wp2**2 - wp2m1**2 )
-
- ! End of code that pulls out a3.
- ! End of Brian's a1 change. Feb. 14, 2008.
-
-! endif ! l_standard_term_ta
-
-
-! return
-! end function wp3_terms_ta_tp_rhs
-
- !=============================================================================
- pure function wp3_terms_bp1_pr2_rhs( C11_Skw_fnc, thv_ds_zt, wp2thvp ) &
- result( rhs )
-
- ! Description:
- ! Buoyancy production of w'^3 and w'^3 pressure term 2: explicit portion of
- ! the code.
- !
- ! The d(w'^3)/dt equation contains a buoyancy production term:
- !
- ! + 3 (g/thv_ds) w'^2th_v';
- !
- ! and pressure term 2:
- !
- ! - C_11 ( -3 w'^3 dw/dz + 3 (g/thv_ds) w'^2th_v' ).
- !
- ! The w'^3 buoyancy production term is completely explicit, while w'^3
- ! pressure term 2 has both implicit and explicit components. The buoyancy
- ! production term and the explicit portion of pressure term 2 are combined
- ! and solved together as:
- !
- ! + ( 1 - C_ll ) ( 3 (g/thv_ds) w'^2th_v' ).
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: & ! Constant(s)
- grav ! Gravitational acceleration [m/s^2]
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C11_Skw_fnc, & ! C_11 parameter with Sk_w applied (k) [-]
- thv_ds_zt, & ! Dry, base-state theta_v at thermo. lev. (k) [K]
- wp2thvp ! w'^2th_v'(k) [K m^2/s^2]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( 1.0_core_rknd - C11_Skw_fnc ) * 3.0_core_rknd * ( grav / thv_ds_zt ) * wp2thvp
-
- return
- end function wp3_terms_bp1_pr2_rhs
-
- !=============================================================================
- pure function wp3_term_bp2_rhs( C15, Kh_zt, wpthvp, wpthvp_m1, &
- dum_dz, dum_dz_m1, dvm_dz, dvm_dz_m1, &
- upwp, upwp_m1, vpwp, vpwp_m1, &
- thv_ds_zt, invrs_dzt ) &
- result( rhs )
-
- ! Description:
- ! Experimental term from CLUBB TRAC ticket #411. The derivative here is of
- ! the form:
- ! - C_15 * Kh * ∂{ grav / thv_ds * [w'th_v'(k) - w'th_v'(k-1)]
- ! -[ u'w'(k) * ∂u(k)/∂z - u'w'(k-1) * ∂u(k-1)/∂z ]
- ! -[ v'w'(k) * ∂v(k)/∂z - v'w'(k-1) * ∂v(k-1)/∂z ] }/∂z.
- !
- ! This does not appear in Andre et al. 1976 or Bougeault et al. 1981, but
- ! is based on experiments in matching LES data.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: & ! Constant(s)
- grav ! Gravitational acceleration [m/s^2]
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C15, & ! Model parameter C15 [-]
- Kh_zt, & ! Eddy-diffusivity on moment. levels [m^2/s]
- wpthvp, & ! w'th_v'(k) [K m/s]
- wpthvp_m1, & ! w'th_v'(k-1) [K m/s]
- dum_dz, & ! d u wind dz (k) [m/s]
- dvm_dz, & ! d v wind dz (k) [m/s]
- dum_dz_m1, & ! d u wind dz (k-1) [m/s]
- dvm_dz_m1, & ! d v wind dz (k-1) [m/s]
- upwp, & ! u'v'(k) [m^2/s^2]
- upwp_m1, & ! u'v'(k-1) [m^2/s^2]
- vpwp, & ! v'w'(k) [m^2/s^2]
- vpwp_m1, & ! v'w'(k-1) [m^2/s^2]
- thv_ds_zt, & ! Dry, base-state theta_v at thermo. lev. (k) [K]
- invrs_dzt ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- ! ---- Begin Code ----
-
-! rhs = - C15 * Kh_zt * invrs_dzt * grav / thv_ds_zt * ( wpthvp - wpthvp_m1 )
-
- rhs = - C15 * Kh_zt * invrs_dzt * &
- ( grav / thv_ds_zt * ( wpthvp - wpthvp_m1 ) &
- - ( upwp * dum_dz - upwp_m1 * dum_dz_m1 ) &
- - ( vpwp * dvm_dz - vpwp_m1 * dvm_dz_m1 ) )
-
- return
- end function wp3_term_bp2_rhs
-
-
- !=============================================================================
- pure function wp3_term_pr1_rhs( C8, C8b, tauw3t, Skw_zt, wp3 ) &
- result( rhs )
-
- ! Description:
- ! Pressure term 1 for w'^3: explicit portion of the code.
- !
- ! Pressure term 1 is the term:
- !
- ! - (C_8/tau_w3t) * ( C_8b * Sk_wt^4 + 1 ) * w'^3;
- !
- ! where Sk_wt = w'^3 / (w'^2)^(3/2).
- !
- ! This term needs to be linearized, so function L(w'^3) is defined to be
- ! equal to this term (pressure term 1), such that:
- !
- ! L(w'^3) = - (C_8/tau_w3t) * ( C_8b * (w'^3)^5 / (w'^2)^6 + w'^3 ).
- !
- ! A Taylor Series expansion (truncated after the first derivative term) of
- ! L(w'^3) around w'^3 = w'^3(t) is used to linearize pressure term 1.
- ! Evaluating L(w'^3) at w'^3(t+1):
- !
- ! L( w'^3(t+1) ) = L( w'^3(t) )
- ! + ( d L(w'^3) / d w'^3 )|_(w'^3=w'^3(t))
- ! * ( w'^3(t+1) - w'^3(t) ).
- !
- ! After evaluating the expression above, the term has become linearized. It
- ! is broken down into implicit (LHS) and explicit (RHS) components.
- ! The explicit portion is:
- !
- ! + (C_8/tau_w3t) * ( 4 * C_8b * Sk_wt^4 + 1 ) * w'^3(t).
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(w'^3)/dt equation.
- !
- ! The values of w'^3 are found on the thermodynamic levels, as are the
- ! values of tau_w3t and Sk_wt (in Sk_wt, w'^3 is found on thermodynamic
- ! levels and w'^2 is interpolated to thermodynamic levels).
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C8, & ! Model parameter C_8 [-]
- C8b, & ! Model parameter C_8b [-]
- tauw3t, & ! Time-scale tau at thermodynamic levels (k) [s]
- Skw_zt, & ! Skewness of w at thermodynamic levels (k) [-]
- wp3 ! w'^3(k) [m^3/s^3]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( C8 / tauw3t ) * ( 4.0_core_rknd * C8b * Skw_zt**4 ) * wp3
-
- return
- end function wp3_term_pr1_rhs
-
-!===============================================================================
-
-end module crmx_advance_wp2_wp3_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_advance_xm_wpxp_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_advance_xm_wpxp_module.F90
deleted file mode 100644
index 160755b817..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_advance_xm_wpxp_module.F90
+++ /dev/null
@@ -1,3213 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: advance_xm_wpxp_module.F90 6146 2013-04-05 18:02:22Z raut@uwm.edu $
-!===============================================================================
-module crmx_advance_xm_wpxp_module
-
- ! Description:
- ! Contains the CLUBB advance_xm_wpxp_module scheme.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- implicit none
-
- private ! Default scope
-
- public :: advance_xm_wpxp
-
- private :: xm_wpxp_lhs, &
- xm_wpxp_rhs, &
- xm_wpxp_solve, &
- xm_wpxp_clipping_and_stats, &
- xm_term_ta_lhs, &
- wpxp_term_ta_lhs, &
- wpxp_term_tp_lhs, &
- wpxp_terms_ac_pr2_lhs, &
- wpxp_term_pr1_lhs, &
- wpxp_terms_bp_pr3_rhs, &
- xm_correction_wpxp_cl, &
- damp_coefficient
-
- ! Parameter Constants
- integer, parameter, private :: &
- nsub = 2, & ! Number of subdiagonals in the LHS matrix
- nsup = 2, & ! Number of superdiagonals in the LHS matrix
- xm_wpxp_thlm = 1, & ! Named constant for thlm solving
- xm_wpxp_rtm = 2, & ! Named constant for rtm solving
- xm_wpxp_scalar = 3 ! Named constant for scalar solving
-
- contains
-
- !=============================================================================
- subroutine advance_xm_wpxp( dt, sigma_sqd_w, wm_zm, wm_zt, wp2, &
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, Kh_zt, &
- tau_zm, Skw_zm, rtpthvp, rtm_forcing, &
- wprtp_forcing, rtm_ref, thlpthvp, &
- thlm_forcing, wpthlp_forcing, thlm_ref, &
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, thv_ds_zm, rtp2, thlp2, &
- w1_zm, w2_zm, varnce_w1_zm, varnce_w2_zm, &
- mixt_frac_zm, l_implemented, &
- sclrpthvp, sclrm_forcing, sclrp2, &
- rtm, wprtp, thlm, wpthlp, &
- err_code, &
- sclrm, wpsclrp )
-
- ! Description:
- ! Advance the mean and flux terms by one timestep.
-
- ! References:
- ! Eqn. 16 & 17 on p. 3546 of
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
-
- ! See Also
- ! ``Equations for CLUBB'' Section 5:
- ! /Implicit solutions for the means and fluxes/
- !-----------------------------------------------------------------------
-
- use crmx_parameters_tunable, only: &
- C6rt, & ! Variable(s)
- C6rtb, &
- C6rtc, &
- C6thl, &
- C6thlb, &
- C6thlc, &
- C7, &
- C7b, &
- C7c, &
- c_K6, &
- C6rt_Lscale0, &
- C6thl_Lscale0, &
- C7_Lscale0, &
- wpxp_L_thresh
-
- use crmx_constants_clubb, only: &
- fstderr, & ! Constant
- rt_tol, &
- thl_tol, &
- thl_tol_mfl, &
- rt_tol_mfl, &
- max_mag_correlation, &
- one, &
- one_half, &
- zero, &
- zero_threshold
-
- use crmx_parameters_model, only: &
- sclr_dim, & ! Variable(s)
- sclr_tol
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_grid_class, only: &
- zm2zt, & ! Procedure(s)
- zt2zm
-
- use crmx_model_flags, only: &
- l_clip_semi_implicit ! Variable(s)
-
- use crmx_mono_flux_limiter, only: &
- calc_turb_adv_range ! Procedure(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_error_code, only: &
- clubb_at_least_debug_level, & ! Procedure(s)
- reportError, &
- fatal_error
-
- use crmx_error_code, only: &
- clubb_var_out_of_range ! Constant(s)
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_end_update, &
- stat_update_var
-
- use crmx_stats_variables, only: &
- zt, &
- zm, &
- irtm_matrix_condt_num, & ! Variables
- ithlm_matrix_condt_num, &
- irtm_sdmp, ithlm_sdmp, &
- l_stats_samp, &
- iC7_Skw_fnc, &
- iC6rt_Skw_fnc, &
- iC6thl_Skw_fnc, &
- l_stats_samp
-
- use crmx_sponge_layer_damping, only: &
- rtm_sponge_damp_settings, &
- thlm_sponge_damp_settings, &
- rtm_sponge_damp_profile, &
- thlm_sponge_damp_profile, &
- sponge_damp_xm ! Procedure(s)
-
- implicit none
-
- ! External
- intrinsic :: exp, sqrt
-
- ! Parameter Constants
- logical, parameter :: &
- l_iter = .true. ! True when the means and fluxes are prognosed
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- sigma_sqd_w, & ! sigma_sqd_w on momentum levels [-]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- Lscale, & ! Turbulent mixing length [m]
- wp3_on_wp2, & ! Smoothed wp3 / wp2 on momentum levels [m/s]
- wp3_on_wp2_zt, & ! Smoothed wp3 / wp2 on thermo. levels [m/s]
- Kh_zt, & ! Eddy diffusivity on thermodynamic levels [m^2/s]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- Skw_zm, & ! Skewness of w on momentum levels [-]
- rtpthvp, & ! r_t'th_v' (momentum levels) [(kg/kg) K]
- rtm_forcing, & ! r_t forcing (thermodynamic levels) [(kg/kg)/s]
- wprtp_forcing, & ! forcing (momentum levels) [(kg/kg)/s^2]
- rtm_ref, & ! rtm for nudging [kg/kg]
- thlpthvp, & ! th_l'th_v' (momentum levels) [K^2]
- thlm_forcing, & ! th_l forcing (thermodynamic levels) [K/s]
- wpthlp_forcing, & ! forcing (momentum levels) [K/s^2]
- thlm_ref, & ! thlm for nudging [K]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ moment. levs. [m^3/kg]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- thv_ds_zm, & ! Dry, base-state theta_v on moment. levs. [K]
- ! Added for clipping by Vince Larson 29 Sep 2007
- rtp2, & ! r_t'^2 (momentum levels) [(kg/kg)^2]
- thlp2, & ! th_l'^2 (momentum levels) [K^2]
- ! End of Vince Larson's addition.
- w1_zm, & ! Mean w (1st PDF component) [m/s]
- w2_zm, & ! Mean w (2nd PDF component) [m/s]
- varnce_w1_zm, & ! Variance of w (1st PDF component) [m^2/s^2]
- varnce_w2_zm, & ! Variance of w (2nd PDF component) [m^2/s^2]
- mixt_frac_zm ! Weight of 1st PDF component (Sk_w dependent) [-]
-
- logical, intent(in) :: &
- l_implemented ! Flag for CLUBB being implemented in a larger model.
-
-
- ! Additional variables for passive scalars
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz,sclr_dim) :: &
- sclrpthvp, sclrm_forcing, & ! [Units vary]
- sclrp2 ! For clipping Vince Larson [Units vary]
-
- ! Input/Output Variables
- real( kind = core_rknd ), intent(inout), dimension(gr%nz) :: &
- rtm, & ! r_t (total water mixing ratio) [kg/kg]
- wprtp, & ! w'r_t' [(kg/kg) m/s]
- thlm, & ! th_l (liquid water potential temperature) [K]
- wpthlp ! w'th_l' [K m/s]
-
- integer, intent(inout) :: err_code ! Error code for the model's status
-
- ! Input/Output Variables
- real( kind = core_rknd ), intent(inout), dimension(gr%nz,sclr_dim) :: &
- sclrm, wpsclrp ! [Units vary]
-
- ! Local variables
- real( kind = core_rknd ), dimension(nsup+nsub+1,2*gr%nz) :: &
- lhs ! Implicit contributions to wpxp/xm (band diag. matrix) (LAPACK)
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- C6rt_Skw_fnc, C6thl_Skw_fnc, C7_Skw_fnc
-
- ! Eddy Diffusion for wpthlp and wprtp.
- real( kind = core_rknd ), dimension(gr%nz) :: Kw6 ! wpxp eddy diff. [m^2/s]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- a1, & ! a_1 (momentum levels); See eqn. 24 in `Equations for CLUBB' [-]
- a1_zt ! a_1 interpolated to thermodynamic levels [-]
-
- ! Variables used as part of the monotonic turbulent advection scheme.
- ! Find the lowermost and uppermost grid levels that can have an effect
- ! on the central thermodynamic level during the course of a time step,
- ! due to the effects of turbulent advection only.
- integer, dimension(gr%nz) :: &
- low_lev_effect, & ! Index of the lowest level that has an effect.
- high_lev_effect ! Index of the highest level that has an effect.
-
- ! Variables used for clipping of w'x' due to correlation
- ! of w with x, such that:
- ! corr_(w,x) = w'x' / [ sqrt(w'^2) * sqrt(x'^2) ];
- ! -1 <= corr_(w,x) <= 1.
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wpxp_upper_lim, & ! Keeps correlations from becoming greater than 1.
- wpxp_lower_lim ! Keeps correlations from becoming less than -1.
-
- real( kind = core_rknd ), dimension(gr%nz) :: dummy_1d ! Unreferenced array
-
- real( kind = core_rknd ), allocatable, dimension(:,:) :: &
- rhs, &! Right-hand sides of band diag. matrix. (LAPACK)
- solution ! solution vectors of band diag. matrix. (LAPACK)
-
- ! Constant parameters as a function of Skw.
-
- integer :: &
- nrhs, & ! Number of RHS vectors
- err_code_xm_wpxp ! Error code
-
- real( kind = core_rknd ) :: rcond
-
- ! Indices
- integer :: i
-
- !---------------------------------------------------------------------------
-
- ! ----- Begin Code -----
- if ( l_clip_semi_implicit ) then
- nrhs = 1
- else
- nrhs = 2+sclr_dim
- endif
-
- ! Allocate rhs and solution vector
- allocate( rhs(2*gr%nz,nrhs) )
- allocate( solution(2*gr%nz,nrhs) )
-
- ! This is initialized solely for the purpose of avoiding a compiler
- ! warning about uninitialized variables.
- dummy_1d = zero
-
- ! Compute C6 and C7 as a function of Skw
- ! The if...then is just here to save compute time
- if ( C6rt /= C6rtb ) then
- C6rt_Skw_fnc(1:gr%nz) = C6rtb + (C6rt-C6rtb) &
- *EXP( -one_half * (Skw_zm(1:gr%nz)/C6rtc)**2 )
- else
- C6rt_Skw_fnc(1:gr%nz) = C6rtb
- endif
-
- if ( C6thl /= C6thlb ) then
- C6thl_Skw_fnc(1:gr%nz) = C6thlb + (C6thl-C6thlb) &
- *EXP( -one_half * (Skw_zm(1:gr%nz)/C6thlc)**2 )
- else
- C6thl_Skw_fnc(1:gr%nz) = C6thlb
- endif
-
- if ( C7 /= C7b ) then
- C7_Skw_fnc(1:gr%nz) = C7b + (C7-C7b) &
- *EXP( -one_half * (Skw_zm(1:gr%nz)/C7c)**2 )
- else
- C7_Skw_fnc(1:gr%nz) = C7b
- endif
-
- ! Damp C6 and C7 as a function of Lscale in stably stratified regions
- C7_Skw_fnc = damp_coefficient( C7, C7_Skw_fnc, &
- C7_Lscale0, wpxp_L_thresh, Lscale )
- C6rt_Skw_fnc = damp_coefficient( C6rt, C6rt_Skw_fnc, &
- C6rt_Lscale0, wpxp_L_thresh, Lscale )
- C6thl_Skw_fnc = damp_coefficient( C6thl, C6thl_Skw_fnc, &
- C6thl_Lscale0, wpxp_L_thresh, Lscale )
-
- ! C6rt_Skw_fnc = C6rt
- ! C6thl_Skw_fnc = C6thl
- ! C7_Skw_fnc = C7
-
- if ( l_stats_samp ) then
-
- call stat_update_var( iC7_Skw_fnc, C7_Skw_fnc, zm )
- call stat_update_var( iC6rt_Skw_fnc, C6rt_Skw_fnc, zm )
- call stat_update_var( iC6thl_Skw_fnc, C6thl_Skw_fnc, zm )
-
- end if
-
- if ( clubb_at_least_debug_level( 2 ) ) then
- ! Assertion check for C7_Skw_fnc
- if ( any( C7_Skw_fnc(:) > one ) .or. any( C7_Skw_fnc(:) < zero ) ) then
- write(fstderr,*) "The C7_Skw_fnc variable is outside the valid range"
- err_code = clubb_var_out_of_range
- return
- end if
- end if
-
- ! Define the Coefficent of Eddy Diffusivity for the wpthlp and wprtp.
- ! Kw6 is used for wpthlp and wprtp, which are located on momentum levels.
- ! Kw6 is located on thermodynamic levels.
- ! Kw6 = c_K6 * Kh_zt
-
- Kw6(1:gr%nz) = c_K6 * Kh_zt(1:gr%nz)
-
- ! Find the number of grid levels, both upwards and downwards, that can
- ! have an effect on the central thermodynamic level during the course of
- ! one time step due to turbulent advection. This is used as part of the
- ! monotonic turbulent advection scheme.
- call calc_turb_adv_range( dt, w1_zm, w2_zm, varnce_w1_zm, varnce_w2_zm, & ! In
- mixt_frac_zm, & ! In
- low_lev_effect, high_lev_effect ) ! Out
-
-
- ! Define a_1 (located on momentum levels).
- ! It is a variable that is a function of sigma_sqd_w (where sigma_sqd_w is
- ! located on momentum levels).
- a1(1:gr%nz) = one / ( one - sigma_sqd_w(1:gr%nz) )
-
- ! Interpolate a_1 from momentum levels to thermodynamic levels. This will
- ! be used for the w'x' turbulent advection (ta) term.
- a1_zt = max( zm2zt( a1 ), zero_threshold ) ! Positive definite quantity
-
- ! Setup and decompose matrix for each variable.
-
- if ( l_clip_semi_implicit ) then
-
- ! Compute the upper and lower limits of w'r_t' at every level,
- ! based on the correlation of w and r_t, such that:
- ! corr_(w,r_t) = w'r_t' / [ sqrt(w'^2) * sqrt(r_t'^2) ];
- ! -1 <= corr_(w,r_t) <= 1.
- if ( l_clip_semi_implicit ) then
- wpxp_upper_lim = max_mag_correlation * sqrt( wp2 * rtp2 )
- wpxp_lower_lim = -wpxp_upper_lim
- endif
-
- ! Compute the implicit portion of the r_t and w'r_t' equations.
- ! Build the left-hand side matrix.
- call xm_wpxp_lhs( l_iter, dt, wprtp, a1, a1_zt, wm_zm, wm_zt, & ! Intent(in)
- wp2, wp3_on_wp2, wp3_on_wp2_zt, & ! Intent(in)
- Kw6, tau_zm, C7_Skw_fnc, & ! Intent(in)
- C6rt_Skw_fnc, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, l_implemented, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Compute the explicit portion of the r_t and w'r_t' equations.
- ! Build the right-hand side vector.
- call xm_wpxp_rhs( xm_wpxp_rtm, l_iter, dt, rtm, wprtp, & ! Intent(in)
- rtm_forcing, wprtp_forcing, C7_Skw_fnc, & ! Intent(in)
- rtpthvp, C6rt_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,1) ) ! Intent(out)
-
- ! Solve r_t / w'r_t'
- if ( l_stats_samp .and. irtm_matrix_condt_num > 0 ) then
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp, rcond ) ! Intent(out)
- else
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp ) ! Intent(out)
- endif
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "Mean total water & total water flux LU decomp. failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_rtm, dt, wp2, rtp2, wm_zt, & ! Intent(in)
- rtm_forcing, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- rt_tol**2, rt_tol, rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,1), & ! Intent(in)
- rtm, rt_tol_mfl, wprtp, & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "rtm monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
-
- ! Compute the upper and lower limits of w'th_l' at every level,
- ! based on the correlation of w and th_l, such that:
- ! corr_(w,th_l) = w'th_l' / [ sqrt(w'^2) * sqrt(th_l'^2) ];
- ! -1 <= corr_(w,th_l) <= 1.
- if ( l_clip_semi_implicit ) then
- wpxp_upper_lim = max_mag_correlation * sqrt( wp2 * thlp2 )
- wpxp_lower_lim = -wpxp_upper_lim
- endif
-
- ! Compute the implicit portion of the th_l and w'th_l' equations.
- ! Build the left-hand side matrix.
- call xm_wpxp_lhs( l_iter, dt, wpthlp, a1, a1_zt, wm_zm, wm_zt, & ! Intent(in)
- wp2, wp3_on_wp2, wp3_on_wp2_zt, & ! Intent(in)
- Kw6, tau_zm, C7_Skw_fnc, & ! Intent(in)
- C6thl_Skw_fnc, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, l_implemented, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Compute the explicit portion of the th_l and w'th_l' equations.
- ! Build the right-hand side vector.
- call xm_wpxp_rhs( xm_wpxp_thlm, l_iter, dt, thlm, wpthlp, & ! Intent(in)
- thlm_forcing, wpthlp_forcing, C7_Skw_fnc, & ! Intent(in)
- thlpthvp, C6thl_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,1) ) ! Intent(out)
-
- ! Solve for th_l / w'th_l'
- if ( l_stats_samp .and. ithlm_matrix_condt_num > 0 ) then
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp, rcond ) ! Intent(out)
- else
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp ) ! Intent(out)
- endif
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "Liquid pot. temp & thetal flux LU decomp. failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_thlm, dt, wp2, thlp2, wm_zt, & ! Intent(in)
- thlm_forcing, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- thl_tol**2, thl_tol, rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,1), & ! Intent(in)
- thlm, thl_tol_mfl, wpthlp, & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "thlm monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- ! Solve sclrm / wpsclrp
- ! If sclr_dim is 0, then this loop will execute 0 times.
-! ---> h1g, 2010-06-15
-! scalar transport, e.g, droplet and ice number concentration
-! are handled in " advance_sclrm_Nd_module.F90 "
-#ifdef GFDL
- do i = 1, 0, 1
-#else
- do i = 1, sclr_dim, 1
-#endif
-! <--- h1g, 2010-06-15
-
- ! Compute the upper and lower limits of w'sclr' at every level,
- ! based on the correlation of w and sclr, such that:
- ! corr_(w,sclr) = w'sclr' / [ sqrt(w'^2) * sqrt(sclr'^2) ];
- ! -1 <= corr_(w,sclr) <= 1.
- if ( l_clip_semi_implicit ) then
- wpxp_upper_lim(:) = max_mag_correlation * sqrt( wp2(:) * sclrp2(:,i) )
- wpxp_lower_lim(:) = -wpxp_upper_lim(:)
- endif
-
- ! Compute the implicit portion of the sclr and w'sclr' equations.
- ! Build the left-hand side matrix.
- call xm_wpxp_lhs( l_iter, dt, wpsclrp(:,i), a1, a1_zt, wm_zm, wm_zt, & ! Intent(in)
- wp2, wp3_on_wp2, wp3_on_wp2_zt, & ! Intent(in)
- Kw6, tau_zm, C7_Skw_fnc, & ! Intent(in)
- C6rt_Skw_fnc, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, l_implemented, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Compute the explicit portion of the sclrm and w'sclr' equations.
- ! Build the right-hand side vector.
- call xm_wpxp_rhs( xm_wpxp_scalar, l_iter, dt, sclrm(:,i), wpsclrp(:,i), & ! Intent(in)
- sclrm_forcing(:,i), dummy_1d, C7_Skw_fnc, & ! Intent(in)
- sclrpthvp(:,i), C6rt_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,1) ) ! Intent(out)
-
- ! Solve for sclrm / w'sclr'
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "Passive scalar # ", i, " LU decomp. failed."
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_scalar, dt, wp2, sclrp2(:,i), & ! Intent(in)
- wm_zt, sclrm_forcing(:,i), & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- sclr_tol(i)**2, sclr_tol(i), rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,1), & ! Intent(in)
- sclrm(:,i), sclr_tol(i), wpsclrp(:,i), & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "sclrm # ", i, "monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- enddo ! passive scalars
-
- else ! Simple case, where l_clip_semi_implicit is false
-
- ! Create the lhs once
- call xm_wpxp_lhs( l_iter, dt, dummy_1d, a1, a1_zt, wm_zm, wm_zt, & ! Intent(in)
- wp2, wp3_on_wp2, wp3_on_wp2_zt, & ! Intent(in)
- Kw6, tau_zm, C7_Skw_fnc, & ! Intent(in)
- C6rt_Skw_fnc, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- dummy_1d, dummy_1d, l_implemented, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Compute the explicit portion of the r_t and w'r_t' equations.
- ! Build the right-hand side vector.
- call xm_wpxp_rhs( xm_wpxp_rtm, l_iter, dt, rtm, wprtp, & ! Intent(in)
- rtm_forcing, wprtp_forcing, C7_Skw_fnc, & ! Intent(in)
- rtpthvp, C6rt_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,1) ) ! Intent(out)
-
- ! Compute the explicit portion of the th_l and w'th_l' equations.
- ! Build the right-hand side vector.
- call xm_wpxp_rhs( xm_wpxp_thlm, l_iter, dt, thlm, wpthlp, & ! Intent(in)
- thlm_forcing, wpthlp_forcing, C7_Skw_fnc, & ! Intent(in)
- thlpthvp, C6thl_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,2) ) ! Intent(out)
-
-! ---> h1g, 2010-06-15
-! scalar transport, e.g, droplet and ice number concentration
-! are handled in " advance_sclrm_Nd_module.F90 "
-#ifdef GFDL
- do i = 1, 0, 1
-#else
- do i = 1, sclr_dim, 1
-#endif
-! <--- h1g, 2010-06-15
-
- call xm_wpxp_rhs( xm_wpxp_scalar, l_iter, dt, sclrm(:,i), wpsclrp(:,i), & ! Intent(in)
- sclrm_forcing(:,i), dummy_1d, C7_Skw_fnc, & ! Intent(in)
- sclrpthvp(:,i), C6rt_Skw_fnc, tau_zm, a1, a1_zt, & ! Intent(in)
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, & ! Intent(in)
- wpxp_upper_lim, wpxp_lower_lim, & ! Intent(in)
- rhs(:,2+i) ) ! Intent(out)
-
- enddo
-
- ! Solve for all fields
- if ( l_stats_samp .and. ithlm_matrix_condt_num + irtm_matrix_condt_num > 0 ) then
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp, rcond ) ! Intent(out)
- else
- call xm_wpxp_solve( nrhs, & ! Intent(in)
- lhs, rhs, & ! Intent(inout)
- solution, err_code_xm_wpxp ) ! Intent(out)
- endif
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "xm_wpxp matrix LU decomp. failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_rtm, dt, wp2, rtp2, wm_zt, & ! Intent(in)
- rtm_forcing, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- rt_tol**2, rt_tol, rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,1), & ! Intent(in)
- rtm, rt_tol_mfl, wprtp, & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "rtm monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_thlm, dt, wp2, thlp2, wm_zt, & ! Intent(in)
- thlm_forcing, rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- thl_tol**2, thl_tol, rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,2), & ! Intent(in)
- thlm, thl_tol_mfl, wpthlp, & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,'(a)') "thlm monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
-! ---> h1g, 2010-06-15
-! scalar transport, e.g, droplet and ice number concentration
-! are handled in " advance_sclrm_Nd_module.F90 "
-#ifdef GFDL
- do i = 1, 0, 1
-#else
- do i = 1, sclr_dim, 1
-#endif
-! <--- h1g, 2010-06-15
-
- call xm_wpxp_clipping_and_stats &
- ( xm_wpxp_scalar, dt, wp2, sclrp2(:,i), & ! Intent(in)
- wm_zt, sclrm_forcing(:,i), & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- sclr_tol(i)**2, sclr_tol(i), rcond, & ! Intent(in)
- low_lev_effect, high_lev_effect, & ! Intent(in)
- l_implemented, solution(:,2+i), & ! Intent(in)
- sclrm(:,i), sclr_tol(i), wpsclrp(:,i), & ! Intent(inout)
- err_code_xm_wpxp ) ! Intent(out)
-
- if ( fatal_error( err_code_xm_wpxp ) ) then
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "sclrm # ", i, "monotonic flux limiter: tridag failed"
- call reportError( err_code_xm_wpxp )
- end if
-
- ! Overwrite the current error status with the new fatal error
- err_code = err_code_xm_wpxp
-
- end if
-
- end do ! 1..sclr_dim
-
- end if ! l_clip_semi_implicit
-
- ! De-allocate memory
- deallocate( rhs, solution )
-
- ! Error Report
- ! Joshua Fasching Feb 2008
- if ( fatal_error( err_code ) .and. clubb_at_least_debug_level( 1 ) ) then
-
- write(fstderr,*) "Error in advance_xm_wpxp"
-
- write(fstderr,*) "Intent(in)"
-
- write(fstderr,*) "dt = ", dt
- write(fstderr,*) "sigma_sqd_w = ", sigma_sqd_w
- write(fstderr,*) "wm_zm = ", wm_zm
- write(fstderr,*) "wm_zt = ", wm_zt
- write(fstderr,*) "wp2 = ", wp2
- write(fstderr,*) "wp3_on_wp2 = ", wp3_on_wp2
- write(fstderr,*) "wp3_on_wp2_zt = ", wp3_on_wp2_zt
- write(fstderr,*) "Kh_zt = ", Kh_zt
- write(fstderr,*) "tau_zm = ", tau_zm
- write(fstderr,*) "Skw_zm = ", Skw_zm
- write(fstderr,*) "rtpthvp = ", rtpthvp
- write(fstderr,*) "rtm_forcing = ", rtm_forcing
- write(fstderr,*) "wprtp_forcing = ", wprtp_forcing
- write(fstderr,*) "rtm_ref = ", rtm_ref
- write(fstderr,*) "thlpthvp = ", thlpthvp
- write(fstderr,*) "thlm_forcing = ", thlm_forcing
- write(fstderr,*) "wpthlp_forcing = ", wpthlp_forcing
- write(fstderr,*) "thlm_ref = ", thlm_ref
- write(fstderr,*) "rho_ds_zm = ", rho_ds_zm
- write(fstderr,*) "rho_ds_zt = ", rho_ds_zt
- write(fstderr,*) "invrs_rho_ds_zm = ", invrs_rho_ds_zm
- write(fstderr,*) "invrs_rho_ds_zt = ", invrs_rho_ds_zt
- write(fstderr,*) "thv_ds_zm = ", thv_ds_zm
- write(fstderr,*) "rtp2 = ", rtp2
- write(fstderr,*) "thlp2 = ", thlp2
- write(fstderr,*) "w1_zm = ", w1_zm
- write(fstderr,*) "w2_zm = ", w2_zm
- write(fstderr,*) "varnce_w1_zm = ", varnce_w1_zm
- write(fstderr,*) "varnce_w2_zm = ", varnce_w2_zm
- write(fstderr,*) "mixt_frac_zm = ", mixt_frac_zm
- write(fstderr,*) "l_implemented = ", l_implemented
-
- if ( sclr_dim > 0 ) then
- write(fstderr,*) "sclrp2 = ", sclrp2
- write(fstderr,*) "sclrpthvp = ", sclrpthvp
- write(fstderr,*) "sclrm_forcing = ", sclrm_forcing
- end if
-
- write(fstderr,*) "Intent(inout)"
-
- write(fstderr,*) "rtm = ", rtm
- write(fstderr,*) "wprtp = ", wprtp
- write(fstderr,*) "thlm = ", thlm
- write(fstderr,*) "wpthlp =", wpthlp
-
- if ( sclr_dim > 0 ) then
- write(fstderr,*) "sclrm = ", sclrm
- write(fstderr,*) "wpsclrp = ", wpsclrp
- end if
-
- end if ! Fatal error and debug_level >= 1
-
- if ( rtm_sponge_damp_settings%l_sponge_damping ) then
- if( l_stats_samp ) then
- call stat_begin_update( irtm_sdmp, rtm / real( dt, kind = core_rknd ), zt )
- end if
- rtm(1:gr%nz) = sponge_damp_xm( dt, rtm_ref(1:gr%nz), rtm(1:gr%nz), &
- rtm_sponge_damp_profile )
-
- if( l_stats_samp ) then
- call stat_end_update( irtm_sdmp, rtm / real( dt, kind = core_rknd ), zt )
- end if
- endif
-
- if ( thlm_sponge_damp_settings%l_sponge_damping ) then
- if( l_stats_samp ) then
- call stat_begin_update( ithlm_sdmp, thlm / real( dt, kind = core_rknd ), zt )
- end if
- thlm(1:gr%nz) = sponge_damp_xm( dt, thlm_ref(1:gr%nz), thlm(1:gr%nz), &
- thlm_sponge_damp_profile )
- if( l_stats_samp ) then
- call stat_end_update( ithlm_sdmp, thlm / real( dt, kind = core_rknd ), zt )
- end if
- endif
-
- return
-
- end subroutine advance_xm_wpxp
-
- !=============================================================================
- subroutine xm_wpxp_lhs( l_iter, dt, wpxp, a1, a1_zt, wm_zm, wm_zt, &
- wp2, wp3_on_wp2, wp3_on_wp2_zt, &
- Kw6, tau_zm, C7_Skw_fnc, &
- C6x_Skw_fnc, rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_rho_ds_zt, &
- wpxp_upper_lim, wpxp_lower_lim, l_implemented, &
- lhs )
-
- ! Description:
- ! Compute LHS band diagonal matrix for xm and w'x'.
- ! This subroutine computes the implicit portion of
- ! the xm and w'x' equations.
-
- ! References:
- ! None
- !------------------------------------------------------------------------
-
- use crmx_parameters_tunable, only: &
- nu6_vert_res_dep ! Variable(s)
-
- use crmx_grid_class, only: &
- gr, & ! Variable(s)
- zm2zt ! Procedure(s)
-
- use crmx_constants_clubb, only: &
- gamma_over_implicit_ts, & ! Constant(s)
- one, &
- zero
-
- use crmx_model_flags, only: &
- l_clip_semi_implicit, & ! Variable(s)
- l_upwind_wpxp_ta
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_diffusion, only: &
- diffusion_zm_lhs ! Procedure(s)
-
- use crmx_mean_adv, only: &
- term_ma_zt_lhs, & ! Procedure(s)
- term_ma_zm_lhs
-
- use crmx_clip_semi_implicit, only: &
- clip_semi_imp_lhs ! Procedure(s)
-
- use crmx_stats_variables, only: &
- ztscr01, & ! Variable(s)
- ztscr02, &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr10, &
- zmscr11, &
- zmscr12, &
- zmscr13, &
- zmscr14, &
- zmscr15
-
- use crmx_stats_variables, only: &
- l_stats_samp, &
- ithlm_ma, &
- ithlm_ta, &
- irtm_ma, &
- irtm_ta, &
- iwpthlp_ma, &
- iwpthlp_ta, &
- iwpthlp_tp, &
- iwpthlp_ac, &
- iwpthlp_pr1, &
- iwpthlp_pr2, &
- iwpthlp_dp1, &
- iwpthlp_sicl, &
- iwprtp_ma, &
- iwprtp_ta, &
- iwprtp_tp, &
- iwprtp_ac, &
- iwprtp_pr1, &
- iwprtp_pr2, &
- iwprtp_dp1, &
- iwprtp_sicl
-
- use crmx_advance_helper_module, only: set_boundary_conditions_lhs ! Procedure(s)
-
-
- implicit none
-
- ! External
- intrinsic :: min, max
-
- ! Constant parameters
- ! Left-hand side matrix diagonal identifiers for
- ! momentum-level variable, w'x'.
- integer, parameter :: &
- m_kp1_mdiag = 1, & ! Momentum superdiagonal index for w'x'.
- m_kp1_tdiag = 2, & ! Thermodynamic superdiagonal index for w'x'.
- m_k_mdiag = 3, & ! Momentum main diagonal index for w'x'.
- m_k_tdiag = 4, & ! Thermodynamic subdiagonal index for w'x'.
- m_km1_mdiag = 5 ! Momentum subdiagonal index for w'x'.
-
- ! Left-hand side matrix diagonal identifiers for
- ! thermodynamic-level variable, xm.
- integer, parameter :: &
- t_kp1_tdiag = 1, & ! Thermodynamic superdiagonal index for xm.
- t_k_mdiag = 2, & ! Momentum superdiagonal index for xm.
- t_k_tdiag = 3, & ! Thermodynamic main diagonal index for xm.
- t_km1_mdiag = 4, & ! Momentum subdiagonal index for xm.
- t_km1_tdiag = 5 ! Thermodynamic subdiagonal index for xm.
-
- ! Input variables
- logical, intent(in) :: l_iter
-
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- wpxp, & ! w'x' (momentum levels) at timestep (t) [{xm units} m/s]
- a1, & ! a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- wm_zm, & ! w wind component on momentum levels [m/s]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp3_on_wp2, & ! Smoothed wp3 / wp2 on momentum levels [m/s]
- wp3_on_wp2_zt, & ! Smoothed wp3 / wp2 on thermo. levels [m/s]
- Kw6, & ! Coefficient of eddy diffusivity for w'x' [m^2/s]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- C7_Skw_fnc, & ! C_7 parameter with Sk_w applied [-]
- C6x_Skw_fnc, & ! C_6x parameter with Sk_w applied [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ moment. levs. [m^3/kg]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- wpxp_upper_lim, & ! Keeps correlations from becoming > 1. [units vary]
- wpxp_lower_lim ! Keeps correlations from becoming < -1. [units vary]
-
- logical, intent(in) :: &
- l_implemented ! Flag for CLUBB being implemented in a larger model.
-
- ! Output Variable
- real( kind = core_rknd ), intent(out), dimension(nsup+nsub+1,2*gr%nz) :: &
- lhs ! Implicit contributions to wpxp/xm (band diag. matrix) (LAPACK)
-
- ! Local Variables
-
- ! Indices
- integer :: k, kp1, km1
- integer :: k_xm, k_wpxp
- integer :: k_wpxp_low, k_wpxp_high
-
- real( kind = core_rknd ), dimension(3) :: tmp
-
- logical :: l_upper_thresh, l_lower_thresh ! flags for clip_semi_imp_lhs
-
-
- ! Initialize the left-hand side matrix to 0.
- lhs = zero
-
- ! The xm loop runs between k = 2 and k = gr%nz. The value of xm at
- ! level k = 1, which is below the model surface, is simply set equal to the
- ! value of xm at level k = 2 after the solve has been completed.
-
- do k = 2, gr%nz, 1
-
- ! Define indices
-
- km1 = max( k-1, 1 )
-
- k_xm = 2*k - 1
- ! k_wpxp is 2*k
-
-
- !!!!!***** xm *****!!!!!
-
- ! xm: Left-hand side (implicit xm portion of the code).
- !
- ! Thermodynamic subdiagonal (lhs index: t_km1_tdiag)
- ! [ x xm(k-1,) ]
- ! Momentum subdiagonal (lhs index: t_km1_mdiag)
- ! [ x wpxp(k-1,) ]
- ! Thermodynamic main diagonal (lhs index: t_k_tdiag)
- ! [ x xm(k,) ]
- ! Momentum superdiagonal (lhs index: t_k_mdiag)
- ! [ x wpxp(k,) ]
- ! Thermodynamic superdiagonal (lhs index: t_kp1_tdiag)
- ! [ x xm(k+1,) ]
-
- ! LHS mean advection (ma) term.
- if ( .not. l_implemented ) then
-
- lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_xm) &
- = lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_xm) &
- + term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
-
- else
-
- lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_xm) &
- = lhs((/t_kp1_tdiag,t_k_tdiag,t_km1_tdiag/),k_xm) + zero
-
- endif
-
- ! LHS turbulent advection (ta) term.
- lhs((/t_k_mdiag,t_km1_mdiag/),k_xm) &
- = lhs((/t_k_mdiag,t_km1_mdiag/),k_xm) &
- + xm_term_ta_lhs( rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), gr%invrs_dzt(k) )
-
- ! LHS time tendency.
- lhs(t_k_tdiag,k_xm) &
- = lhs(t_k_tdiag,k_xm) + one / real( dt, kind = core_rknd )
-
- if (l_stats_samp) then
-
- ! Statistics: implicit contributions for rtm or thlm.
-
- if ( irtm_ma > 0 .or. ithlm_ma > 0 ) then
- if ( .not. l_implemented ) then
- tmp(1:3) = &
- + term_ma_zt_lhs( wm_zt(k), gr%invrs_dzt(k), k, gr%invrs_dzm(k), gr%invrs_dzm(km1) )
- ztscr01(k) = - tmp(3)
- ztscr02(k) = - tmp(2)
- ztscr03(k) = - tmp(1)
- else
- ztscr01(k) = zero
- ztscr02(k) = zero
- ztscr03(k) = zero
- endif
- endif
-
- if ( irtm_ta > 0 .or. ithlm_ta > 0 ) then
- tmp(1:2) = &
- + xm_term_ta_lhs( rho_ds_zm(k), rho_ds_zm(km1), &
- invrs_rho_ds_zt(k), gr%invrs_dzt(k) )
- ztscr04(k) = - tmp(2)
- ztscr05(k) = - tmp(1)
- endif
-
- endif
-
- enddo ! xm loop: 2..gr%nz
-
-
- ! The wpxp loop runs between k = 2 and k = gr%nz-1. The value of wpxp
- ! is set to specified values at both the lowest level, k = 1, and the
- ! highest level, k = gr%nz.
-
- do k = 2, gr%nz-1, 1
-
- ! Define indices
-
- kp1 = min( k+1, gr%nz )
- km1 = max( k-1, 1 )
-
- ! k_xm is 2*k - 1
- k_wpxp = 2*k
-
-
- !!!!!***** w'x' *****!!!!!
-
- ! w'x': Left-hand side (implicit w'x' portion of the code).
- !
- ! Momentum subdiagonal (lhs index: m_km1_mdiag)
- ! [ x wpxp(k-1,) ]
- ! Thermodynamic subdiagonal (lhs index: m_k_tdiag)
- ! [ x xm(k,) ]
- ! Momentum main diagonal (lhs index: m_k_mdiag)
- ! [ x wpxp(k,) ]
- ! Thermodynamic superdiagonal (lhs index: m_kp1_tdiag)
- ! [ x xm(k+1,) ]
- ! Momentum superdiagonal (lhs index: m_kp1_mdiag)
- ! [ x wpxp(k+1,) ]
-
- ! LHS mean advection (ma) term.
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
-
- ! LHS turbulent advection (ta) term.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! The weight of the implicit portion of this term is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (w'x' in this case), y(t) is the linearized portion of
- ! the term that gets treated implicitly, and RHS is the portion of
- ! the term that is always treated explicitly (in the case of the
- ! w'x' turbulent advection term, RHS = 0). A weight of greater
- ! than 1 can be applied to make the term more numerically stable.
- if ( .not. l_upwind_wpxp_ta ) then
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- + gamma_over_implicit_ts &
- * wpxp_term_ta_lhs( a1_zt(kp1), a1_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), &
- gr%invrs_dzm(k), k )
- else
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- + gamma_over_implicit_ts &
- * wpxp_term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1) )
- end if
-
- ! LHS turbulent production (tp) term.
- lhs((/m_kp1_tdiag,m_k_tdiag/),k_wpxp) &
- = lhs((/m_kp1_tdiag,m_k_tdiag/),k_wpxp) &
- + wpxp_term_tp_lhs( wp2(k), gr%invrs_dzm(k) )
-
- ! LHS accumulation (ac) term and pressure term 2 (pr2).
- lhs(m_k_mdiag,k_wpxp) &
- = lhs(m_k_mdiag,k_wpxp) &
- + wpxp_terms_ac_pr2_lhs( C7_Skw_fnc(k), &
- wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
-
- ! LHS pressure term 1 (pr1).
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs(m_k_mdiag,k_wpxp) &
- = lhs(m_k_mdiag,k_wpxp) &
- + gamma_over_implicit_ts &
- * wpxp_term_pr1_lhs( C6x_Skw_fnc(k), tau_zm(k) )
-
- ! LHS eddy diffusion term: dissipation term 1 (dp1).
- lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- = lhs((/m_kp1_mdiag,m_k_mdiag,m_km1_mdiag/),k_wpxp) &
- + diffusion_zm_lhs( Kw6(k), Kw6(kp1), nu6_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
-
- ! LHS time tendency.
- if ( l_iter ) then
- lhs(m_k_mdiag,k_wpxp) &
- = lhs(m_k_mdiag,k_wpxp) + one / real(dt, kind = core_rknd)
- endif
-
- ! LHS portion of semi-implicit clipping term.
- if ( l_clip_semi_implicit ) then
- l_upper_thresh = .true.
- l_lower_thresh = .true.
-
- lhs(m_k_mdiag,k_wpxp) &
- = lhs(m_k_mdiag,k_wpxp) &
- + clip_semi_imp_lhs( dt, wpxp(k), &
- l_upper_thresh, wpxp_upper_lim(k), &
- l_lower_thresh, wpxp_lower_lim(k) )
-
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for wprtp or wpthlp.
-
- if ( iwprtp_ma > 0 .or. iwpthlp_ma > 0 ) then
- tmp(1:3) = &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
- zmscr01(k) = - tmp(3)
- zmscr02(k) = - tmp(2)
- zmscr03(k) = - tmp(1)
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) term).
- if ( iwprtp_ta > 0 .or. iwpthlp_ta > 0 ) then
- if ( .not. l_upwind_wpxp_ta ) then
- tmp(1:3) &
- = gamma_over_implicit_ts &
- * wpxp_term_ta_lhs( a1_zt(kp1), a1_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), &
- gr%invrs_dzm(k), k )
- else
- tmp(1:3) &
- = gamma_over_implicit_ts &
- * wpxp_term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1) )
- end if
-
- zmscr04(k) = - tmp(3)
- zmscr05(k) = - tmp(2)
- zmscr06(k) = - tmp(1)
- endif
-
- if ( iwprtp_tp > 0 .or. iwpthlp_tp > 0 ) then
- tmp(1:2) = &
- + wpxp_term_tp_lhs( wp2(k), gr%invrs_dzm(k) )
- zmscr07(k) = - tmp(2)
- zmscr08(k) = - tmp(1)
- endif
-
- ! Note: To find the contribution of w'x' term ac, substitute 0 for the
- ! C_7 skewness function input to function wpxp_terms_ac_pr2_lhs.
- if ( iwprtp_ac > 0 .or. iwpthlp_ac > 0 ) then
- zmscr09(k) = &
- - wpxp_terms_ac_pr2_lhs( zero, &
- wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) term).
- if ( iwprtp_pr1 > 0 .or. iwpthlp_pr1 > 0 ) then
- zmscr10(k) &
- = - gamma_over_implicit_ts &
- * wpxp_term_pr1_lhs( C6x_Skw_fnc(k), tau_zm(k) )
- endif
-
- ! Note: To find the contribution of w'x' term pr2, add 1 to the
- ! C_7 skewness function input to function wpxp_terms_ac_pr2_lhs.
- if ( iwprtp_pr2 > 0 .or. iwpthlp_pr2 > 0 ) then
- zmscr11(k) = &
- - wpxp_terms_ac_pr2_lhs( (one+C7_Skw_fnc(k)), &
- wm_zt(kp1), wm_zt(k), gr%invrs_dzm(k) )
- endif
-
- if ( iwprtp_dp1 > 0 .or. iwpthlp_dp1 > 0 ) then
- tmp(1:3) = &
- + diffusion_zm_lhs( Kw6(k), Kw6(kp1), nu6_vert_res_dep, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- zmscr12(k) = - tmp(3)
- zmscr13(k) = - tmp(2)
- zmscr14(k) = - tmp(1)
- endif
-
- if ( l_clip_semi_implicit ) then
- if ( iwprtp_sicl > 0 .or. iwpthlp_sicl > 0 ) then
- l_upper_thresh = .true.
- l_lower_thresh = .true.
- zmscr15(k) = &
- - clip_semi_imp_lhs( dt, wpxp(k), &
- l_upper_thresh, wpxp_upper_lim(k), &
- l_lower_thresh, wpxp_lower_lim(k) )
- endif
- endif
-
- endif
-
- enddo ! wpxp loop: 2..gr%nz-1
-
-
- ! Boundary conditions
-
- ! The turbulent flux (wpxp) use fixed-point boundary conditions at both the
- ! upper and lower boundaries. Therefore, anything set in the wpxp loop
- ! at both the upper and lower boundaries would be overwritten here.
- ! However, the wpxp loop does not extend to the boundary levels. An array
- ! with a value of 1 at the main diagonal on the left-hand side and with
- ! values of 0 at all other diagonals on the left-hand side will preserve the
- ! right-hand side value at that level. The value of xm at level k = 1,
- ! which is below the model surface, is preserved and then overwritten to
- ! match the new value of xm at level k = 2.
- !
- ! xm(1) wpxp(1) ... wpxp(nzmax)
- ! [ 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 ]
- ! [ 1.0 1.0 ... 1.0 ]
- ! [ 0.0 0.0 0.0 ]
- ! [ 0.0 0.0 0.0 ]
-
- ! Lower boundary
- k = 1
- k_xm = 2*k - 1
- k_wpxp_low = 2*k
-
- ! Upper boundary
- k = gr%nz
- !k_xm is 2*k - 1
- k_wpxp_high = 2*k
-
- call set_boundary_conditions_lhs( m_k_mdiag, k_wpxp_low, k_wpxp_high, lhs, &
- t_k_tdiag, k_xm)
-
- return
-
- end subroutine xm_wpxp_lhs
-
- !=============================================================================
- subroutine xm_wpxp_rhs( solve_type, l_iter, dt, xm, wpxp, &
- xm_forcing, wpxp_forcing, C7_Skw_fnc, &
- xpthvp, C6x_Skw_fnc, tau_zm, a1, a1_zt, &
- wp3_on_wp2, wp3_on_wp2_zt, rho_ds_zt, &
- rho_ds_zm, invrs_rho_ds_zm, thv_ds_zm, &
- wpxp_upper_lim, wpxp_lower_lim, &
- rhs )
-
- ! Description:
- ! Compute RHS vector for xm and w'x'.
- ! This subroutine computes the explicit portion of
- ! the xm and w'x' equations.
-
- ! References:
- !------------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- gamma_over_implicit_ts, & ! Constant(s)
- one, &
- zero
-
- use crmx_model_flags, only: &
- l_clip_semi_implicit, & ! Variable(s)
- l_upwind_wpxp_ta
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_clip_semi_implicit, only: &
- clip_semi_imp_rhs ! Procedure(s)
-
- use crmx_stats_type, only: &
- stat_update_var_pt, &
- stat_begin_update_pt
-
- use crmx_stats_variables, only: &
- zt, & ! Variable(s)
- zm, &
- irtm_forcing, &
- ithlm_forcing, &
- iwprtp_bp, &
- iwprtp_pr3, &
- iwprtp_sicl, &
- iwprtp_ta, &
- iwprtp_pr1, &
- iwprtp_forcing, &
- iwpthlp_bp, &
- iwpthlp_pr3, &
- iwpthlp_sicl, &
- iwpthlp_ta, &
- iwpthlp_pr1, &
- iwpthlp_forcing, &
- l_stats_samp
-
- use crmx_advance_helper_module, only: set_boundary_conditions_rhs
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Variables being solved for.
-
- logical, intent(in) :: l_iter
-
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- xm, & ! xm (thermodynamic levels) [{xm units}]
- wpxp, & ! (momentum levels) [{xm units} m/s]
- xm_forcing, & ! xm forcings (thermodynamic levels) [{xm units}/s]
- wpxp_forcing, & ! forcing (momentum levels) [{xm units} m/s^2]
- C7_Skw_fnc, & ! C_7 parameter with Sk_w applied [-]
- xpthvp, & ! x'th_v' (momentum levels) [{xm units} K]
- C6x_Skw_fnc, & ! C_6x parameter with Sk_w applied [-]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- a1, & ! a_1 [-]
- wp3_on_wp2, & ! Smoothed wp3 / wp2 on moment. levels [m/s]
- wp3_on_wp2_zt, & ! Smoothed wp3 / wp2 on thermo. levels [m/s]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- rho_ds_zm, & ! Dry, static density on moment. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ moment. levs. [m^3/kg]
- thv_ds_zm, & ! Dry, base-state theta_v on momentum levs. [K]
- wpxp_upper_lim, & ! Keeps correlations from becoming > 1. [units vary]
- wpxp_lower_lim ! Keeps correlations from becoming < -1. [units vary]
-
- ! Output Variable
- real( kind = core_rknd ), intent(out), dimension(2*gr%nz) :: &
- rhs ! Right-hand side of band diag. matrix. (LAPACK)
-
- ! Local Variables.
-
- ! For "over-implicit" weighted time step.
- ! This vector holds output from the LHS (implicit) portion of a term at a
- ! given vertical level. This output is weighted and applied to the RHS.
- ! This is used if the implicit portion of the term is "over-implicit", which
- ! means that the LHS contribution is given extra weight (>1) in order to
- ! increase numerical stability. A weighted factor must then be applied to
- ! the RHS in order to balance the weight.
- real( kind = core_rknd ), dimension(3) :: lhs_fnc_output
-
- ! Indices
- integer :: k, km1, kp1, k_xm, k_wpxp, k_xm_low, k_wpxp_low, k_wpxp_high
-
-
- integer :: &
- ixm_f, &
- iwpxp_bp, &
- iwpxp_pr3, &
- iwpxp_f, &
- iwpxp_sicl, &
- iwpxp_ta, &
- iwpxp_pr1
-
- logical :: l_upper_thresh, l_lower_thresh ! flags for clip_semi_imp_lhs
-
- ! ---- Begin Code ----
-
- select case ( solve_type )
- case ( xm_wpxp_rtm ) ! rtm/wprtp budget terms
- ixm_f = irtm_forcing
- iwpxp_bp = iwprtp_bp
- iwpxp_pr3 = iwprtp_pr3
- iwpxp_f = iwprtp_forcing
- iwpxp_sicl = iwprtp_sicl
- iwpxp_ta = iwprtp_ta
- iwpxp_pr1 = iwprtp_pr1
- case ( xm_wpxp_thlm ) ! thlm/wpthlp budget terms
- ixm_f = ithlm_forcing
- iwpxp_bp = iwpthlp_bp
- iwpxp_pr3 = iwpthlp_pr3
- iwpxp_f = iwpthlp_forcing
- iwpxp_sicl = iwpthlp_sicl
- iwpxp_ta = iwpthlp_ta
- iwpxp_pr1 = iwpthlp_pr1
- case default ! this includes the sclrm case
- ixm_f = 0
- iwpxp_bp = 0
- iwpxp_pr3 = 0
- iwpxp_f = 0
- iwpxp_sicl = 0
- iwpxp_ta = 0
- iwpxp_pr1 = 0
- end select
-
-
- ! Initialize the right-hand side vector to 0.
- rhs = zero
-
- ! The xm loop runs between k = 2 and k = gr%nz. The value of xm at
- ! level k = 1, which is below the model surface, is simply set equal to the
- ! value of xm at level k = 2 after the solve has been completed.
-
- do k = 2, gr%nz, 1
-
- ! Define indices
-
- k_xm = 2*k - 1
- ! k_wpxp is 2*k
-
-
- !!!!!***** xm *****!!!!!
-
- ! xm: Right-hand side (explicit xm portion of the code).
-
- ! RHS time tendency.
- rhs(k_xm) = rhs(k_xm) + xm(k) / real( dt, kind = core_rknd )
-
- ! RHS xm forcings.
- ! Note: xm forcings include the effects of microphysics,
- ! cloud water sedimentation, radiation, and any
- ! imposed forcings on xm.
- rhs(k_xm) = rhs(k_xm) + xm_forcing(k)
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for xm
- ! (including microphysics/radiation).
-
- ! xm forcings term is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( ixm_f, k, xm_forcing(k), zt )
-
- endif ! l_stats_samp
-
- enddo ! xm loop: 2..gr%nz
-
-
- ! The wpxp loop runs between k = 2 and k = gr%nz-1. The value of wpxp
- ! is set to specified values at both the lowest level, k = 1, and the
- ! highest level, k = gr%nz.
-
- do k = 2, gr%nz-1, 1
-
- ! Define indices
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! k_xm is 2*k - 1
- k_wpxp = 2*k
-
-
- !!!!!***** w'x' *****!!!!!
-
- ! w'x': Right-hand side (explicit w'x' portion of the code).
-
- ! RHS buoyancy production (bp) term and pressure term 3 (pr3).
- rhs(k_wpxp) &
- = rhs(k_wpxp) &
- + wpxp_terms_bp_pr3_rhs( C7_Skw_fnc(k), thv_ds_zm(k), xpthvp(k) )
-
- ! RHS time tendency.
- if ( l_iter ) then
- rhs(k_wpxp) = rhs(k_wpxp) + wpxp(k) / real( dt, kind = core_rknd )
- end if
-
- ! RHS forcing.
- ! Note: forcing includes the effects of microphysics on .
- rhs(k_wpxp) = rhs(k_wpxp) + wpxp_forcing(k)
-
- ! RHS portion of semi-implicit clipping (sicl) term.
- if ( l_clip_semi_implicit ) then
- l_upper_thresh = .true.
- l_lower_thresh = .true.
-
- rhs(k_wpxp) &
- = rhs(k_wpxp) &
- + clip_semi_imp_rhs( dt, wpxp(k), &
- l_upper_thresh, wpxp_upper_lim(k), &
- l_lower_thresh, wpxp_lower_lim(k) )
-
- endif
-
- if( .not. l_upwind_wpxp_ta ) then ! Only do this when not using Upwind Differencing
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS turbulent advection (ta) term.
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! The weight of the implicit portion of this term is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (w'x' in this case), y(t) is the linearized portion of
- ! the term that gets treated implicitly, and RHS is the portion of
- ! the term that is always treated explicitly (in the case of the
- ! w'x' turbulent advection term, RHS = 0). A weight of greater
- ! than 1 can be applied to make the term more numerically stable.
- lhs_fnc_output(1:3) &
- = wpxp_term_ta_lhs( a1_zt(kp1), a1_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), &
- gr%invrs_dzm(k), k )
- else
- lhs_fnc_output(1:3) &
- = wpxp_term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1) )
- endif
-
- rhs(k_wpxp) &
- = rhs(k_wpxp) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wpxp(kp1) &
- - lhs_fnc_output(2) * wpxp(k) &
- - lhs_fnc_output(3) * wpxp(km1) )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS pressure term 1 (pr1).
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs_fnc_output(1) &
- = wpxp_term_pr1_lhs( C6x_Skw_fnc(k), tau_zm(k) )
- rhs(k_wpxp) &
- = rhs(k_wpxp) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wpxp(k) )
-
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for wpxp.
-
- ! w'x' term bp is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of w'x' term bp, substitute 0 for the
- ! C_7 skewness function input to function wpxp_terms_bp_pr3_rhs.
- call stat_update_var_pt( iwpxp_bp, k, &
- wpxp_terms_bp_pr3_rhs( zero, thv_ds_zm(k), xpthvp(k) ), zm )
-
- ! w'x' term pr3 is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of w'x' term pr3, add 1 to the
- ! C_7 skewness function input to function wpxp_terms_bp_pr2_rhs.
- call stat_update_var_pt( iwpxp_pr3, k, &
- wpxp_terms_bp_pr3_rhs( (one+C7_Skw_fnc(k)), thv_ds_zm(k), &
- xpthvp(k) ), &
- zm )
-
- ! w'x' forcing term is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_f, k, wpxp_forcing(k), zm )
-
- ! w'x' term sicl has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on clip_semi_imp_rhs.
- if ( l_clip_semi_implicit ) then
- l_upper_thresh = .true.
- l_lower_thresh = .true.
- call stat_begin_update_pt( iwpxp_sicl, k, &
- -clip_semi_imp_rhs( dt, wpxp(k), &
- l_upper_thresh, wpxp_upper_lim(k), &
- l_lower_thresh, wpxp_lower_lim(k) ), zm )
- endif
-
- if ( l_upwind_wpxp_ta ) then ! Use upwind differencing
- lhs_fnc_output(1:3) &
- = wpxp_term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1) )
-
- else
- ! w'x' term ta is normally completely implicit. However, there is a
- ! RHS contribution from the "over-implicit" weighted time step. A
- ! weighting factor of greater than 1 may be used to make the term more
- ! numerically stable (see note above for RHS contribution from
- ! "over-implicit" weighted time step for LHS turbulent advection (ta)
- ! term). Therefore, w'x' term ta has both implicit and explicit
- ! components; call stat_begin_update_pt. Since stat_begin_update_pt
- ! automatically subtracts the value sent in, reverse the sign on the
- ! input value.
- lhs_fnc_output(1:3) &
- = wpxp_term_ta_lhs( a1_zt(kp1), a1_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), &
- invrs_rho_ds_zm(k), &
- gr%invrs_dzm(k), k )
- endif
-
- call stat_begin_update_pt( iwpxp_ta, k, &
- - ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wpxp(kp1) &
- - lhs_fnc_output(2) * wpxp(k) &
- - lhs_fnc_output(3) * wpxp(km1) ), zm )
-
- ! w'x' term pr1 is normally completely implicit. However, there is a
- ! RHS contribution from the "over-implicit" weighted time step. A
- ! weighting factor of greater than 1 may be used to make the term more
- ! numerically stable (see note above for RHS contribution from
- ! "over-implicit" weighted time step for LHS turbulent advection (ta)
- ! term). Therefore, w'x' term pr1 has both implicit and explicit
- ! components; call stat_begin_update_pt. Since stat_begin_update_pt
- ! automatically subtracts the value sent in, reverse the sign on the
- ! input value.
- lhs_fnc_output(1) &
- = wpxp_term_pr1_lhs( C6x_Skw_fnc(k), tau_zm(k) )
- call stat_begin_update_pt( iwpxp_pr1, k, &
- - ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * wpxp(k) ), zm )
-
-
- endif ! l_stats_samp
-
- enddo ! wpxp loop: 2..gr%nz-1
-
-
- ! Boundary conditions
-
- ! The turbulent flux (wpxp) use fixed-point boundary conditions at both the
- ! upper and lower boundaries. Therefore, anything set in the wpxp loop
- ! at both the upper and lower boundaries would be overwritten here.
- ! However, the wpxp loop does not extend to the boundary levels. An array
- ! with a value of 1 at the main diagonal on the left-hand side and with
- ! values of 0 at all other diagonals on the left-hand side will preserve the
- ! right-hand side value at that level. The value of xm at level k = 1,
- ! which is below the model surface, is preserved and then overwritten to
- ! match the new value of xm at level k = 2.
-
- ! Lower boundary
- k = 1
- k_xm_low = 2*k - 1
- k_wpxp_low = 2*k
-
- ! Upper boundary
- k = gr%nz
- !k_xm is 2*k - 1
- k_wpxp_high = 2*k
-
-
- ! The value of xm at the lower boundary will remain the same.
- ! However, the value of xm at the lower boundary gets overwritten
- ! after the matrix is solved for the next timestep, such
- ! that xm(1) = xm(2).
-
- ! The value of w'x' at the lower boundary will remain the same.
- ! The surface value of w'x' is set elsewhere
- ! (case-specific information).
-
- ! The value of w'x' at the upper boundary will be 0.
- call set_boundary_conditions_rhs( &
- wpxp(1), k_wpxp_low, zero, k_wpxp_high, &
- rhs, &
- xm(1), k_xm_low )
-
-
- end subroutine xm_wpxp_rhs
-
- !=============================================================================
- subroutine xm_wpxp_solve( nrhs, lhs, rhs, solution, err_code, rcond )
-
- ! Description:
- ! Solve for xm / w'x' using the band diagonal solver.
-
- ! References:
- ! None
- !------------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_lapack_wrap, only: &
- band_solve, & ! Procedure(s)
- band_solvex
-
- use crmx_error_code, only: &
- clubb_no_error ! Constant
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- nrhs ! Number of rhs vectors
-
- ! Input/Output Variables
- real( kind = core_rknd ), intent(inout), dimension(nsup+nsub+1,2*gr%nz) :: &
- lhs ! Implicit contributions to wpxp/xm (band diag. matrix in LAPACK storage)
-
- real( kind = core_rknd ), intent(inout), dimension(2*gr%nz,nrhs) :: &
- rhs ! Right-hand side of band diag. matrix. (LAPACK storage)
-
- real( kind = core_rknd ), intent(out), dimension(2*gr%nz,nrhs) :: &
- solution ! Solution to band diagonal system (LAPACK storage)
-
- ! Output Variables
- integer, intent(out) :: err_code
-
- real( kind = core_rknd ), optional, intent(out) :: &
- rcond ! Est. of the reciprocal of the condition #
-
- err_code = clubb_no_error ! Initialize to the value for no errors
-
- if ( present( rcond ) ) then
- ! Perform LU decomp and solve system (LAPACK with diagnostics)
- call band_solvex( "xm_wpxp", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solution, rcond, err_code )
-
-
- else
- ! Perform LU decomp and solve system (LAPACK)
- call band_solve( "xm_wpxp", nsup, nsub, 2*gr%nz, nrhs, &
- lhs, rhs, solution, err_code )
- end if
-
-
- return
- end subroutine xm_wpxp_solve
-
-!===============================================================================
- subroutine xm_wpxp_clipping_and_stats &
- ( solve_type, dt, wp2, xp2, wm_zt, &
- xm_forcing, rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_rho_ds_zt, &
- xp2_threshold, xm_threshold, rcond, &
- low_lev_effect, high_lev_effect, &
- l_implemented, solution, &
- xm, xm_tol, wpxp, err_code )
-
- ! Description:
- ! Clips and computes implicit stats for an artitrary xm and wpxp
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_model_flags, only: &
- l_clip_semi_implicit ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_mono_flux_limiter, only: &
- monotonic_turbulent_flux_limit ! Procedure(s)
-
- use crmx_pos_definite_module, only: &
- pos_definite_adj ! Procedure(s)
-
- use crmx_clip_explicit, only: &
- clip_covar, & ! Procedure(s)
- clip_wprtp, & ! Variable(s)
- clip_wpthlp, &
- clip_wpsclrp
-
- use crmx_model_flags, only: &
- l_pos_def, & ! Logical for whether to apply the positive definite scheme to rtm
- l_hole_fill, & ! Logical for whether to apply the hole filling scheme to thlm/rtm
- l_clip_turb_adv ! Logical for whether to clip xm when wpxp is clipped
-
- use crmx_constants_clubb, only: &
- fstderr, & ! Constant(s)
- one, &
- zero
-
- use crmx_fill_holes, only: &
- fill_holes_driver ! Procedure
-
- use crmx_error_code, only: &
- clubb_at_least_debug_level, & ! Procedure(s)
- clubb_no_error ! Constant
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_update_var_pt, &
- stat_end_update_pt, &
- stat_end_update, &
- stat_update_var, &
- stat_modify
-
- use crmx_stats_variables, only: &
- zt, & ! Variable(s)
- zm, &
- sfc, &
- irtm_ta, &
- irtm_ma, &
- irtm_matrix_condt_num, &
- irtm_pd, &
- irtm_cl, &
- iwprtp_bt, &
- iwprtp_ma, &
- iwprtp_ta, &
- iwprtp_tp, &
- iwprtp_ac, &
- iwprtp_pr1, &
- iwprtp_pr2, &
- iwprtp_dp1, &
- iwprtp_pd, &
- iwprtp_sicl, &
- ithlm_ta
-
- use crmx_stats_variables, only: &
- ithlm_ma, &
- ithlm_cl, &
- ithlm_matrix_condt_num, &
- iwpthlp_bt, &
- iwpthlp_ma, &
- iwpthlp_ta, &
- iwpthlp_tp, &
- iwpthlp_ac, &
- iwpthlp_pr1, &
- iwpthlp_pr2, &
- iwpthlp_dp1, &
- iwpthlp_sicl
-
- use crmx_stats_variables, only: &
- l_stats_samp, &
- ztscr01, &
- ztscr02, &
- ztscr03, &
- ztscr04, &
- ztscr05, &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr10, &
- zmscr11, &
- zmscr12, &
- zmscr13, &
- zmscr14, &
- zmscr15
-
- implicit none
-
- ! Constant Parameters
- logical, parameter :: &
- l_mono_flux_lim = .true., & ! Flag for monotonic turbulent flux limiter
- l_enable_relaxed_clipping = .true., & ! Flag to relax clipping
- l_first_clip_ts = .true., &
- l_last_clip_ts = .false.
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Variables being solved for.
-
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- xp2, & ! x'^2 (momentum levels) [{xm units}^2]
- wm_zt, & ! w wind component on thermodynamic levels [m/s]
- xm_forcing, & ! xm forcings (thermodynamic levels) [units vary]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ moment. levs. [m^3/kg]
- invrs_rho_ds_zt ! Inv. dry, static density @ thermo. levs. [m^3/kg]
-
- real( kind = core_rknd ), intent(in) :: &
- xp2_threshold, & ! Minimum allowable value of x'^2 [units vary]
- xm_threshold, & ! Minimum allowable value of xm [units vary]
- xm_tol, & ! Minimum allowable deviation of xm [units vary]
- rcond ! Reciprocal of the estimated condition number (from computing A^-1)
-
- ! Variables used as part of the monotonic turbulent advection scheme.
- ! Find the lowermost and uppermost grid levels that can have an effect
- ! on the central thermodynamic level during the course of a time step,
- ! due to the effects of turbulent advection only.
- integer, dimension(gr%nz), intent(in) :: &
- low_lev_effect, & ! Index of the lowest level that has an effect.
- high_lev_effect ! Index of the highest level that has an effect.
-
- logical, intent(in) :: &
- l_implemented ! Flag for CLUBB being implemented in a larger model.
-
- real( kind = core_rknd ), intent(in), dimension(2*gr%nz) :: &
- solution ! The value of xm and wpxp [units vary]
-
- ! Input/Output Variables
- real( kind = core_rknd ), intent(inout), dimension(gr%nz) :: &
- xm, & ! The mean x field [units vary]
- wpxp ! The flux of x [units vary m/s]
-
- ! Output Variable
- integer, intent(out) :: &
- err_code ! Returns an error code in the event of a singular matrix
-
- ! Local Variables
- integer :: &
- solve_type_cl ! solve_type used for clipping statistics.
-
- character(len=10) :: &
- solve_type_str ! solve_type as a string for debug output purposes
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- xm_n ! Old value of xm for positive definite scheme [units vary]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wpxp_pd, xm_pd ! Change in xm and wpxp due to the pos. def. scheme
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wpxp_chnge, & ! Net change in w'x' due to clipping [units vary]
- xp2_relaxed ! Value of x'^2 * clip_factor [units vary]
-
- ! Indices
- integer :: &
- k, km1, kp1, &
- k_xm, k_wpxp
-
- integer :: &
- ixm_ta, &
- ixm_ma, &
- ixm_matrix_condt_num, &
- ixm_pd, &
- ixm_cl, &
- iwpxp_bt, &
- iwpxp_ma, &
- iwpxp_ta, &
- iwpxp_tp, &
- iwpxp_ac, &
- iwpxp_pr1, &
- iwpxp_pr2, &
- iwpxp_dp1, &
- iwpxp_pd, &
- iwpxp_sicl
-
- ! ----- Begin code ------
- err_code = clubb_no_error ! Initialize to the value for no errors
-
- select case ( solve_type )
- case ( xm_wpxp_rtm ) ! rtm/wprtp budget terms
- ixm_ta = irtm_ta
- ixm_ma = irtm_ma
- ixm_pd = irtm_pd
- ixm_cl = irtm_cl
- iwpxp_bt = iwprtp_bt
- iwpxp_ma = iwprtp_ma
- iwpxp_ta = iwprtp_ta
- iwpxp_tp = iwprtp_tp
- iwpxp_ac = iwprtp_ac
- iwpxp_pr1 = iwprtp_pr1
- iwpxp_pr2 = iwprtp_pr2
- iwpxp_dp1 = iwprtp_dp1
- iwpxp_pd = iwprtp_pd
- iwpxp_sicl = iwprtp_sicl
-
- ! This is a diagnostic from inverting the matrix, not a budget
- ixm_matrix_condt_num = irtm_matrix_condt_num
- case ( xm_wpxp_thlm ) ! thlm/wpthlp budget terms
- ixm_ta = ithlm_ta
- ixm_ma = ithlm_ma
- ixm_pd = 0
- ixm_cl = ithlm_cl
- iwpxp_bt = iwpthlp_bt
- iwpxp_ma = iwpthlp_ma
- iwpxp_ta = iwpthlp_ta
- iwpxp_tp = iwpthlp_tp
- iwpxp_ac = iwpthlp_ac
- iwpxp_pr1 = iwpthlp_pr1
- iwpxp_pr2 = iwpthlp_pr2
- iwpxp_dp1 = iwpthlp_dp1
- iwpxp_pd = 0
- iwpxp_sicl = iwpthlp_sicl
-
- ! This is a diagnostic from inverting the matrix, not a budget
- ixm_matrix_condt_num = ithlm_matrix_condt_num
-
- case default ! this includes the sclrm case
- ixm_ta = 0
- ixm_ma = 0
- ixm_pd = 0
- ixm_cl = 0
- iwpxp_bt = 0
- iwpxp_ma = 0
- iwpxp_ta = 0
- iwpxp_tp = 0
- iwpxp_ac = 0
- iwpxp_pr1 = 0
- iwpxp_pr2 = 0
- iwpxp_dp1 = 0
- iwpxp_pd = 0
- iwpxp_sicl = 0
-
- ixm_matrix_condt_num = 0
- end select
-
- ! Copy result into output arrays
-
- do k=1, gr%nz, 1
-
- k_xm = 2 * k - 1
- k_wpxp = 2 * k
-
- xm_n(k) = xm(k)
-
- xm(k) = solution(k_xm)
- wpxp(k) = solution(k_wpxp)
-
- end do ! k=1..gr%nz
-
- ! Lower boundary condition on xm
- xm(1) = xm(2)
-
-
- if ( l_stats_samp ) then
-
-
- if ( ixm_matrix_condt_num > 0 ) then
- ! Est. of the condition number of the mean/flux LHS matrix
- call stat_update_var_pt( ixm_matrix_condt_num, 1, one / rcond, sfc )
- end if
-
-
- ! The xm loop runs between k = 2 and k = gr%nz. The value of xm at
- ! level k = 1, which is below the model surface, is simply set equal to
- ! the value of xm at level k = 2 after the solve has been completed.
- ! Thus, the statistical code will run from levels 2 through gr%nz.
-
- do k = 2, gr%nz
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! Finalize implicit contributions for xm
-
- ! xm term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixm_ma, k, &
- ztscr01(k) * xm(km1) &
- + ztscr02(k) * xm(k) &
- + ztscr03(k) * xm(kp1), zt )
-
- ! xm term ta is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixm_ta, k, &
- ztscr04(k) * wpxp(km1) &
- + ztscr05(k) * wpxp(k), zt )
-
- enddo ! xm loop: 2..gr%nz
-
-
- ! The wpxp loop runs between k = 2 and k = gr%nz-1. The value of wpxp
- ! is set to specified values at both the lowest level, k = 1, and the
- ! highest level, k = gr%nz. Thus, the statistical code will run from
- ! levels 2 through gr%nz-1.
-
- do k = 2, gr%nz-1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! Finalize implicit contributions for wpxp
-
- ! w'x' term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_ma, k, &
- zmscr01(k) * wpxp(km1) &
- + zmscr02(k) * wpxp(k) &
- + zmscr03(k) * wpxp(kp1), zm )
-
-! if( .not. l_upwind_wpxp_ta ) then
- ! w'x' term ta is normally completely implicit. However, due to the
- ! RHS contribution from the "over-implicit" weighted time step,
- ! w'x' term ta has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwpxp_ta, k, &
- zmscr04(k) * wpxp(km1) &
- + zmscr05(k) * wpxp(k) &
- + zmscr06(k) * wpxp(kp1), zm )
-! endif
-
- ! w'x' term tp is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_tp, k, &
- zmscr07(k) * xm(k) &
- + zmscr08(k) * xm(kp1), zm )
-
- ! w'x' term ac is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_ac, k, &
- zmscr09(k) * wpxp(k), zm )
-
- ! w'x' term pr1 is normally completely implicit. However, due to the
- ! RHS contribution from the "over-implicit" weighted time step,
- ! w'x' term pr1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( iwpxp_pr1, k, &
- zmscr10(k) * wpxp(k), zm )
-
- ! w'x' term pr2 is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_pr2, k, &
- zmscr11(k) * wpxp(k), zm )
-
- ! w'x' term dp1 is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( iwpxp_dp1, k, &
- zmscr12(k) * wpxp(km1) &
- + zmscr13(k) * wpxp(k) &
- + zmscr14(k) * wpxp(kp1), zm )
-
- ! w'x' term sicl has both implicit and explicit components;
- ! call stat_end_update_pt.
- if ( l_clip_semi_implicit ) then
- call stat_end_update_pt( iwpxp_sicl, k, &
- zmscr15(k) * wpxp(k), zm )
- endif
-
- enddo ! wpxp loop: 2..gr%nz-1
-
-
- endif ! l_stats_samp
-
-
- ! Apply a monotonic turbulent flux limiter to xm/w'x'.
- if ( l_mono_flux_lim ) then
- call monotonic_turbulent_flux_limit( solve_type, dt, xm_n, &
- xp2, wm_zt, xm_forcing, &
- rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, invrs_rho_ds_zt, &
- xp2_threshold, l_implemented, &
- low_lev_effect, high_lev_effect, &
- xm, xm_tol, wpxp, err_code )
- end if ! l_mono_flux_lim
-
- ! Apply a flux limiting positive definite scheme if the solution
- ! for the mean field is negative and we're determining total water
- if ( solve_type == xm_wpxp_rtm .and. l_pos_def .and. any( xm < zero ) ) then
-
- call pos_definite_adj( dt, "zt", xm, wpxp, &
- xm_n, xm_pd, wpxp_pd )
-
- else
- ! For stats purposes
- xm_pd = zero
- wpxp_pd = zero
-
- end if ! l_pos_def and solve_type == "rtm" and rtm less than 0
-
- if ( l_stats_samp ) then
-
- call stat_update_var( iwpxp_pd, wpxp_pd(1:gr%nz), zm )
-
- call stat_update_var( ixm_pd, xm_pd(1:gr%nz), zt )
-
- end if
-
- ! Computed value before clipping
- if ( l_stats_samp ) then
- call stat_begin_update( ixm_cl, xm / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- end if
-
- if ( any( xm < xm_threshold ) .and. l_hole_fill ) then
-
- select case ( solve_type )
- case ( xm_wpxp_rtm )
- solve_type_str = "rtm"
- case ( xm_wpxp_thlm )
- solve_type_str = "thlm"
- case default
- solve_type_str = "scalars"
- end select
-
- if ( clubb_at_least_debug_level( 1 ) ) then
- do k = 1, gr%nz
- if ( xm(k) < zero ) then
- write(fstderr,*) solve_type_str//" < ", xm_threshold, &
- " in advance_xm_wpxp_module at k= ", k
- end if
- end do
- end if
-
- call fill_holes_driver( 2, xm_threshold, "zt", &
- rho_ds_zt, rho_ds_zm, &
- xm )
-
- end if ! any( xm < xm_threshold ) .and. l_hole_fill
-
- if ( l_stats_samp ) then
- call stat_end_update( ixm_cl, xm / real( dt, kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- end if
-
- ! Use solve_type to find solve_type_cl, which is used
- ! in subroutine clip_covar.
- select case ( solve_type )
- case ( xm_wpxp_rtm )
- solve_type_cl = clip_wprtp
- case ( xm_wpxp_thlm )
- solve_type_cl = clip_wpthlp
- case default
- solve_type_cl = clip_wpsclrp
- end select
-
- ! Clipping for w'x'
- ! Clipping w'x' at each vertical level, based on the
- ! correlation of w and x at each vertical level, such that:
- ! corr_(w,x) = w'x' / [ sqrt(w'^2) * sqrt(x'^2) ];
- ! -1 <= corr_(w,x) <= 1.
- ! Since w'^2, x'^2, and w'x' are updated in different places
- ! from each other, clipping for w'x' has to be done three times
- ! (three times each for w'r_t', w'th_l', and w'sclr'). This is
- ! the second instance of w'x' clipping.
-
- ! Compute a slightly larger value of rt'^2 for clipping purposes. This was
- ! added to prevent a situation in which both the variance and flux are small
- ! and the simulation gets "stuck" at the rt_tol^2 value.
- ! See ticket #389 on the CLUBB TRAC for further details.
- ! -dschanen 10 Jan 2011
- if ( l_enable_relaxed_clipping ) then
- if ( solve_type == xm_wpxp_rtm ) then
- xp2_relaxed = max( 1e-7_core_rknd , xp2 )
-
- else if ( solve_type == xm_wpxp_thlm ) then
- xp2_relaxed = max( 0.01_core_rknd, xp2 )
-
- else ! This includes the passive scalars
- xp2_relaxed = max( 1e-7_core_rknd , xp2 )
-
- end if
-
- else ! Don't relax clipping
- xp2_relaxed = xp2
-
- end if
-
- call clip_covar( solve_type_cl, l_first_clip_ts, & ! In
- l_last_clip_ts, dt, wp2, xp2_relaxed, & ! In
- wpxp, wpxp_chnge ) ! In/Out
-
- ! Adjusting xm based on clipping for w'x'.
- if ( any( wpxp_chnge /= zero ) .and. l_clip_turb_adv ) then
- call xm_correction_wpxp_cl( solve_type, dt, wpxp_chnge, gr%invrs_dzt, &
- xm )
- endif
-
- if ( l_stats_samp ) then
-
- ! wpxp time tendency
- call stat_modify( iwpxp_bt, wpxp / real( dt, kind = core_rknd ), zm )
- ! Brian Griffin; July 5, 2008.
-
- endif
-
- return
- end subroutine xm_wpxp_clipping_and_stats
-
- !=============================================================================
- pure function xm_term_ta_lhs( rho_ds_zm, rho_ds_zmm1, &
- invrs_rho_ds_zt, invrs_dzt ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection of xm: implicit portion of the code.
- !
- ! The d(xm)/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'x' )/dz.
- !
- ! This term is solved for completely implicitly, such that:
- !
- ! - (1/rho_ds) * d( rho_ds * w'x'(t+1) )/dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'x' being used is from
- ! the next timestep, which is being advanced to in solving the d(xm)/dt and
- ! d(w'x')/dt equations.
- !
- ! This term is discretized as follows:
- !
- ! While the values of xm are found on the thermodynamic levels, the values
- ! of w'x' are found on the momentum levels. Additionally, the values of
- ! rho_ds_zm are found on the momentum levels, and the values of
- ! invrs_rho_ds_zt are found on the thermodynamic levels. On the momentum
- ! levels, the values of rho_ds_zm are multiplied by the values of w'x'. The
- ! derivative of (rho_ds_zm * w'x') is taken over the intermediate (central)
- ! thermodynamic level, where it is multiplied by invrs_rho_ds_zt, yielding
- ! the desired results.
- !
- ! =====rho_ds_zm=====wpxp================================== m(k)
- !
- ! ------invrs_rho_ds_zt--------d(rho_ds*wpxp)/dz----------- t(k)
- !
- ! =====rho_ds_zmm1===wpxpm1================================ m(k-1)
- !
- ! The vertical indices m(k), t(k), and m(k-1) correspond with altitudes
- ! zm(k), zt(k), and zm(k-1), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- k_mdiag = 1, & ! Momentum superdiagonal index.
- km1_mdiag = 2 ! Momentum subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- rho_ds_zm, & ! Dry, static density at momentum level (k) [kg/m^3]
- rho_ds_zmm1, & ! Dry, static density at momentum level (k+1) [kg/m^3]
- invrs_rho_ds_zt, & ! Inverse dry, static density @ thermo lev (k) [m^3/kg]
- invrs_dzt ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(2) :: lhs
-
-
- ! Momentum superdiagonal [ x wpxp(k,) ]
- lhs(k_mdiag) &
- = + invrs_rho_ds_zt * invrs_dzt * rho_ds_zm
-
- ! Momentum subdiagonal [ x wpxp(k-1,) ]
- lhs(km1_mdiag) &
- = - invrs_rho_ds_zt * invrs_dzt * rho_ds_zmm1
-
-
- return
- end function xm_term_ta_lhs
-
- !=============================================================================
- pure function wpxp_term_ta_lhs( wp3_on_wp2_ztp1, wp3_on_wp2_zt, &
- a1_ztp1, a1_zt, &
- rho_ds_ztp1, rho_ds_zt, &
- invrs_rho_ds_zm, &
- invrs_dzm, level ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection of w'x': implicit portion of the code.
- !
- ! The d(w'x')/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'^2x' )/dz.
- !
- ! A substitution is made in order to close the turbulent advection term,
- ! such that:
- !
- ! w'^2x' = a_1 * ( w'^3 / w'^2 ) * w'x',
- !
- ! where a_1 is a variable that is a function of sigma_sqd_w. The turbulent
- ! advection term becomes:
- !
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( w'^3 / w'^2 ) * w'x' ] / dz.
- !
- ! This term is solved for completely implicitly, such that:
- !
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( w'^3 / w'^2 ) * w'x'(t+1) ] / dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'x' being used is from
- ! the next timestep, which is being advanced to in solving the d(w'x')/dt
- ! equation.
- !
- ! This term is discretized as follows:
- !
- ! The values of w'x', w'^2, and a_1 are found on the momentum levels, while
- ! the values of w'^3 are found on the thermodynamic levels. Additionally,
- ! the values of rho_ds_zt are found on the thermodynamic levels, and the
- ! values of invrs_rho_ds_zm are found on the momentum levels. Each of the
- ! variables w'x', w'^2, and a_1 are interpolated to the intermediate
- ! thermodynamic levels. The values of the mathematical expression (called F
- ! here) within the dF/dz term are computed on the thermodynamic levels.
- ! Then, the derivative (d/dz) of the expression (F) is taken over the
- ! central momentum level, where it is multiplied by invrs_rho_ds_zm,
- ! yielding the desired result. In this function, the values of F are as
- ! follows:
- !
- ! F = rho_ds_zt * a_1(t) * ( w'^3(t) / w'^2(t) ) * w'x'(t+1);
- !
- ! where the timestep index (t) stands for the index of the current timestep.
- !
- !
- ! =a1p1========wp2p1========wpxpp1=================================== m(k+1)
- !
- ! -----a1(interp)---wp2(interp)---wpxp(interp)---wp3p1---rho_ds_ztp1- t(k+1)
- !
- ! =a1==========wp2==========wpxp=======invrs_rho_ds_zm=======dF/dz=== m(k)
- !
- ! -----a1(interp)---wp2(interp)---wpxp(interp)---wp3-----rho_ds_zt--- t(k)
- !
- ! =a1m1========wp2m1========wpxpm1=================================== m(k-1)
- !
- ! The vertical indices m(k+1), t(k+1), m(k), t(k), and m(k-1) correspond
- ! with altitudes zm(k+1), zt(k+1), zm(k), zt(k), and zm(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_grid_class, only: &
- gr ! Variable; gr%weights_zm2zt
-
-! use model_flags, only: &
-! l_standard_term_ta
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- integer, parameter :: &
- m_above = 1, & ! Index for upper momentum level grid weight.
- m_below = 2 ! Index for lower momentum level grid weight.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- wp3_on_wp2_ztp1, & ! Smoothed wp3 / wp2 on thermo. levels (k+1) [m/s]
- wp3_on_wp2_zt, & ! Smoothed wp3 / wp2 on thermo. levels (k) [m/s]
-! a1, & ! a_1 interpolated to thermo. level (k+1) [-]
- a1_ztp1, & ! a_1 interpolated to thermo. level (k+1) [-]
- a1_zt, & ! a_1 interpolated to thermo. level (k) [-]
- rho_ds_ztp1, & ! Dry, static density at thermo. level (k+1) [kg/m^3]
- rho_ds_zt, & ! Dry, static density at thermo. level (k) [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum lev (k) [m^3/kg]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- integer, intent(in) :: &
- level ! Central momentum level (on which calculation occurs).
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
- ! Local Variables
- integer :: &
- tkp1, & ! Thermodynamic level directly above central momentum level.
- tk ! Thermodynamic level directly below central momentum level.
-
- ! Thermodynamic level (k+1) is between momentum level (k+1)
- ! and momentum level (k).
- tkp1 = level + 1
-
- ! Thermodynamic level (k) is between momentum level (k)
- ! and momentum level (k-1).
- tk = level
-
- ! Note: The w'x' turbulent advection term, which is
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( w'^3 / w'^2 ) * w'x' ] / dz,
- ! still keeps the a_1 term inside the derivative, unlike the w'^3
- ! equation (found in advance_wp2_wp3_module.F90) and the equations for
- ! r_t'^2, th_l'^2, r_t'th_l', u'^2, v'^2, sclr'r_t', sclr'th_l', and
- ! sclr'^2 (found in advance_xp2_xpyp_module.F90). Brian.
-
-! if ( l_standard_term_ta ) then
-
- ! Always use the standard discretization for the w'x' turbulent advection
- ! term. Brian.
-
- ! The turbulent advection term is discretized normally, in accordance
- ! with the model equations found in the documentation and the description
- ! listed above.
- ! The w'x' turbulent advection term is
- ! - (1/rho_ds) * d [ rho_ds * a_1 * ( w'^3 / w'^2 ) * w'x' ] / dz
-
- ! Momentum superdiagonal: [ x wpxp(k+1,) ]
- lhs(kp1_mdiag) &
- = + invrs_rho_ds_zm &
- * invrs_dzm &
- * rho_ds_ztp1 * a1_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_above,tkp1)
-
- ! Momentum main diagonal: [ x wpxp(k,) ]
- lhs(k_mdiag) &
- = + invrs_rho_ds_zm &
- * invrs_dzm &
- * ( rho_ds_ztp1 * a1_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_below,tkp1) &
- - rho_ds_zt * a1_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_above,tk) &
- )
-
- ! Momentum subdiagonal: [ x wpxp(k-1,) ]
- lhs(km1_mdiag) &
- = - invrs_rho_ds_zm &
- * invrs_dzm &
- * rho_ds_zt * a1_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_below,tk)
-
-! else
-
- ! This discretization very similar to what Brian did for the xp2_ta terms
- ! and is intended to stabilize the simulation by pulling a1 out of the
- ! derivative. It didn't seem to work very well. -dschanen 17 Jan 2010
-
- ! Momentum superdiagonal: [ x wpxp(k+1,) ]
-! lhs(kp1_mdiag) &
-! = + invrs_rho_ds_zm * a1 &
-! * invrs_dzm &
-! * rho_ds_ztp1 &
-! * wp3_on_wp2_ztp1 &
-! * gr%weights_zm2zt(m_above,tkp1)
-
- ! Momentum main diagonal: [ x wpxp(k,) ]
-! lhs(k_mdiag) &
-! = + invrs_rho_ds_zm * a1 &
-! * invrs_dzm &
-! * ( rho_ds_ztp1 &
-! * wp3_on_wp2_ztp1 &
-! * gr%weights_zm2zt(m_below,tkp1) &
-! - rho_ds_zt &
-! * wp3_on_wp2_zt &
-! * gr%weights_zm2zt(m_above,tk) &
-! )
-
-! ! Momentum subdiagonal: [ x wpxp(k-1,) ]
-! lhs(km1_mdiag) &
-! = - invrs_rho_ds_zm * a1 &
-! * invrs_dzm &
-! * rho_ds_zt &
-! * wp3_on_wp2_zt &
-! * gr%weights_zm2zt(m_below,tk)
-
-! endif ! l_standard_term_ta
-
-
- return
- end function wpxp_term_ta_lhs
-
- !=============================================================================
- pure function wpxp_term_ta_lhs_upwind( a1_zm, a1_zm_p1, a1_zm_m1, &
- wp3_on_wp2_p1, wp3_on_wp2, wp3_on_wp2_m1, &
- invrs_dzt, invrs_dztkp1, &
- invrs_rho_ds_zm, &
- rho_ds_zmp1, rho_ds_zm, rho_ds_zmm1 ) &
- result( lhs )
-
- ! Description:
- ! Upwind Differencing for the wpxp term
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- zero ! Constant(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- a1_zm, & ! a_1(k) on momentum levels [-]
- a1_zm_p1, & ! a_1(k+1) on momentum levels [-]
- a1_zm_m1, & ! a_1(k-1) on momentum levels [-]
- wp3_on_wp2_p1, & ! Smoothed wp3 / wp2 on moment. levels (k+1) [m/s]
- wp3_on_wp2, & ! Smoothed wp3 / wp2 on moment. levels (k) [m/s]
- wp3_on_wp2_m1, & ! Smoothed wp3 / wp2 on moment. levels (k-1) [m/s]
- invrs_dzt, & ! Inverse of grid spacing (k) [1/m]
- invrs_dztkp1, & ! Inverse of grid spacing (k+1) [1/m]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum lev (k) [m^3/kg]
- rho_ds_zm, & ! Density of air (k) [kg/m^3]
- rho_ds_zmp1, & ! Density of air (k+1) [kg/m^3]
- rho_ds_zmm1 ! Density of air (k-1) [kg/m^3]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
-
- if ( wp3_on_wp2 > zero ) then
-
- ! "Wind" is blowing upwards (a1_zm > 0 and wp2 > 0 always)
- lhs(kp1_mdiag) = zero
-
- lhs(k_mdiag) &
- = + invrs_dzt * invrs_rho_ds_zm &
- * rho_ds_zm * a1_zm * wp3_on_wp2
-
- lhs(km1_mdiag) &
- = - invrs_dzt * invrs_rho_ds_zm &
- * rho_ds_zmm1 * a1_zm_m1 * wp3_on_wp2_m1
-
- else ! "Wind" is blowing downward
-
- lhs(kp1_mdiag) &
- = + invrs_dztkp1 * invrs_rho_ds_zm &
- * rho_ds_zmp1 * a1_zm_p1 * wp3_on_wp2_p1
-
- lhs(k_mdiag) &
- = - invrs_dztkp1 * invrs_rho_ds_zm &
- * rho_ds_zm * a1_zm * wp3_on_wp2
-
- lhs(km1_mdiag) = zero
-
- endif
-
-
- return
- end function wpxp_term_ta_lhs_upwind
-
- !=============================================================================
- pure function wpxp_term_tp_lhs( wp2, invrs_dzm ) &
- result( lhs )
-
- ! Description:
- ! Turbulent production of w'x': implicit portion of the code.
- !
- ! The d(w'x')/dt equation contains a turbulent production term:
- !
- ! - w'^2 d(xm)/dz.
- !
- ! This term is solved for completely implicitly, such that:
- !
- ! - w'^2 * d( xm(t+1) )/dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of xm being used is from the
- ! next timestep, which is being advanced to in solving the d(w'x')/dt and
- ! d(xm)/dt equations.
- !
- ! This term is discretized as follows:
- !
- ! The values of xm are found on thermodynamic levels, while the values of
- ! w'^2 are found on momentum levels. The derivative of xm is taken over the
- ! intermediate (central) momentum level, where it is multiplied by w'^2,
- ! yielding the desired result.
- !
- ! ---------------------------xmp1-------------------------- t(k+1)
- !
- ! ==========wp2=====================d(xm)/dz=============== m(k)
- !
- ! ---------------------------xm---------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- wp2, & ! w'^2(k) [m^2/s^2]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(2) :: lhs
-
-
- ! Thermodynamic superdiagonal [ x xm(k+1,) ]
- lhs(kp1_tdiag) &
- = + wp2 * invrs_dzm
-
- ! Thermodynamic subdiagonal [ x xm(k,) ]
- lhs(k_tdiag) &
- = - wp2 * invrs_dzm
-
-
- return
- end function wpxp_term_tp_lhs
-
- !=============================================================================
- pure function wpxp_terms_ac_pr2_lhs( C7_Skw_fnc, &
- wm_ztp1, wm_zt, invrs_dzm ) &
- result( lhs )
-
- ! Description:
- ! Accumulation of w'x' and w'x' pressure term 2: implicit portion of the
- ! code.
- !
- ! The d(w'x')/dt equation contains an accumulation term:
- !
- ! - w'x' dw/dz;
- !
- ! and pressure term 2:
- !
- ! + C_7 w'x' dw/dz.
- !
- ! Both the w'x' accumulation term and pressure term 2 are completely
- ! implicit. The accumulation term and pressure term 2 are combined and
- ! solved together as:
- !
- ! - ( 1 - C_7 ) * w'x'(t+1) * dw/dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'x' being used is from
- ! the next timestep, which is being advanced to in solving the d(w'x')/dt
- ! equation.
- !
- ! The terms are discretized as follows:
- !
- ! The values of w'x' are found on momentum levels, while the values of wm_zt
- ! (mean vertical velocity on thermodynamic levels) are found on
- ! thermodynamic levels. The vertical derivative of wm_zt is taken over the
- ! intermediate (central) momentum level. It is then multiplied by w'x'
- ! (implicitly calculated at timestep (t+1)) and the coefficients to yield
- ! the desired results.
- !
- ! -------wm_ztp1------------------------------------------- t(k+1)
- !
- ! ===============d(wm_zt)/dz============wpxp=============== m(k)
- !
- ! -------wm_zt--------------------------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- one ! Constant(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C7_Skw_fnc, & ! C_7 parameter with Sk_w applied (k) [-]
- wm_ztp1, & ! w wind component on thermodynamic level (k+1) [m/s]
- wm_zt, & ! w wind component on thermodynamic level (k) [m/s]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
-
- ! Momentum main diagonal: [ x wpxp(k,) ]
- lhs = ( one - C7_Skw_fnc ) * invrs_dzm * ( wm_ztp1 - wm_zt )
-
-
- return
- end function wpxp_terms_ac_pr2_lhs
-
- !=============================================================================
- pure function wpxp_term_pr1_lhs( C6x_Skw_fnc, tau_zm ) &
- result( lhs )
-
- ! Description
- ! Pressure term 1 for w'x': implicit portion of the code.
- !
- ! The d(w'x')/dt equation contains pressure term 1:
- !
- ! - ( C_6 / tau_m ) w'x'.
- !
- ! This term is solved for completely implicitly, such that:
- !
- ! - ( C_6 / tau_m ) w'x'(t+1)
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of w'x' being used is from
- ! the next timestep, which is being advanced to in solving the d(w'x')/dt
- ! equation.
- !
- ! The values of w'x' are found on the momentum levels. The values of the
- ! C_6 skewness function and time-scale tau_m are also found on the momentum
- ! levels.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C6x_Skw_fnc, & ! C_6x parameter with Sk_w applied (k) [-]
- tau_zm ! Time-scale tau at momentum level (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
-
- ! Momentum main diagonal: [ x wpxp(k,) ]
- lhs = C6x_Skw_fnc / tau_zm
-
-
- return
- end function wpxp_term_pr1_lhs
-
- !=============================================================================
- pure function wpxp_terms_bp_pr3_rhs( C7_Skw_fnc, thv_ds_zm, xpthvp ) &
- result( rhs )
-
- ! Description:
- ! Buoyancy production of w'x' and w'x' pressure term 3: explicit portion of
- ! the code.
- !
- ! The d(w'x')/dt equation contains a buoyancy production term:
- !
- ! + (g/thv_ds) x'th_v';
- !
- ! and pressure term 3:
- !
- ! - C_7 (g/thv_ds) x'th_v'.
- !
- ! Both the w'x' buoyancy production term and pressure term 3 are completely
- ! explicit. The buoyancy production term and pressure term 3 are combined
- ! and solved together as:
- !
- ! + ( 1 - C_7 ) * (g/thv_ds) * x'th_v'.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: & ! Constants(s)
- grav, & ! Gravitational acceleration [m/s^2]
- one
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C7_Skw_fnc, & ! C_7 parameter with Sk_w applied (k) [-]
- thv_ds_zm, & ! Dry, base-state theta_v on mom. lev. (k) [K]
- xpthvp ! x'th_v'(k) [K {xm units}]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
-
- rhs = ( grav / thv_ds_zm ) * ( one - C7_Skw_fnc ) * xpthvp
-
-
- return
- end function wpxp_terms_bp_pr3_rhs
-
- !=============================================================================
- subroutine xm_correction_wpxp_cl( solve_type, dt, wpxp_chnge, invrs_dzt, &
- xm )
-
- ! Description:
- ! Corrects the value of xm if w'x' needed to be clipped, for xm is partially
- ! based on the derivative of w'x' with respect to altitude.
- !
- ! The time-tendency equation for xm is:
- !
- ! d(xm)/dt = -w d(xm)/dz - d(w'x')/dz + d(xm)/dt|_ls;
- !
- ! where d(xm)/dt|_ls is the rate of change of xm over time due to radiation,
- ! microphysics, and/or any other large-scale forcing(s).
- !
- ! The time-tendency equation for xm is solved in conjunction with the
- ! time-tendency equation for w'x'. Both equations are solved together in a
- ! semi-implicit manner. However, after both equations have been solved (and
- ! thus both xm and w'x' have been advanced to the next timestep with
- ! timestep index {t+1}), the value of covariance w'x' may be clipped at any
- ! level in order to prevent the correlation of w and x from becoming greater
- ! than 1 or less than -1.
- !
- ! The correlation between w and x is:
- !
- ! corr_(w,x) = w'x' / [ sqrt(w'^2) * sqrt(x'^2) ].
- !
- ! The correlation must always have a value between -1 and 1, such that:
- !
- ! -1 <= corr_(w,x) <= 1.
- !
- ! Therefore, there is an upper limit on w'x', such that:
- !
- ! w'x' <= [ sqrt(w'^2) * sqrt(x'^2) ];
- !
- ! and a lower limit on w'x', such that:
- !
- ! w'x' >= -[ sqrt(w'^2) * sqrt(x'^2) ].
- !
- ! The aforementioned time-tendency equation for xm is based on the value of
- ! w'x' without being clipped (w'x'{t+1}_unclipped), such that:
- !
- ! d(xm)/dt = -w d(xm{t+1})/dz - d(w'x'{t+1}_unclipped)/dz + d(xm{t})/dt|_ls;
- !
- ! where the both the mean advection term, -w d(xm{t+1})/dz, and the
- ! turbulent advection term, -d(w'x'{t+1}_unclipped)/dz, are solved
- ! completely implicitly. The xm forcing term, +d(xm{t})/dt|_ls, is solved
- ! completely explicitly.
- !
- ! However, if w'x' needs to be clipped after being advanced one timestep,
- ! then xm needs to be altered to reflect the fact that w'x' has a different
- ! value than the value used while both were being solved together. Ideally,
- ! the xm time-tendency equation that should be used is:
- !
- ! d(xm)/dt = -w d(xm{t+1})/dz - d(w'x'{t+1}_clipped)/dz + d(xm{t})/dt|_ls.
- !
- ! However, w'x'{t+1}_clipped isn't known until after the w'x' and xm
- ! equations have been solved together. However, a proper adjuster can be
- ! applied to xm through the use of the following relationship:
- !
- ! w'x'{t+1}_clipped = w'x'{t+1}_unclipped + w'x'{t+1}_amount_clipped;
- !
- ! at any given vertical level.
- !
- ! When the expression above is substituted into the preceeding xm
- ! time-tendency equation, the resulting equation for xm time-tendency is:
- !
- ! d(xm)/dt = -w d(xm{t+1})/dz - d(w'x'{t+1}_unclipped)/dz
- ! - d(w'x'{t+1}_amount_clipped)/dz + d(xm{t})/dt|_ls.
- !
- ! Thus, the resulting xm time-tendency equation is the same as the original
- ! xm time-tendency equation, but with added adjuster term:
- !
- ! -d(w'x'{t+1}_amount_clipped)/dz.
- !
- ! Since the adjuster term needs to be applied after xm has already been
- ! solved, it needs to be multiplied by the timestep length and added on to
- ! xm{t+1}, such that:
- !
- ! xm{t+1}_after_adjustment =
- ! xm{t+1}_before_adjustment + ( -d(w'x'{t+1}_amount_clipped)/dz ) * dt.
- !
- ! The adjuster term is discretized as follows:
- !
- ! The values of w'x' are located on the momentum levels. Thus, the values
- ! of w'x'_amount_clipped are also located on the momentum levels. The
- ! values of xm are located on the thermodynamic levels. The derivatives
- ! (d/dz) of w'x'_amount_clipped are taken over the intermediate
- ! thermodynamic levels, where they are applied to xm.
- !
- ! =======wpxp_amount_clipped=============================== m(k)
- !
- ! -----------------------------d(wpxp_amount_clipped)/dz--- t(k)
- !
- ! =======wpxpm1_amount_clipped============================= m(k-1)
- !
- ! The vertical indices m(k), t(k), and m(k-1) correspond with altitudes
- ! zm(k), zt(k), and zm(k-1), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
-
- ! Note: The results of this xm adjustment are highly dependent on the
- ! numerical stability and the smoothness of the w'^2 and x'^2 fields.
- ! An unstable "sawtooth" profile for w'^2 and/or x'^2 causes an
- ! unstable "sawtooth" profile for the upper and lower limits on w'x'.
- ! In turn, this causes an unstable "sawtooth" profile for
- ! w'x'_amount_clipped. Taking the derivative of that such a "noisy"
- ! field and applying the results to xm causes the xm field to become
- ! more "noisy" and unstable.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s); gr%nz only.
-
- use crmx_clubb_precision, only: &
- time_precision, &
- core_rknd
-
- use crmx_stats_type, only: &
- stat_update_var ! Procedure(s)
-
- use crmx_stats_variables, only: &
- l_stats_samp, & ! Variable(s)
- zt, &
- ithlm_tacl, &
- irtm_tacl
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Variable that is being solved for.
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wpxp_chnge, & ! Amount of change in w'x' due to clipping [m/s {xm units}]
- invrs_dzt ! Inverse of grid spacing [1/m]
-
- ! Input/Output Variable
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- xm ! xm (thermodynamic levels) [{xm units}]
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- xm_tndcy_wpxp_cl ! d(xm)/dt due to clipping of w'x' [{xm units}/s]
-
- integer :: k ! Array index
-
- integer :: ixm_tacl ! Statistical index
-
-
- select case ( solve_type )
- case ( xm_wpxp_rtm )
- ixm_tacl = irtm_tacl
- case ( xm_wpxp_thlm )
- ixm_tacl = ithlm_tacl
- case default
- ixm_tacl = 0
- end select
-
- ! Adjusting xm based on clipping for w'x'.
- ! Loop over all thermodynamic levels between the second-lowest and the
- ! highest.
- do k = 2, gr%nz, 1
- xm_tndcy_wpxp_cl(k) = - invrs_dzt(k) * ( wpxp_chnge(k) - wpxp_chnge(k-1) )
- xm(k) = xm(k) + xm_tndcy_wpxp_cl(k) * real( dt, kind = core_rknd )
- enddo
-
- if ( l_stats_samp ) then
- ! The adjustment to xm due to turbulent advection term clipping
- ! (xm term tacl) is completely explicit; call stat_update_var.
- call stat_update_var( ixm_tacl, xm_tndcy_wpxp_cl, zt )
- endif
-
-
- return
-
- end subroutine xm_correction_wpxp_cl
-
-
- !=============================================================================
- pure function damp_coefficient( coefficient, Cx_Skw_fnc, max_coeff_value, &
- threshold, Lscale ) &
- result( damped_value )
-
- ! Description:
- ! Damps a given coefficient linearly based on the value of Lscale.
- ! For additional information see CLUBB ticket #431.
-
- use crmx_constants_clubb, only: &
- one_hundred ! Constant(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- implicit none
-
- ! Input variables
- real( kind = core_rknd ), intent(in) :: &
- coefficient, & ! The coefficient to be damped
- max_coeff_value, & ! Maximum value the damped coefficient should have
- threshold ! Value of Lscale below which the damping should occur
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- Lscale, & ! Current value of Lscale
- Cx_Skw_fnc ! Initial skewness function before damping
-
- ! Local variables
- real( kind = core_rknd ), parameter :: &
- ! Added to prevent large damping at low altitudes where Lscale is small
- altitude_threshold = one_hundred ! Altitude above which damping should occur
-
- ! Return Variable
- real( kind = core_rknd ), dimension(gr%nz) :: damped_value
-
- damped_value = Cx_Skw_fnc
-
- where( Lscale < threshold .and. gr%zt > altitude_threshold)
- damped_value = max_coeff_value &
- + ( ( coefficient - max_coeff_value ) / threshold ) &
- * Lscale
- end where
-
- return
-
- end function damp_coefficient
-!===============================================================================
-
-end module crmx_advance_xm_wpxp_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_advance_xp2_xpyp_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_advance_xp2_xpyp_module.F90
deleted file mode 100644
index c4f490df6f..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_advance_xp2_xpyp_module.F90
+++ /dev/null
@@ -1,3417 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: advance_xp2_xpyp_module.F90 6149 2013-04-08 21:45:56Z storer@uwm.edu $
-!===============================================================================
-module crmx_advance_xp2_xpyp_module
-
- ! Description:
- ! Contains the subroutine advance_xp2_xpyp and ancillary functions.
- !-----------------------------------------------------------------------
-
- implicit none
-
- public :: advance_xp2_xpyp, &
- update_xp2_mc_tndcy
-
- private :: xp2_xpyp_lhs, &
- xp2_xpyp_solve, &
- xp2_xpyp_uv_rhs, &
- xp2_xpyp_rhs, &
- xp2_xpyp_implicit_stats, &
- term_ta_lhs, &
- term_ta_lhs_upwind, &
- term_ta_rhs, &
- term_tp, &
- term_dp1_lhs, &
- term_dp1_rhs, &
- term_pr1, &
- term_pr2
-
- private ! Set default scope
-
- ! Private named constants to avoid string comparisons
- integer, parameter, private :: &
- xp2_xpyp_rtp2 = 1, & ! Named constant for rtp2 solves
- xp2_xpyp_thlp2 = 2, & ! Named constant for thlp2 solves
- xp2_xpyp_rtpthlp = 3, & ! Named constant for rtpthlp solves
- xp2_xpyp_up2_vp2 = 4, & ! Named constant for up2_vp2 solves
- xp2_xpyp_up2 = 5, & ! Named constant for up2 solves
- xp2_xpyp_vp2 = 6, & ! Named constant for vp2 solves
- xp2_xpyp_scalars = 7, & ! Named constant for scalar solves
- xp2_xpyp_sclrp2 = 8, & ! Named constant for sclrp2 solves
- xp2_xpyp_sclrprtp = 9, & ! Named constant for sclrprtp solves
- xp2_xpyp_sclrpthlp = 10 ! Named constant for sclrpthlp solves
-
- contains
-
- !=============================================================================
- subroutine advance_xp2_xpyp( tau_zm, wm_zm, rtm, wprtp, thlm, &
- wpthlp, wpthvp, um, vm, wp2, wp2_zt, &
- wp3, upwp, vpwp, sigma_sqd_w, Skw_zm, &
- Kh_zt, rtp2_forcing, thlp2_forcing, &
- rtpthlp_forcing, rho_ds_zm, rho_ds_zt, &
- invrs_rho_ds_zm, thv_ds_zm, &
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, &
- l_iter, dt, &
- sclrm, wpsclrp, &
- rtp2, thlp2, rtpthlp, up2, vp2, &
- err_code, &
- sclrp2, sclrprtp, sclrpthlp )
-
- ! Description:
- ! Subprogram to diagnose variances by solving steady-state equations
-
- ! References:
- ! Eqn. 13, 14, 15 on p. 3545 of
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
-
- ! See also:
- ! ``Equations for CLUBB'', Section 4:
- ! /Steady-state solution for the variances/
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- w_tol_sqd, & ! Constant(s)
- rt_tol, &
- thl_tol, &
- w_tol_sqd, &
- fstderr, &
- one, &
- two_thirds, &
- one_half, &
- one_third, &
- zero, &
- zero_threshold
-
- use crmx_model_flags, only: &
- l_hole_fill, & ! logical constants
- l_single_C2_Skw
-
- use crmx_parameters_tunable, only: &
- C2rt, & ! Variable(s)
- C2thl, &
- C2rtthl, &
- c_K2, &
- nu2_vert_res_dep, &
- c_K9, &
- nu9_vert_res_dep, &
- beta, &
- C4, &
- C14, &
- C5, &
- C2, &
- C2b, &
- C2c
-
- use crmx_parameters_model, only: &
- sclr_dim, & ! Variable(s)
- sclr_tol
-
- use crmx_grid_class, only: &
- gr, & ! Variable(s)
- zm2zt ! Procedure(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_clip_explicit, only: &
- clip_covar, & ! Procedure(s)
- clip_variance, &
- clip_rtp2, & ! Variable(s)
- clip_thlp2, &
- clip_rtpthlp, &
- clip_up2, &
- clip_vp2, &
- clip_sclrp2, &
- clip_sclrprtp, &
- clip_sclrpthlp
-
- use crmx_stats_type, only: &
- stat_modify
-
- use crmx_error_code, only: &
- clubb_no_error, & ! Variable(s)
- clubb_var_out_of_range, &
- clubb_singular_matrix
-
- use crmx_error_code, only: &
- fatal_error, & ! Procedure(s)
- clubb_at_least_debug_level
-
- use crmx_stats_variables, only: &
- zm, &
- irtp2_cl, &
- l_stats_samp
-
- use crmx_array_index, only: &
- iisclr_rt, &
- iisclr_thl
-
- implicit none
-
- ! Intrinsic functions
- intrinsic :: &
- exp, sqrt, min
-
- ! Constant parameters
- logical, parameter :: &
- l_clip_large_rtp2 = .true. ! Clip rtp2 to be < rtm^2 * coef
-
- real( kind = core_rknd ), parameter :: &
- rtp2_clip_coef = one_half ! Coefficient appled the clipping threshold on rtp2 [-]
-
- ! Input variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- tau_zm, & ! Time-scale tau on momentum levels [s]
- wm_zm, & ! w-wind component on momentum levels [m/s]
- rtm, & ! Total water mixing ratio (t-levs) [kg/kg]
- wprtp, & ! (momentum levels) [(m/s)(kg/kg)]
- thlm, & ! Liquid potential temp. (t-levs) [K]
- wpthlp, & ! (momentum levels) [(m K)/s]
- wpthvp, & ! (momentum levels) [(m K)/s]
- um, & ! u wind (thermodynamic levels) [m/s]
- vm, & ! v wind (thermodynamic levels) [m/s]
- wp2, & ! (momentum levels) [m^2/s^2]
- wp2_zt, & ! interpolated to thermo. levels [m^2/s^2]
- wp3, & ! (thermodynamic levels) [m^3/s^3]
- upwp, & ! (momentum levels) [m^2/s^2]
- vpwp, & ! (momentum levels) [m^2/s^2]
- sigma_sqd_w, & ! sigma_sqd_w (momentum levels) [-]
- Skw_zm, & ! Skewness of w on momentum levels [-]
- Kh_zt, & ! Eddy diffusivity on thermo. levels [m^2/s]
- rtp2_forcing, & ! forcing (momentum levels) [(kg/kg)^2/s]
- thlp2_forcing, & ! forcing (momentum levels) [K^2/s]
- rtpthlp_forcing, & ! forcing (momentum levels) [(kg/kg)K/s]
- rho_ds_zm, & ! Dry, static density on momentum levs. [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ mom. levs. [m^3/kg]
- thv_ds_zm, & ! Dry, base-state theta_v on mom. levs. [K]
- Lscale, & ! Mixing length [m]
- wp3_on_wp2, & ! Smoothed version of / zm [m/s]
- wp3_on_wp2_zt ! Smoothed version of / zt [m/s]
-
- logical, intent(in) :: l_iter ! Whether variances are prognostic
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- ! Passive scalar input
- real( kind = core_rknd ), intent(in), dimension(gr%nz, sclr_dim) :: &
- sclrm, wpsclrp
-
- ! Input/Output variables
- ! An attribute of (inout) is also needed to import the value of the variances
- ! at the surface. Brian. 12/18/05.
- real( kind = core_rknd ), intent(inout), dimension(gr%nz) :: &
- rtp2, & ! [(kg/kg)^2]
- thlp2, & ! [K^2]
- rtpthlp, & ! [(kg K)/kg]
- up2, & ! [m^2/s^2]
- vp2 ! [m^2/s^2]
-
- ! Output variable for singular matrices
- integer, intent(inout) :: err_code
-
- ! Passive scalar output
- real( kind = core_rknd ), intent(inout), dimension(gr%nz, sclr_dim) :: &
- sclrp2, sclrprtp, sclrpthlp
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- C2sclr_1d, C2rt_1d, C2thl_1d, C2rtthl_1d, &
- C4_C14_1d ! Parameters C4 and C14 combined for simplicity
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- a1 ! a_1 (momentum levels); See eqn. 24 in `Equations for CLUBB' [-]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- upwp_zt, & ! interpolated to thermodynamic levels [m^2/s^2]
- vpwp_zt, & ! interpolated to thermodynamic levels [m^2/s^2]
- wpsclrp_zt ! interp. to thermo. levels [m/s {sclrm units}]
-
- real( kind = core_rknd ) :: &
- threshold ! Minimum value for variances [units vary]
-
- real( kind = core_rknd ), dimension(3,gr%nz) :: &
- lhs ! Tridiagonal matrix
-
- real( kind = core_rknd ), dimension(gr%nz,1) :: &
- rhs ! RHS vector of tridiagonal matrix
-
- real( kind = core_rknd ), dimension(gr%nz,2) :: &
- uv_rhs, &! RHS vectors of tridiagonal system for up2/vp2
- uv_solution ! Solution to the tridiagonal system for up2/vp2
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim*3) :: &
- sclr_rhs, & ! RHS vectors of tridiagonal system for the passive scalars
- sclr_solution ! Solution to tridiagonal system for the passive scalars
-
- integer, dimension(5+1) :: &
- err_code_array ! Array containing the error codes for each variable
-
- ! Eddy Diffusion for Variances and Covariances.
- real( kind = core_rknd ), dimension(gr%nz) :: &
- Kw2, & ! For rtp2, thlp2, rtpthlp, and passive scalars [m^2/s]
- Kw9 ! For up2 and vp2 [m^2/s]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- wprtp_zt, & ! w'r_t' interpolated to thermodynamic levels [(kg/kg) m/s]
- wpthlp_zt ! w'th_l' interpolated to thermodyamnic levels [K m/s]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- rtpthlp_chnge ! Net change in r_t'th_l' due to clipping [(kg/kg) K]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim) :: &
- sclrprtp_chnge, & ! Net change in sclr'r_t' due to clipping [{units vary}]
- sclrpthlp_chnge ! Net change in sclr'th_l' due to clipping [{units vary}]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- sclrp2_forcing, & ! forcing (momentum levels) [units vary]
- sclrprtp_forcing, & ! forcing (momentum levels) [units vary]
- sclrpthlp_forcing ! forcing (momentum levels) [units vary]
-
- logical :: l_scalar_calc, l_first_clip_ts, l_last_clip_ts
-
- ! Loop indices
- integer :: i, k
-
- !---------------------------- Begin Code ----------------------------------
-
- if ( clubb_at_least_debug_level( 2 ) ) then
- ! Assertion check for C5
- if ( C5 > one .or. C5 < zero ) then
- write(fstderr,*) "The C5 variable is outside the valid range"
- err_code = clubb_var_out_of_range
- return
- end if
- end if
-
- if ( l_single_C2_Skw ) then
- ! Use a single value of C2 for all equations.
- C2rt_1d(1:gr%nz) &
- = C2b + (C2-C2b) *exp( -one_half * (Skw_zm(1:gr%nz)/C2c)**2 )
-
- C2thl_1d = C2rt_1d
- C2rtthl_1d = C2rt_1d
-
- C2sclr_1d = C2rt_1d
- else
- ! Use 3 different values of C2 for rtp2, thlp2, rtpthlp.
- C2rt_1d(1:gr%nz) = C2rt
- C2thl_1d(1:gr%nz) = C2thl
- C2rtthl_1d(1:gr%nz) = C2rtthl
-
- C2sclr_1d(1:gr%nz) = C2rt ! Use rt value for now
- end if
-
- ! Combine C4 and C14 for simplicity
- C4_C14_1d(1:gr%nz) = ( two_thirds * C4 ) + ( one_third * C14 )
-
- ! Are we solving for passive scalars as well?
- if ( sclr_dim > 0 ) then
- l_scalar_calc = .true.
- else
- l_scalar_calc = .false.
- end if
-
-
- ! Define a_1 (located on momentum levels).
- ! It is a variable that is a function of sigma_sqd_w (where sigma_sqd_w is
- ! located on the momentum levels).
- a1(1:gr%nz) = one / ( one - sigma_sqd_w(1:gr%nz) )
-
-
- ! Interpolate a_1, w'r_t', w'th_l', u'w', and v'w' from the momentum levels
- ! to the thermodynamic levels. These will be used for the turbulent
- ! advection (ta) terms in each equation.
- a1_zt = max( zm2zt( a1 ), zero_threshold ) ! Positive definite quantity
- wprtp_zt = zm2zt( wprtp )
- wpthlp_zt = zm2zt( wpthlp )
- upwp_zt = zm2zt( upwp )
- vpwp_zt = zm2zt( vpwp )
-
- ! Initialize tridiagonal solutions to valid
-
- err_code_array(:) = clubb_no_error
-
-
- ! Define the Coefficent of Eddy Diffusivity for the variances
- ! and covariances.
- do k = 1, gr%nz, 1
-
- ! Kw2 is used for variances and covariances rtp2, thlp2, rtpthlp, and
- ! passive scalars. The variances and covariances are located on the
- ! momentum levels. Kw2 is located on the thermodynamic levels.
- ! Kw2 = c_K2 * Kh_zt
- Kw2(k) = c_K2 * Kh_zt(k)
-
- ! Kw9 is used for variances up2 and vp2. The variances are located on
- ! the momentum levels. Kw9 is located on the thermodynamic levels.
- ! Kw9 = c_K9 * Kh_zt
- Kw9(k) = c_K9 * Kh_zt(k)
-
- enddo
-
- !!!!!***** r_t'^2 *****!!!!!
-
- ! Implicit contributions to term rtp2
- call xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, & ! Intent(in)
- a1, a1_zt, tau_zm, wm_zm, Kw2, & ! Intent(in)
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, & ! Intent(in)
- C2rt_1d, nu2_vert_res_dep, beta, & ! Intent(in)
- lhs ) ! Intent(out)
-
-
- call xp2_xpyp_rhs( xp2_xpyp_rtp2, dt, l_iter, a1, a1_zt, & ! Intent(in)
- wp2_zt, wprtp, wprtp_zt, wp3_on_wp2, & ! Intent(in)
- wp3_on_wp2_zt, wprtp, wprtp_zt, & ! Intent(in)
- rtm, rtm, rtp2, rtp2_forcing, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! Intent(in)
- C2rt_1d, tau_zm, rt_tol**2, beta, & ! Intent(in)
- rhs ) ! Intent(out)
-
- ! Solve the tridiagonal system
- call xp2_xpyp_solve( xp2_xpyp_rtp2, 1, & ! Intent(in)
- rhs, lhs, rtp2, & ! Intent(inout)
- err_code_array(1) ) ! Intent(out)
-
- if ( l_stats_samp ) then
- call xp2_xpyp_implicit_stats( xp2_xpyp_rtp2, rtp2 ) ! Intent(in)
- end if
-
- !!!!!***** th_l'^2 *****!!!!!
-
- ! Implicit contributions to term thlp2
- call xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, & ! Intent(in)
- a1, a1_zt, tau_zm, wm_zm, Kw2, & ! Intent(in)
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, & ! Intent(in)
- C2thl_1d, nu2_vert_res_dep, beta, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Explicit contributions to thlp2
- call xp2_xpyp_rhs( xp2_xpyp_thlp2, dt, l_iter, a1, a1_zt, & ! Intent(in)
- wp2_zt, wpthlp, wpthlp_zt, wp3_on_wp2, & ! Intent(in)
- wp3_on_wp2_zt, wpthlp, wpthlp_zt, & ! Intent(in)
- thlm, thlm, thlp2, thlp2_forcing, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! Intent(in)
- C2thl_1d, tau_zm, thl_tol**2, beta, & ! Intent(in)
- rhs ) ! Intent(out)
-
- ! Solve the tridiagonal system
- call xp2_xpyp_solve( xp2_xpyp_thlp2, 1, & ! Intent(in)
- rhs, lhs, thlp2, & ! Intent(inout)
- err_code_array(2) ) ! Intent(out)
-
- if ( l_stats_samp ) then
- call xp2_xpyp_implicit_stats( xp2_xpyp_thlp2, thlp2 ) ! Intent(in)
- end if
-
-
- !!!!!***** r_t'th_l' *****!!!!!
-
- ! Implicit contributions to term rtpthlp
- call xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, & ! Intent(in)
- a1, a1_zt, tau_zm, wm_zm, Kw2, & ! Intent(in)
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, & ! Intent(in)
- C2rtthl_1d, nu2_vert_res_dep, beta, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Explicit contributions to rtpthlp
- call xp2_xpyp_rhs( xp2_xpyp_rtpthlp, dt, l_iter, a1, a1_zt, & ! Intent(in)
- wp2_zt, wprtp, wprtp_zt, wp3_on_wp2, & ! Intent(in)
- wp3_on_wp2_zt, wpthlp, wpthlp_zt, & ! Intent(in)
- rtm, thlm, rtpthlp, rtpthlp_forcing, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! Intent(in)
- C2rtthl_1d, tau_zm, zero_threshold, beta, & ! Intent(in)
- rhs ) ! Intent(out)
-
- ! Solve the tridiagonal system
- call xp2_xpyp_solve( xp2_xpyp_rtpthlp, 1, & ! Intent(in)
- rhs, lhs, rtpthlp, & ! Intent(inout)
- err_code_array(3) ) ! Intent(out)
-
- if ( l_stats_samp ) then
- call xp2_xpyp_implicit_stats( xp2_xpyp_rtpthlp, rtpthlp ) ! Intent(in)
- end if
-
-
- !!!!!***** u'^2 / v'^2 *****!!!!!
-
- ! Implicit contributions to term up2/vp2
- call xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, & ! Intent(in)
- a1, a1_zt, tau_zm, wm_zm, Kw9, & ! Intent(in)
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, & ! Intent(in)
- C4_C14_1d, nu9_vert_res_dep, beta, & ! Intent(in)
- lhs ) ! Intent(out)
-
- ! Explicit contributions to up2
- call xp2_xpyp_uv_rhs( xp2_xpyp_up2, dt, l_iter, a1, a1_zt, wp2, & ! Intent(in)
- wp2_zt, wpthvp, Lscale, wp3_on_wp2_zt, & ! Intent(in)
- wp3_on_wp2, C4_C14_1d, tau_zm, & ! Intent(in)
- um, vm, upwp, upwp_zt, vpwp, vpwp_zt, & ! Intent(in)
- up2, vp2, rho_ds_zt, invrs_rho_ds_zm, & ! Intent(in)
- rho_ds_zm, & ! Intent(in)
- thv_ds_zm, C4, C5, C14, beta, & ! Intent(in)
- uv_rhs(:,1) ) ! Intent(out)
-
- ! Explicit contributions to vp2
- call xp2_xpyp_uv_rhs( xp2_xpyp_vp2, dt, l_iter, a1, a1_zt, wp2, & ! Intent(in)
- wp2_zt, wpthvp, Lscale, wp3_on_wp2_zt, & ! Intent(in)
- wp3_on_wp2, C4_C14_1d, tau_zm, & ! Intent(in)
- vm, um, vpwp, vpwp_zt, upwp, upwp_zt, & ! Intent(in)
- vp2, up2, rho_ds_zt, invrs_rho_ds_zm, & ! Intent(in)
- rho_ds_zm, & ! Intent(in)
- thv_ds_zm, C4, C5, C14, beta, & ! Intent(in)
- uv_rhs(:,2) ) ! Intent(out)
-
- ! Solve the tridiagonal system
- call xp2_xpyp_solve( xp2_xpyp_up2_vp2, 2, & ! Intent(in)
- uv_rhs, lhs, & ! Intent(inout)
- uv_solution, err_code_array(4) ) ! Intent(out)
-
- up2(1:gr%nz) = uv_solution(1:gr%nz,1)
- vp2(1:gr%nz) = uv_solution(1:gr%nz,2)
-
- if ( l_stats_samp ) then
- call xp2_xpyp_implicit_stats( xp2_xpyp_up2, up2 ) ! Intent(in)
- call xp2_xpyp_implicit_stats( xp2_xpyp_vp2, vp2 ) ! Intent(in)
- end if
-
-
- ! Apply the positive definite scheme to variances
- if ( l_hole_fill ) then
- call pos_definite_variances( xp2_xpyp_rtp2, dt, rt_tol**2, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- rtp2 ) ! Intent(inout)
- call pos_definite_variances( xp2_xpyp_thlp2, dt, thl_tol**2, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- thlp2 ) ! Intent(inout)
- call pos_definite_variances( xp2_xpyp_up2, dt, w_tol_sqd, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- up2 ) ! Intent(inout)
- call pos_definite_variances( xp2_xpyp_vp2, dt, w_tol_sqd, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- vp2 ) ! Intent(inout)
- endif
-
-
- ! Clipping for r_t'^2
-
- !threshold = zero_threshold
- !
- !where ( wp2 >= w_tol_sqd ) &
- ! threshold = rt_tol*rt_tol
-
- threshold = rt_tol**2
-
- call clip_variance( xp2_xpyp_rtp2, dt, threshold, & ! Intent(in)
- rtp2 ) ! Intent(inout)
-
- ! Special clipping on the variance of rt to prevent a large variance at
- ! higher altitudes. This is done because we don't want the PDF to extend
- ! into the negative, and found that for latin hypercube sampling a large
- ! variance aloft leads to negative samples of total water.
- ! -dschanen 8 Dec 2010
- if ( l_clip_large_rtp2 ) then
-
- ! This overwrites stats clipping data from clip_variance
- if ( l_stats_samp ) then
- call stat_modify( irtp2_cl, -rtp2 / real( dt, kind = core_rknd ), zm )
- endif
-
- do k = 1, gr%nz
- threshold = rtp2_clip_coef * rtm(k)**2
- if ( rtp2(k) > threshold ) then
- rtp2(k) = threshold
- end if
- end do ! k = 1..gr%nz
-
- if ( l_stats_samp ) then
- call stat_modify( irtp2_cl, rtp2 / real( dt, kind = core_rknd ), zm )
- endif
-
- end if ! l_clip_large_rtp2
-
-
-
- ! Clipping for th_l'^2
-
- !threshold = zero_threshold
- !
- !where ( wp2 >= w_tol_sqd ) &
- ! threshold = thl_tol*thl_tol
-
- threshold = thl_tol**2
-
- call clip_variance( xp2_xpyp_thlp2, dt, threshold, & ! Intent(in)
- thlp2 ) ! Intent(inout)
-
-
- ! Clipping for u'^2
-
- !threshold = zero_threshold
- threshold = w_tol_sqd
-
- call clip_variance( xp2_xpyp_up2, dt, threshold, & ! Intent(in)
- up2 ) ! Intent(inout)
-
-
- ! Clipping for v'^2
-
- !threshold = zero_threshold
- threshold = w_tol_sqd
-
- call clip_variance( xp2_xpyp_vp2, dt, threshold, & ! Intent(in)
- vp2 ) ! Intent(inout)
-
-
- ! Clipping for r_t'th_l'
- ! Clipping r_t'th_l' at each vertical level, based on the
- ! correlation of r_t and th_l at each vertical level, such that:
- ! corr_(r_t,th_l) = r_t'th_l' / [ sqrt(r_t'^2) * sqrt(th_l'^2) ];
- ! -1 <= corr_(r_t,th_l) <= 1.
- ! Since r_t'^2, th_l'^2, and r_t'th_l' are all computed in the
- ! same place, clipping for r_t'th_l' only has to be done once.
- l_first_clip_ts = .true.
- l_last_clip_ts = .true.
- call clip_covar( xp2_xpyp_rtpthlp, l_first_clip_ts, & ! Intent(in)
- l_last_clip_ts, dt, rtp2, thlp2, & ! Intent(in)
- rtpthlp, rtpthlp_chnge ) ! Intent(inout)
-
- if ( l_scalar_calc ) then
-
- ! Implicit contributions to passive scalars
-
- !!!!!***** sclr'^2, sclr'r_t', sclr'th_l' *****!!!!!
-
- call xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, & ! Intent(in)
- a1, a1_zt, tau_zm, wm_zm, Kw2, & ! Intent(in)
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, & ! Intent(in)
- C2sclr_1d, nu2_vert_res_dep, beta, & ! Intent(in)
- lhs ) ! Intent(out)
-
-
- ! Explicit contributions to passive scalars
-
- do i = 1, sclr_dim, 1
-
- ! Interpolate w'sclr' from momentum levels to thermodynamic
- ! levels. These will be used for the turbulent advection (ta)
- ! terms in each equation.
- wpsclrp_zt = zm2zt( wpsclrp(:,i) )
-
- ! Forcing for .
- sclrp2_forcing = zero
-
- !!!!!***** sclr'^2 *****!!!!!
-
- call xp2_xpyp_rhs( xp2_xpyp_sclrp2, dt, l_iter, a1, a1_zt, & ! In
- wp2_zt, wpsclrp(:,i), wpsclrp_zt, wp3_on_wp2, & ! In
- wp3_on_wp2_zt, wpsclrp(:,i), wpsclrp_zt, & ! In
- sclrm(:,i), sclrm(:,i), sclrp2(:,i), sclrp2_forcing, & ! In
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! In
- C2sclr_1d, tau_zm, sclr_tol(i)**2, beta, & ! In
- sclr_rhs(:,i) ) ! Out
-
-
- !!!!!***** sclr'r_t' *****!!!!!
- if ( i == iisclr_rt ) then
- ! In this case we're trying to emulate rt'^2 with sclr'rt', so we
- ! handle this as we would a variance, even though generally speaking
- ! the scalar is not rt
- sclrprtp_forcing = rtp2_forcing
- threshold = rt_tol**2
- else
- sclrprtp_forcing = zero
- threshold = zero_threshold
- endif
-
- call xp2_xpyp_rhs( xp2_xpyp_sclrprtp, dt, l_iter, a1, a1_zt, & ! In
- wp2_zt, wpsclrp(:,i), wpsclrp_zt, wp3_on_wp2, & ! In
- wp3_on_wp2_zt, wprtp, wprtp_zt, & ! In
- sclrm(:,i), rtm, sclrprtp(:,i), sclrprtp_forcing, & ! In
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! In
- C2sclr_1d, tau_zm, threshold, beta, & ! In
- sclr_rhs(:,i+sclr_dim) ) ! Out
-
-
- !!!!!***** sclr'th_l' *****!!!!!
-
- if ( i == iisclr_thl ) then
- ! In this case we're trying to emulate thl'^2 with sclr'thl', so we
- ! handle this as we did with sclr_rt, above.
- sclrpthlp_forcing = thlp2_forcing
- threshold = thl_tol**2
- else
- sclrpthlp_forcing = zero
- threshold = zero_threshold
- endif
-
- call xp2_xpyp_rhs( xp2_xpyp_sclrpthlp, dt, l_iter, a1, a1_zt, & ! In
- wp2_zt, wpsclrp(:,i), wpsclrp_zt, wp3_on_wp2, & ! In
- wp3_on_wp2_zt, wpthlp, wpthlp_zt, & ! In
- sclrm(:,i), thlm, sclrpthlp(:,i), sclrpthlp_forcing, & ! In
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! In
- C2sclr_1d, tau_zm, threshold, beta, & ! In
- sclr_rhs(:,i+2*sclr_dim) ) ! Out
-
-
- enddo ! 1..sclr_dim
-
-
- ! Solve the tridiagonal system
-
- call xp2_xpyp_solve( xp2_xpyp_scalars, 3*sclr_dim, & ! Intent(in)
- sclr_rhs, lhs, sclr_solution, & ! Intent(inout)
- err_code_array(6) ) ! Intent(out)
-
- sclrp2(:,1:sclr_dim) = sclr_solution(:,1:sclr_dim)
-
- sclrprtp(:,1:sclr_dim) = sclr_solution(:,sclr_dim+1:2*sclr_dim)
-
- sclrpthlp(:,1:sclr_dim) = sclr_solution(:,2*sclr_dim+1:3*sclr_dim)
-
- ! Apply hole filling algorithm to the scalar variance terms
- if ( l_hole_fill ) then
- do i = 1, sclr_dim, 1
- call pos_definite_variances( xp2_xpyp_sclrp2, dt, sclr_tol(i)**2, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- sclrp2(:,i) ) ! Intent(inout)
- if ( i == iisclr_rt ) then
- ! Here again, we do this kluge here to make sclr'rt' == rt'^2
- call pos_definite_variances( xp2_xpyp_sclrprtp, dt, sclr_tol(i)**2, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- sclrprtp(:,i) ) ! Intent(inout)
- end if
- if ( i == iisclr_thl ) then
- ! As with sclr'rt' above, but for sclr'thl'
- call pos_definite_variances( xp2_xpyp_sclrpthlp, dt, sclr_tol(i)**2, & ! Intent(in)
- rho_ds_zm, rho_ds_zt, & ! Intent(in)
- sclrpthlp(:,i) ) ! Intent(inout)
- end if
- enddo
- endif
-
-
- ! Clipping for sclr'^2
- do i = 1, sclr_dim, 1
-
-! threshold = zero_threshold
-!
-! where ( wp2 >= w_tol_sqd ) &
-! threshold = sclr_tol(i)*sclr_tol(i)
-
- threshold = sclr_tol(i)**2
-
- call clip_variance( clip_sclrp2, dt, threshold, & ! Intent(in)
- sclrp2(:,i) ) ! Intent(inout)
-
- enddo
-
-
- ! Clipping for sclr'r_t'
- ! Clipping sclr'r_t' at each vertical level, based on the
- ! correlation of sclr and r_t at each vertical level, such that:
- ! corr_(sclr,r_t) = sclr'r_t' / [ sqrt(sclr'^2) * sqrt(r_t'^2) ];
- ! -1 <= corr_(sclr,r_t) <= 1.
- ! Since sclr'^2, r_t'^2, and sclr'r_t' are all computed in the
- ! same place, clipping for sclr'r_t' only has to be done once.
- do i = 1, sclr_dim, 1
-
- if ( i == iisclr_rt ) then
- ! Treat this like a variance if we're emulating rt
- threshold = sclr_tol(i) * rt_tol
-
- call clip_variance( clip_sclrprtp, dt, threshold, & ! Intent(in)
- sclrprtp(:,i) ) ! Intent(inout)
- else
- l_first_clip_ts = .true.
- l_last_clip_ts = .true.
- call clip_covar( clip_sclrprtp, l_first_clip_ts, & ! Intent(in)
- l_last_clip_ts, dt, sclrp2(:,i), rtp2(:), & ! Intent(in)
- sclrprtp(:,i), sclrprtp_chnge(:,i) ) ! Intent(inout)
- end if
- enddo
-
-
- ! Clipping for sclr'th_l'
- ! Clipping sclr'th_l' at each vertical level, based on the
- ! correlation of sclr and th_l at each vertical level, such that:
- ! corr_(sclr,th_l) = sclr'th_l' / [ sqrt(sclr'^2) * sqrt(th_l'^2) ];
- ! -1 <= corr_(sclr,th_l) <= 1.
- ! Since sclr'^2, th_l'^2, and sclr'th_l' are all computed in the
- ! same place, clipping for sclr'th_l' only has to be done once.
- do i = 1, sclr_dim, 1
- if ( i == iisclr_thl ) then
- ! As above, but for thl
- threshold = sclr_tol(i) * thl_tol
- call clip_variance( clip_sclrpthlp, dt, threshold, & ! Intent(in)
- sclrpthlp(:,i) ) ! Intent(inout)
- else
- l_first_clip_ts = .true.
- l_last_clip_ts = .true.
- call clip_covar( clip_sclrpthlp, l_first_clip_ts, & ! Intent(in)
- l_last_clip_ts, dt, sclrp2(:,i), thlp2(:), & ! Intent(in)
- sclrpthlp(:,i), sclrpthlp_chnge(:,i) ) ! Intent(inout)
- end if
- enddo
-
- endif ! l_scalar_calc
-
-
- ! Check for singular matrices and bad LAPACK arguments
- if ( any( fatal_error( err_code_array ) ) ) then
- err_code = clubb_singular_matrix
- end if
-
- if ( fatal_error( err_code ) .and. &
- clubb_at_least_debug_level( 1 ) ) then
-
- write(fstderr,*) "Error in advance_xp2_xpyp"
-
- write(fstderr,*) "Intent(in)"
-
- write(fstderr,*) "tau_zm = ", tau_zm
- write(fstderr,*) "wm_zm = ", wm_zm
- write(fstderr,*) "rtm = ", rtm
- write(fstderr,*) "wprtp = ", wprtp
- write(fstderr,*) "thlm = ", thlm
- write(fstderr,*) "wpthlp = ", wpthlp
- write(fstderr,*) "wpthvp = ", wpthvp
- write(fstderr,*) "um = ", um
- write(fstderr,*) "vm = ", vm
- write(fstderr,*) "wp2 = ", wp2
- write(fstderr,*) "wp3 = ", wp3
- write(fstderr,*) "upwp = ", upwp
- write(fstderr,*) "vpwp = ", vpwp
- write(fstderr,*) "sigma_sqd_w = ", sigma_sqd_w
- write(fstderr,*) "Skw_zm = ", Skw_zm
- write(fstderr,*) "Kh_zt = ", Kh_zt
- write(fstderr,*) "rtp2_forcing = ", rtp2_forcing
- write(fstderr,*) "thlp2_forcing = ", thlp2_forcing
- write(fstderr,*) "rtpthlp_forcing = ", rtpthlp_forcing
- write(fstderr,*) "rho_ds_zm = ", rho_ds_zm
- write(fstderr,*) "rho_ds_zt = ", rho_ds_zt
- write(fstderr,*) "invrs_rho_ds_zm = ", invrs_rho_ds_zm
- write(fstderr,*) "thv_ds_zm = ", thv_ds_zm
- write(fstderr,*) "wp2_zt = ", wp2_zt
-
- do i = 1, sclr_dim
- write(fstderr,*) "sclrm = ", i, sclrm(:,i)
- write(fstderr,*) "wpsclrp = ", i, wpsclrp(:,i)
- enddo
-
- write(fstderr,*) "Intent(In/Out)"
-
- write(fstderr,*) "rtp2 = ", rtp2
- write(fstderr,*) "thlp2 = ", thlp2
- write(fstderr,*) "rtpthlp = ", rtpthlp
- write(fstderr,*) "up2 = ", up2
- write(fstderr,*) "vp2 = ", vp2
-
- do i = 1, sclr_dim
- write(fstderr,*) "sclrp2 = ", i, sclrp2(:,i)
- write(fstderr,*) "sclrprtp = ", i, sclrprtp(:,i)
- write(fstderr,*) "sclrthlp = ", i, sclrpthlp(:,i)
- enddo
-
- endif
-
- return
- end subroutine advance_xp2_xpyp
-
- !=============================================================================
- subroutine xp2_xpyp_lhs( dt, l_iter, wp3_on_wp2_zt, wp3_on_wp2, &
- a1, a1_zt, tau_zm, wm_zm, Kw, &
- rho_ds_zt, rho_ds_zm, invrs_rho_ds_zm, &
- Cn, nu, beta, lhs )
-
- ! Description:
- ! Compute LHS tridiagonal matrix for a variance or covariance term
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- gamma_over_implicit_ts, & ! Constant(s)
- one, &
- zero
-
- use crmx_model_flags, only: &
- l_upwind_xpyp_ta ! Constant(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_diffusion, only: &
- diffusion_zm_lhs ! Procedure(s)
-
- use crmx_mean_adv, only: &
- term_ma_zm_lhs ! Procedure(s)
-
- use crmx_stats_variables, only: &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr10, &
- l_stats_samp, &
- irtp2_ma, &
- irtp2_ta, &
- irtp2_dp1, &
- irtp2_dp2, &
- ithlp2_ma, &
- ithlp2_ta, &
- ithlp2_dp1, &
- ithlp2_dp2, &
- irtpthlp_ma, &
- irtpthlp_ta, &
- irtpthlp_dp1, &
- irtpthlp_dp2, &
- iup2_ma, &
- iup2_ta, &
- iup2_dp2, &
- ivp2_ma, &
- ivp2_ta, &
- ivp2_dp2
-
- use crmx_advance_helper_module, only: set_boundary_conditions_lhs
-
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep length [s]
-
- logical, intent(in) :: &
- l_iter ! Whether the variances are prognostic (T/F)
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp3_on_wp2, & ! Smoothed w'^3 / w'^2 (moment. levels) [m/s]
- wp3_on_wp2_zt, & ! Smoothed w'^3 / w'^2 (thermo. levels) [m/s]
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- wm_zm, & ! w wind component on momentum levels [m/s]
- Kw, & ! Coefficient of eddy diffusivity (all vars.) [m^2/s]
- rho_ds_zt, & ! Dry, static density on thermodynamic levels [kg/m^3]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density on momentum levs. [m^3/kg]
- Cn ! Coefficient C_n [-]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- nu ! Background constant coef. of eddy diff. [-]
- real( kind = core_rknd ), intent(in) :: &
- beta ! Constant model parameter beta [-]
-
- ! Output Variables
- real( kind = core_rknd ), dimension(3,gr%nz), intent(out) :: &
- lhs ! Implicit contributions to the term
-
- ! Local Variables
-
- ! Array indices
- integer :: k, kp1, km1, low_bound, high_bound
-
- real( kind = core_rknd ), dimension(3) :: &
- tmp
-
- ! Initialize LHS matrix to 0.
- lhs = zero
-
- ! Setup LHS of the tridiagonal system
- do k = 2, gr%nz-1, 1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! LHS mean advection (ma) term.
- lhs(kp1_mdiag:km1_mdiag,k) &
- = lhs(kp1_mdiag:km1_mdiag,k) &
- + term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
-
- ! LHS turbulent advection (ta) term.
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! The weight of the implicit portion of this term is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (x'^2 or x'y' in this case), y(t) is the linearized
- ! portion of the term that gets treated implicitly, and RHS is the
- ! portion of the term that is always treated explicitly. A weight
- ! of greater than 1 can be applied to make the term more
- ! numerically stable.
- if ( .not. l_upwind_xpyp_ta ) then
- lhs(kp1_mdiag:km1_mdiag,k) &
- = lhs(kp1_mdiag:km1_mdiag,k) &
- + gamma_over_implicit_ts &
- * term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else
- lhs(kp1_mdiag:km1_mdiag,k) &
- = lhs(kp1_mdiag:km1_mdiag,k) &
- + gamma_over_implicit_ts &
- * term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- end if
-
- ! LHS dissipation term 1 (dp1)
- ! (combined with pressure term 1 (pr1) for u'^2 and v'^2).
- ! Note: An "over-implicit" weighted time step is applied to this term
- ! (and to pressure term 1 for u'^2 and v'^2).
- lhs(k_mdiag,k) &
- = lhs(k_mdiag,k) &
- + gamma_over_implicit_ts &
- * term_dp1_lhs( Cn(k), tau_zm(k) )
-
- ! LHS eddy diffusion term: dissipation term 2 (dp2).
- lhs(kp1_mdiag:km1_mdiag,k) &
- = lhs(kp1_mdiag:km1_mdiag,k) &
- + diffusion_zm_lhs( Kw(k), Kw(kp1), nu, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
-
- ! LHS time tendency.
- if ( l_iter ) then
- lhs(k_mdiag,k) = lhs(k_mdiag,k) + ( one / real( dt, kind = core_rknd ) )
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: implicit contributions for rtp2, thlp2,
- ! rtpthlp, up2, or vp2.
-
- if ( irtp2_dp1 + ithlp2_dp1 + irtpthlp_dp1 > 0 ) then
- ! Note: The statistical implicit contribution to term dp1
- ! (as well as to term pr1) for up2 and vp2 is recorded
- ! in xp2_xpyp_uv_rhs because up2 and vp2 use a special
- ! dp1/pr1 combined term.
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note above for
- ! LHS turbulent advection (ta) term).
- tmp(1) &
- = gamma_over_implicit_ts &
- * term_dp1_lhs( Cn(k), tau_zm(k) )
- zmscr01(k) = -tmp(1)
- endif
-
- if ( irtp2_dp2 + ithlp2_dp2 + irtpthlp_dp2 + &
- iup2_dp2 + ivp2_dp2 > 0 ) then
- tmp(1:3) &
- = diffusion_zm_lhs( Kw(k), Kw(kp1), nu, &
- gr%invrs_dzt(kp1), gr%invrs_dzt(k), &
- gr%invrs_dzm(k), k )
- zmscr02(k) = -tmp(3)
- zmscr03(k) = -tmp(2)
- zmscr04(k) = -tmp(1)
- endif
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for LHS turbulent
- ! advection (ta) term).
- if ( irtp2_ta + ithlp2_ta + irtpthlp_ta + &
- iup2_ta + ivp2_ta > 0 ) then
- if ( .not. l_upwind_xpyp_ta ) then
- tmp(1:3) &
- = gamma_over_implicit_ts &
- * term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else
- tmp(1:3) &
- = gamma_over_implicit_ts &
- * term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- end if
-
- zmscr05(k) = -tmp(3)
- zmscr06(k) = -tmp(2)
- zmscr07(k) = -tmp(1)
- endif
-
- if ( irtp2_ma + ithlp2_ma + irtpthlp_ma + &
- iup2_ma + ivp2_ma > 0 ) then
- tmp(1:3) &
- = term_ma_zm_lhs( wm_zm(k), gr%invrs_dzm(k), k )
- zmscr08(k) = -tmp(3)
- zmscr09(k) = -tmp(2)
- zmscr10(k) = -tmp(1)
- endif
-
- endif ! l_stats_samp
-
- enddo ! k=2..gr%nz-1
-
-
- ! Boundary Conditions
- ! These are set so that the surface_varnce value of the variances and
- ! covariances can be used at the lowest boundary and the values of those
- ! variables can be set to their respective threshold minimum values at the
- ! top boundary. Fixed-point boundary conditions are used for both the
- ! variances and the covariances.
- low_bound = 1
- high_bound = gr%nz
-
- call set_boundary_conditions_lhs( k_mdiag, low_bound, high_bound, lhs )
-
- return
-
- end subroutine xp2_xpyp_lhs
-
- !=============================================================================
- subroutine xp2_xpyp_solve( solve_type, nrhs, rhs, lhs, xapxbp, err_code )
-
- ! Description:
- ! Solve a tridiagonal system
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- one ! Constant(s)
-
- use crmx_lapack_wrap, only: &
- tridag_solve, & ! Variable(s)
- tridag_solvex !, &
-! band_solve
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_stats_type, only: &
- stat_update_var_pt ! Procedure(s)
-
- use crmx_stats_variables, only: &
- sfc, & ! Derived type
- irtp2_matrix_condt_num, & ! Stat index Variables
- ithlp2_matrix_condt_num, &
- irtpthlp_matrix_condt_num, &
- iup2_vp2_matrix_condt_num, &
- l_stats_samp ! Logical
-
- use crmx_error_code, only: &
- clubb_no_error ! Constant
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: trim
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- ! Input variables
- integer, intent(in) :: &
- nrhs ! Number of right hand side vectors
-
- integer, intent(in) :: &
- solve_type ! Variable(s) description
-
- ! Input/Ouput variables
- real( kind = core_rknd ), dimension(gr%nz,nrhs), intent(inout) :: &
- rhs ! Explicit contributions to x variance/covariance term [units vary]
-
- real( kind = core_rknd ), dimension(3,gr%nz), intent(inout) :: &
- lhs ! Implicit contributions to x variance/covariance term [units vary]
-
- ! Output Variables
- real( kind = core_rknd ), dimension(gr%nz,nrhs), intent(out) :: &
- xapxbp ! Computed value of the variable(s) at [units vary]
-
- integer, intent(out) :: &
- err_code ! Returns an error code in the event of a singular matrix
-
- ! Local variables
- real( kind = core_rknd ) :: rcond ! Est. of the reciprocal of the condition # on the matrix
-
- integer :: ixapxbp_matrix_condt_num ! Stat index
-
- character(len=10) :: &
- solve_type_str ! solve_type in string format for debug output purposes
-
- ! --- Begin Code ---
- err_code = clubb_no_error ! Initialize to the value for no errors
-
- select case ( solve_type )
- !------------------------------------------------------------------------
- ! Note that these are diagnostics from inverting the matrix, not a budget
- !------------------------------------------------------------------------
- case ( xp2_xpyp_rtp2 )
- ixapxbp_matrix_condt_num = irtp2_matrix_condt_num
- solve_type_str = "rtp2"
- case ( xp2_xpyp_thlp2 )
- ixapxbp_matrix_condt_num = ithlp2_matrix_condt_num
- solve_type_str = "thlp2"
- case ( xp2_xpyp_rtpthlp )
- ixapxbp_matrix_condt_num = irtpthlp_matrix_condt_num
- solve_type_str = "rtpthlp"
- case ( xp2_xpyp_up2_vp2 )
- ixapxbp_matrix_condt_num = iup2_vp2_matrix_condt_num
- solve_type_str = "up2_vp2"
- case default
- ! No condition number is setup for the passive scalars
- ixapxbp_matrix_condt_num = 0
- solve_type_str = "scalar"
- end select
-
- if ( l_stats_samp .and. ixapxbp_matrix_condt_num > 0 ) then
- call tridag_solvex &
- ( solve_type_str, gr%nz, nrhs, & ! Intent(in)
- lhs(kp1_mdiag,:), lhs(k_mdiag,:), lhs(km1_mdiag,:), rhs(:,1:nrhs), & ! Intent(inout)
- xapxbp(:,1:nrhs), rcond, err_code ) ! Intent(out)
-
- ! Est. of the condition number of the variance LHS matrix
- call stat_update_var_pt( ixapxbp_matrix_condt_num, 1, one / rcond, & ! Intent(in)
- sfc ) ! Intent(inout)
-
- else
- call tridag_solve &
- ( solve_type_str, gr%nz, nrhs, lhs(kp1_mdiag,:), & ! Intent(in)
- lhs(k_mdiag,:), lhs(km1_mdiag,:), rhs(:,1:nrhs), & ! Intent(inout)
- xapxbp(:,1:nrhs), err_code ) ! Intent(out)
- end if
-
- return
- end subroutine xp2_xpyp_solve
-
- !=============================================================================
- subroutine xp2_xpyp_implicit_stats( solve_type, xapxbp )
-
- ! Description:
- ! Finalize implicit contributions for r_t'^2, th_l'^2, r_t'th_l',
- ! u'^2, and v'^2.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Derived type variable
-
- use crmx_stats_type, only: &
- stat_end_update_pt, & ! Procedure(s)
- stat_update_var_pt
-
- use crmx_stats_variables, only: &
- zm, & ! Variable(s)
- irtp2_dp1, &
- irtp2_dp2, &
- irtp2_ta, &
- irtp2_ma, &
- ithlp2_dp1, &
- ithlp2_dp2, &
- ithlp2_ta, &
- ithlp2_ma, &
- irtpthlp_dp1, &
- irtpthlp_dp2, &
- irtpthlp_ta, &
- irtpthlp_ma, &
- iup2_dp1, &
- iup2_dp2, &
- iup2_ta, &
- iup2_ma, &
- iup2_pr1, &
- ivp2_dp1
-
- use crmx_stats_variables, only: &
- ivp2_dp2, &
- ivp2_ta, &
- ivp2_ma, &
- ivp2_pr1, &
- zmscr01, &
- zmscr02, &
- zmscr03, &
- zmscr04, &
- zmscr05, &
- zmscr06, &
- zmscr07, &
- zmscr08, &
- zmscr09, &
- zmscr10, &
- zmscr11
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: max, min, trim
-
- ! Input variables
- integer, intent(in) :: &
- solve_type ! Variable(s) description
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- xapxbp ! Computed value of the variable at [units vary]
-
- ! Local variables
- integer :: k, kp1, km1 ! Array indices
-
- ! Budget indices
- integer :: &
- ixapxbp_dp1, &
- ixapxbp_dp2, &
- ixapxbp_ta, &
- ixapxbp_ma, &
- ixapxbp_pr1
-
- ! --- Begin Code ---
-
- select case ( solve_type )
- case ( xp2_xpyp_rtp2 )
- ixapxbp_dp1 = irtp2_dp1
- ixapxbp_dp2 = irtp2_dp2
- ixapxbp_ta = irtp2_ta
- ixapxbp_ma = irtp2_ma
- ixapxbp_pr1 = 0
-
- case ( xp2_xpyp_thlp2 )
- ixapxbp_dp1 = ithlp2_dp1
- ixapxbp_dp2 = ithlp2_dp2
- ixapxbp_ta = ithlp2_ta
- ixapxbp_ma = ithlp2_ma
- ixapxbp_pr1 = 0
-
- case ( xp2_xpyp_rtpthlp )
- ixapxbp_dp1 = irtpthlp_dp1
- ixapxbp_dp2 = irtpthlp_dp2
- ixapxbp_ta = irtpthlp_ta
- ixapxbp_ma = irtpthlp_ma
- ixapxbp_pr1 = 0
-
- case ( xp2_xpyp_up2 )
- ixapxbp_dp1 = iup2_dp1
- ixapxbp_dp2 = iup2_dp2
- ixapxbp_ta = iup2_ta
- ixapxbp_ma = iup2_ma
- ixapxbp_pr1 = iup2_pr1
-
- case ( xp2_xpyp_vp2 )
- ixapxbp_dp1 = ivp2_dp1
- ixapxbp_dp2 = ivp2_dp2
- ixapxbp_ta = ivp2_ta
- ixapxbp_ma = ivp2_ma
- ixapxbp_pr1 = ivp2_pr1
-
- case default ! No budgets are setup for the passive scalars
- ixapxbp_dp1 = 0
- ixapxbp_dp2 = 0
- ixapxbp_ta = 0
- ixapxbp_ma = 0
- ixapxbp_pr1 = 0
-
- end select
-
- do k = 2, gr%nz-1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! x'y' term dp1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( ixapxbp_dp1, k, & ! Intent(in)
- zmscr01(k) * xapxbp(k), & ! Intent(in)
- zm ) ! Intent(inout)
-
- ! x'y' term dp2 is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_dp2, k, & ! Intent(in)
- zmscr02(k) * xapxbp(km1) & ! Intent(in)
- + zmscr03(k) * xapxbp(k) &
- + zmscr04(k) * xapxbp(kp1), &
- zm ) ! Intent(inout)
-
- ! x'y' term ta has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( ixapxbp_ta, k, & ! Intent(in)
- zmscr05(k) * xapxbp(km1) & ! Intent(in)
- + zmscr06(k) * xapxbp(k) &
- + zmscr07(k) * xapxbp(kp1), &
- zm ) ! Intent(inout)
-
- ! x'y' term ma is completely implicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_ma, k, & ! Intent(in)
- zmscr08(k) * xapxbp(km1) & ! Intent(in)
- + zmscr09(k) * xapxbp(k) &
- + zmscr10(k) * xapxbp(kp1), &
- zm ) ! Intent(inout)
-
- ! x'y' term pr1 has both implicit and explicit components;
- ! call stat_end_update_pt.
- call stat_end_update_pt( ixapxbp_pr1, k, & ! Intent(in)
- zmscr11(k) * xapxbp(k), & ! Intent(in)
- zm ) ! Intent(inout)
-
- end do ! k=2..gr%nz-1
-
- return
- end subroutine xp2_xpyp_implicit_stats
-
- !=============================================================================
- subroutine xp2_xpyp_uv_rhs( solve_type, dt, l_iter, a1, a1_zt, wp2, &
- wp2_zt, wpthvp, Lscale, wp3_on_wp2_zt, &
- wp3_on_wp2, C4_C14_1d, tau_zm, &
- xam, xbm, wpxap, wpxap_zt, wpxbp, wpxbp_zt, &
- xap2, xbp2, rho_ds_zt, invrs_rho_ds_zm, &
- rho_ds_zm, &
- thv_ds_zm, C4, C5, C14, beta, &
- rhs )
-
- ! Description:
- ! Explicit contributions to u'^2 or v'^2
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- gamma_over_implicit_ts, & ! Constant(s)
- w_tol_sqd, &
- one, &
- two_thirds, &
- one_third, &
- zero
-
- use crmx_model_flags, only: &
- l_upwind_xpyp_ta ! Constant(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update_pt, & ! Procedure(s)
- stat_update_var_pt, &
- stat_modify_pt
-
- use crmx_stats_variables, only: &
- ivp2_ta, & ! Variable(s)
- ivp2_tp, &
- ivp2_dp1, &
- ivp2_pr1, &
- ivp2_pr2, &
- iup2_ta, &
- iup2_tp, &
- iup2_dp1, &
- iup2_pr1, &
- iup2_pr2, &
- zm, &
- zmscr01, &
- zmscr11, &
- l_stats_samp
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: solve_type
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- logical, intent(in) :: &
- l_iter ! Whether x is prognostic (T/F)
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp2_zt, & ! w'^2 interpolated to thermodynamic levels [m^2/s^2]
- wpthvp, & ! w'th_v' (momentum levels) [K m/s]
- Lscale, & ! Mixing Length [m]
- wp3_on_wp2, & ! Smoothed w'^3 / w'^2 on momentum levels [m/s]
- wp3_on_wp2_zt, & ! Smoothed w'^3 / w'^2 on thermo. levels [m/s]
- C4_C14_1d, & ! Combination of model params. C_4 and C_14 [-]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- xam, & ! x_am (thermodynamic levels) [m/s]
- xbm, & ! x_bm (thermodynamic levels) [m/s]
- wpxap, & ! w'x_a' (momentum levels) [m^2/s^2]
- wpxap_zt, & ! w'x_a' interpolated to thermodynamic levels [m^2/s^2]
- wpxbp, & ! w'x_b' (momentum levels) [m^2/s^2]
- wpxbp_zt, & ! w'x_b' interpolated to thermodynamic levels [m^2/s^2]
- xap2, & ! x_a'^2 (momentum levels) [m^2/s^2]
- xbp2, & ! x_b'^2 (momentum levels) [m^2/s^2]
- rho_ds_zt, & ! Dry, static density on thermodynamic levels [kg/m^3]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density on momentum levs. [m^3/kg]
- thv_ds_zm ! Dry, base-state theta_v on momentum levels [K]
-
- real( kind = core_rknd ), intent(in) :: &
- C4, & ! Model parameter C_4 [-]
- C5, & ! Model parameter C_5 [-]
- C14, & ! Model parameter C_{14} [-]
- beta ! Model parameter beta [-]
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz,1), intent(out) :: &
- rhs ! Explicit contributions to x variance/covariance terms
-
- ! Local Variables
-
- ! Array indices
- integer :: k, kp1, km1
-
- ! For "over-implicit" weighted time step.
- ! This vector holds output from the LHS (implicit) portion of a term at a
- ! given vertical level. This output is weighted and applied to the RHS.
- ! This is used if the implicit portion of the term is "over-implicit", which
- ! means that the LHS contribution is given extra weight (>1) in order to
- ! increase numerical stability. A weighted factor must then be applied to
- ! the RHS in order to balance the weight.
- real( kind = core_rknd ), dimension(3) :: lhs_fnc_output
-
- real( kind = core_rknd ) :: tmp
-
- integer :: &
- ixapxbp_ta, &
- ixapxbp_tp, &
- ixapxbp_dp1, &
- ixapxbp_pr1, &
- ixapxbp_pr2
-
- !----------------------------- Begin Code ----------------------------------
-
- select case ( solve_type )
- case ( xp2_xpyp_vp2 )
- ixapxbp_ta = ivp2_ta
- ixapxbp_tp = ivp2_tp
- ixapxbp_dp1 = ivp2_dp1
- ixapxbp_pr1 = ivp2_pr1
- ixapxbp_pr2 = ivp2_pr2
- case ( xp2_xpyp_up2 )
- ixapxbp_ta = iup2_ta
- ixapxbp_tp = iup2_tp
- ixapxbp_dp1 = iup2_dp1
- ixapxbp_pr1 = iup2_pr1
- ixapxbp_pr2 = iup2_pr2
- case default ! No budgets for passive scalars
- ixapxbp_ta = 0
- ixapxbp_tp = 0
- ixapxbp_dp1 = 0
- ixapxbp_pr1 = 0
- ixapxbp_pr2 = 0
- end select
-
-
- ! Initialize RHS vector to 0.
- rhs = zero
-
- do k = 2, gr%nz-1, 1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! RHS turbulent advection (ta) term.
- rhs(k,1) &
- = rhs(k,1) &
- + term_ta_rhs( wp2_zt(kp1), wp2_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), wpxbp_zt(kp1), wpxbp_zt(k), &
- wpxap_zt(kp1), wpxap_zt(k), gr%invrs_dzm(k), beta )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS turbulent advection (ta) term.
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! The weight of the implicit portion of this term is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (x'^2 or x'y' in this case), y(t) is the linearized
- ! portion of the term that gets treated implicitly, and RHS is the
- ! portion of the term that is always treated explicitly. A weight
- ! of greater than 1 can be applied to make the term more
- ! numerically stable.
- if ( .not. l_upwind_xpyp_ta ) then
- lhs_fnc_output(1:3) &
- = term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else
- lhs_fnc_output(1:3) &
- = term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- end if
-
- rhs(k,1) &
- = rhs(k,1) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * xap2(kp1) &
- - lhs_fnc_output(2) * xap2(k) &
- - lhs_fnc_output(3) * xap2(km1) )
-
- ! RHS turbulent production (tp) term.
- rhs(k,1) &
- = rhs(k,1) &
- + ( one - C5 ) &
- * term_tp( xam(kp1), xam(k), xam(kp1), xam(k), &
- wpxap(k), wpxap(k), gr%invrs_dzm(k) )
-
- ! RHS pressure term 1 (pr1) (and dissipation term 1 (dp1)).
- rhs(k,1) &
- = rhs(k,1) &
- + term_pr1( C4, C14, xbp2(k), wp2(k), tau_zm(k) )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS dissipation term 1 (dp1) and pressure term 1 (pr1).
- !
- ! Note: An "over-implicit" weighted time step is applied to these terms.
- lhs_fnc_output(1) &
- = term_dp1_lhs( C4_C14_1d(k), tau_zm(k) )
- rhs(k,1) &
- = rhs(k,1) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * xap2(k) )
-
- ! RHS pressure term 2 (pr2).
- rhs(k,1) &
- = rhs(k,1) &
- + term_pr2( C5, thv_ds_zm(k), wpthvp(k), wpxap(k), wpxbp(k), &
- xam, xbm, gr%invrs_dzm(k), kp1, k, &
- Lscale(kp1), Lscale(k), wp2_zt(kp1), wp2_zt(k) )
-
- ! RHS time tendency.
- if ( l_iter ) then
- rhs(k,1) = rhs(k,1) + one/real( dt, kind = core_rknd ) * xap2(k)
- endif
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for up2 or vp2.
-
- ! x'y' term ta has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on term_ta_rhs.
- call stat_begin_update_pt( ixapxbp_ta, k, & ! Intent(in)
- -term_ta_rhs( wp2_zt(kp1), wp2_zt(k), & ! Intent(in)
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), wpxbp_zt(kp1), wpxbp_zt(k), &
- wpxap_zt(kp1), wpxap_zt(k), gr%invrs_dzm(k), beta ), &
- zm ) ! Intent(inout)
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) term).
- if ( .not. l_upwind_xpyp_ta ) then
- lhs_fnc_output(1:3) &
- = term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else ! turbulent advection is using an upwind discretization
- lhs_fnc_output(1:3) &
- = term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- end if ! ~l_upwind_xpyp_ta
-
- call stat_modify_pt( ixapxbp_ta, k, & ! Intent(in)
- + ( one - gamma_over_implicit_ts ) & ! Intent(in)
- * ( - lhs_fnc_output(1) * xap2(kp1) &
- - lhs_fnc_output(2) * xap2(k) &
- - lhs_fnc_output(3) * xap2(km1) ), &
- zm ) ! Intent(inout)
-
- if ( ixapxbp_dp1 > 0 ) then
- ! Note: The function term_pr1 is the explicit component of a
- ! semi-implicit solution to dp1 and pr1.
- ! Record the statistical contribution of the implicit component of
- ! term dp1 for up2 or vp2. This will overwrite anything set
- ! statistically in xp2_xpyp_lhs for this term.
- ! Note: To find the contribution of x'y' term dp1, substitute
- ! (2/3)*C_4 for the C_n input to function term_dp1_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note above for
- ! RHS turbulent advection (ta) term).
- tmp &
- = gamma_over_implicit_ts &
- * term_dp1_lhs( two_thirds*C4, tau_zm(k) )
- zmscr01(k) = -tmp
- ! Statistical contribution of the explicit component of term dp1 for
- ! up2 or vp2.
- ! x'y' term dp1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on term_pr1.
- ! Note: To find the contribution of x'y' term dp1, substitute 0 for
- ! the C_14 input to function term_pr1.
- call stat_begin_update_pt( ixapxbp_dp1, k, & ! Intent(in)
- -term_pr1( C4, zero, xbp2(k), wp2(k), tau_zm(k) ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note above for
- ! RHS turbulent advection (ta) term).
- lhs_fnc_output(1) &
- = term_dp1_lhs( two_thirds*C4, tau_zm(k) )
- call stat_modify_pt( ixapxbp_dp1, k, & ! Intent(in)
- + ( one - gamma_over_implicit_ts ) & ! Intent(in)
- * ( - lhs_fnc_output(1) * xap2(k) ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- endif
-
- if ( ixapxbp_pr1 > 0 ) then
- ! Note: The function term_pr1 is the explicit component of a
- ! semi-implicit solution to dp1 and pr1.
- ! Statistical contribution of the implicit component of term pr1 for
- ! up2 or vp2.
- ! Note: To find the contribution of x'y' term pr1, substitute
- ! (1/3)*C_14 for the C_n input to function term_dp1_lhs.
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note above for
- ! RHS turbulent advection (ta) term).
- tmp &
- = gamma_over_implicit_ts &
- * term_dp1_lhs( one_third*C14, tau_zm(k) )
- zmscr11(k) = -tmp
- ! Statistical contribution of the explicit component of term pr1 for
- ! up2 or vp2.
- ! x'y' term pr1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on term_pr1.
- ! Note: To find the contribution of x'y' term pr1, substitute 0 for
- ! the C_4 input to function term_pr1.
- call stat_begin_update_pt( ixapxbp_pr1, k, & ! Intent(in)
- -term_pr1( zero, C14, xbp2(k), wp2(k), tau_zm(k) ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- ! Note: An "over-implicit" weighted time step is applied to this
- ! term. A weighting factor of greater than 1 may be used to
- ! make the term more numerically stable (see note above for
- ! RHS turbulent advection (ta) term).
- lhs_fnc_output(1) &
- = term_dp1_lhs( one_third*C14, tau_zm(k) )
- call stat_modify_pt( ixapxbp_pr1, k, & ! Intent(in)
- + ( one - gamma_over_implicit_ts ) & ! Intent(in)
- * ( - lhs_fnc_output(1) * xap2(k) ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- endif
-
- ! x'y' term pr2 is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_pr2, k, & ! Intent(in)
- term_pr2( C5, thv_ds_zm(k), wpthvp(k), wpxap(k), wpxbp(k), & ! Intent(in)
- xam, xbm, gr%invrs_dzm(k), kp1, k, &
- Lscale(kp1), Lscale(k), wp2_zt(kp1), wp2_zt(k) ), &
- zm ) ! Intent(inout)
-
- ! x'y' term tp is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_tp, k, & ! Intent(in)
- ( one - C5 ) & ! Intent(in)
- * term_tp( xam(kp1), xam(k), xam(kp1), xam(k), &
- wpxap(k), wpxap(k), gr%invrs_dzm(k) ), &
- zm ) ! Intent(inout)
-
- endif ! l_stats_samp
-
- enddo ! k=2..gr%nz-1
-
-
- ! Boundary Conditions
- ! These are set so that the surface_varnce value of u'^2 or v'^2 can be
- ! used at the lowest boundary and the values of those variables can be
- ! set to their respective threshold minimum values at the top boundary.
- ! Fixed-point boundary conditions are used for the variances.
-
- rhs(1,1) = xap2(1)
- ! The value of u'^2 or v'^2 at the upper boundary will be set to the
- ! threshold minimum value of w_tol_sqd.
- rhs(gr%nz,1) = w_tol_sqd
-
- return
- end subroutine xp2_xpyp_uv_rhs
-
- !=============================================================================
- subroutine xp2_xpyp_rhs( solve_type, dt, l_iter, a1, a1_zt, &
- wp2_zt, wpxap, wpxap_zt, wp3_on_wp2, &
- wp3_on_wp2_zt, wpxbp, wpxbp_zt, &
- xam, xbm, xapxbp, xapxbp_forcing, &
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- Cn, tau_zm, threshold, beta, &
- rhs )
-
- ! Description:
- ! Explicit contributions to r_t'^2, th_l'^2, r_t'th_l', sclr'r_t',
- ! sclr'th_l', or sclr'^2.
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- gamma_over_implicit_ts, & ! Constant(s)
- one, &
- zero
-
- use crmx_model_flags, only: &
- l_upwind_xpyp_ta ! Constant(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update_pt, & ! Procedure(s)
- stat_update_var_pt, &
- stat_modify_pt
-
- use crmx_stats_variables, only: &
- irtp2_ta, & ! Variable(s)
- irtp2_tp, &
- irtp2_dp1, &
- irtp2_forcing, &
- ithlp2_ta, &
- ithlp2_tp, &
- ithlp2_dp1, &
- ithlp2_forcing, &
- irtpthlp_ta, &
- irtpthlp_tp1, &
- irtpthlp_tp2, &
- irtpthlp_dp1, &
- irtpthlp_forcing, &
- zm, &
- l_stats_samp
-
- use crmx_advance_helper_module, only: set_boundary_conditions_rhs
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: solve_type
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- logical, intent(in) :: &
- l_iter ! Whether x is prognostic (T/F)
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- a1, & ! sigma_sqd_w term a_1 (momentum levels) [-]
- a1_zt, & ! a_1 interpolated to thermodynamic levels [-]
- wp2_zt, & ! w'^2 interpolated to thermodynamic levels [m^2/s^2]
- wpxap, & ! w'x_a' (momentum levels) [m/s {x_am units}]
- wpxap_zt, & ! w'x_a' interpolated to thermodynamic levels [m/s {x_am units}]
- wp3_on_wp2, & ! w'^3 / w'^2 on momentum levels [m/s]
- wp3_on_wp2_zt, & ! w'^3 / w'^2 on thermodynamic levels [m/s]
- wpxbp, & ! w'x_b' (momentum levels) [m/s {x_bm units}]
- wpxbp_zt, & ! w'x_b' interpolated to thermodynamic levels [m/s {x_bm units}]
- xam, & ! x_am (thermodynamic levels) [{x_am units}]
- xbm, & ! x_bm (thermodynamic levels) [{x_bm units}]
- xapxbp, & ! x_a'x_b' (momentum levels) [{x_am units}*{x_bm units}]
- xapxbp_forcing, & ! x_a'x_b' forcing (momentum levels) [{x_am units}*{x_bm units}/s]
- rho_ds_zm, & ! Dry, static density on moment. levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density on momentum levs. [m^3/kg]
- tau_zm, & ! Time-scale tau on momentum levels [s]
- Cn ! Coefficient C_n [-]
-
- real( kind = core_rknd ), intent(in) :: &
- threshold, & ! Smallest allowable mag. value for x_a'x_b' [{x_am units}
- ! *{x_bm units}]
- beta ! Model parameter beta [-]
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz,1), intent(out) :: &
- rhs ! Explicit contributions to x variance/covariance terms
-
- ! Local Variables
-
- ! Array indices
- integer :: k, kp1, km1, k_low, k_high
-
- ! For "over-implicit" weighted time step.
- ! This vector holds output from the LHS (implicit) portion of a term at a
- ! given vertical level. This output is weighted and applied to the RHS.
- ! This is used if the implicit portion of the term is "over-implicit", which
- ! means that the LHS contribution is given extra weight (>1) in order to
- ! increase numerical stability. A weighted factor must then be applied to
- ! the RHS in order to balance the weight.
- real( kind = core_rknd ), dimension(3) :: lhs_fnc_output
-
- integer :: &
- ixapxbp_ta, &
- ixapxbp_tp, &
- ixapxbp_tp1, &
- ixapxbp_tp2, &
- ixapxbp_dp1, &
- ixapxbp_f
-
- !------------------------------ Begin Code ---------------------------------
-
- select case ( solve_type )
- case ( xp2_xpyp_rtp2 )
- ixapxbp_ta = irtp2_ta
- ixapxbp_tp = irtp2_tp
- ixapxbp_tp1 = 0
- ixapxbp_tp2 = 0
- ixapxbp_dp1 = irtp2_dp1
- ixapxbp_f = irtp2_forcing
- case ( xp2_xpyp_thlp2 )
- ixapxbp_ta = ithlp2_ta
- ixapxbp_tp = ithlp2_tp
- ixapxbp_tp1 = 0
- ixapxbp_tp2 = 0
- ixapxbp_dp1 = ithlp2_dp1
- ixapxbp_f = ithlp2_forcing
- case ( xp2_xpyp_rtpthlp )
- ixapxbp_ta = irtpthlp_ta
- ixapxbp_tp = 0
- ixapxbp_tp1 = irtpthlp_tp1
- ixapxbp_tp2 = irtpthlp_tp2
- ixapxbp_dp1 = irtpthlp_dp1
- ixapxbp_f = irtpthlp_forcing
- case default ! No budgets for passive scalars
- ixapxbp_ta = 0
- ixapxbp_tp = 0
- ixapxbp_tp1 = 0
- ixapxbp_tp2 = 0
- ixapxbp_dp1 = 0
- ixapxbp_f = 0
- end select
-
-
- ! Initialize RHS vector to 0.
- rhs = zero
-
- do k = 2, gr%nz-1, 1
-
- km1 = max( k-1, 1 )
- kp1 = min( k+1, gr%nz )
-
- ! RHS turbulent advection (ta) term.
- rhs(k,1) &
- = rhs(k,1) &
- + term_ta_rhs( wp2_zt(kp1), wp2_zt(k), &
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), wpxbp_zt(kp1), wpxbp_zt(k), &
- wpxap_zt(kp1), wpxap_zt(k), gr%invrs_dzm(k), beta )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS turbulent advection (ta) term.
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! The weight of the implicit portion of this term is controlled
- ! by the factor gamma_over_implicit_ts (abbreviated "gamma" in the
- ! expression below). A factor is added to the right-hand side of
- ! the equation in order to balance a weight that is not equal to 1,
- ! such that:
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- ! where X is the variable that is being solved for in a predictive
- ! equation (x'^2 or x'y' in this case), y(t) is the linearized
- ! portion of the term that gets treated implicitly, and RHS is the
- ! portion of the term that is always treated explicitly. A weight
- ! of greater than 1 can be applied to make the term more
- ! numerically stable.
- if ( .not. l_upwind_xpyp_ta ) then
- lhs_fnc_output(1:3) &
- = term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else
- lhs_fnc_output(1:3) &
- = term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- endif
-
- rhs(k,1) &
- = rhs(k,1) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * xapxbp(kp1) &
- - lhs_fnc_output(2) * xapxbp(k) &
- - lhs_fnc_output(3) * xapxbp(km1) )
-
- ! RHS turbulent production (tp) term.
- rhs(k,1) &
- = rhs(k,1) &
- + term_tp( xam(kp1), xam(k), xbm(kp1), xbm(k), &
- wpxbp(k), wpxap(k), gr%invrs_dzm(k) )
-
- ! RHS dissipation term 1 (dp1)
- rhs(k,1) &
- = rhs(k,1) + term_dp1_rhs( Cn(k), tau_zm(k), threshold )
-
- ! RHS contribution from "over-implicit" weighted time step
- ! for LHS dissipation term 1 (dp1).
- !
- ! Note: An "over-implicit" weighted time step is applied to this term.
- lhs_fnc_output(1) &
- = term_dp1_lhs( Cn(k), tau_zm(k) )
- rhs(k,1) &
- = rhs(k,1) &
- + ( one - gamma_over_implicit_ts ) &
- * ( - lhs_fnc_output(1) * xapxbp(k) )
-
- ! RHS time tendency.
- if ( l_iter ) then
- rhs(k,1) = rhs(k,1) + one/real( dt, kind = core_rknd ) * xapxbp(k)
- endif
-
- ! RHS forcing.
- ! Note: forcing includes the effects of microphysics on .
- rhs(k,1) = rhs(k,1) + xapxbp_forcing(k)
-
-
- if ( l_stats_samp ) then
-
- ! Statistics: explicit contributions for rtp2, thlp2, or rtpthlp.
-
- ! x'y' term ta has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on term_ta_rhs.
- call stat_begin_update_pt( ixapxbp_ta, k, & ! Intent(in)
- -term_ta_rhs( wp2_zt(kp1), wp2_zt(k), & ! Intent(in)
- wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), wpxbp_zt(kp1), wpxbp_zt(k), &
- wpxap_zt(kp1), wpxap_zt(k), gr%invrs_dzm(k), beta ), &
- zm ) ! Intent(inout)
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) term).
- if ( .not. l_upwind_xpyp_ta ) then
- lhs_fnc_output(1:3) &
- = term_ta_lhs( wp3_on_wp2_zt(kp1), wp3_on_wp2_zt(k), &
- rho_ds_zt(kp1), rho_ds_zt(k), invrs_rho_ds_zm(k), &
- a1_zt(kp1), a1(k), a1_zt(k), gr%invrs_dzm(k), beta, k )
- else
- lhs_fnc_output(1:3) &
- = term_ta_lhs_upwind( a1(k), a1(kp1), a1(km1), &
- wp3_on_wp2(kp1), wp3_on_wp2(k), wp3_on_wp2(km1), &
- gr%invrs_dzt(k), gr%invrs_dzt(kp1), &
- invrs_rho_ds_zm(k), &
- rho_ds_zm(kp1), rho_ds_zm(k), rho_ds_zm(km1), beta )
- end if
- call stat_modify_pt( ixapxbp_ta, k, & ! Intent(in)
- + ( one - gamma_over_implicit_ts ) & ! Intent(in)
- * ( - lhs_fnc_output(1) * xapxbp(kp1) &
- - lhs_fnc_output(2) * xapxbp(k) &
- - lhs_fnc_output(3) * xapxbp(km1) ), &
- zm ) ! Intent(inout)
-
- ! x'y' term dp1 has both implicit and explicit components; call
- ! stat_begin_update_pt. Since stat_begin_update_pt automatically
- ! subtracts the value sent in, reverse the sign on term_dp1_rhs.
- call stat_begin_update_pt( ixapxbp_dp1, k, & ! Intent(in)
- -term_dp1_rhs( Cn(k), tau_zm(k), threshold ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- ! Note: An "over-implicit" weighted time step is applied to this term.
- ! A weighting factor of greater than 1 may be used to make the
- ! term more numerically stable (see note above for RHS turbulent
- ! advection (ta) term).
- lhs_fnc_output(1) &
- = term_dp1_lhs( Cn(k), tau_zm(k) )
- call stat_modify_pt( ixapxbp_dp1, k, & ! Intent(in)
- + ( one - gamma_over_implicit_ts ) & ! Intent(in)
- * ( - lhs_fnc_output(1) * xapxbp(k) ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- ! rtp2/thlp2 case (1 turbulent production term)
- ! x'y' term tp is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_tp, k, & ! Intent(in)
- term_tp( xam(kp1), xam(k), xbm(kp1), xbm(k), & ! Intent(in)
- wpxbp(k), wpxap(k), gr%invrs_dzm(k) ), &
- zm ) ! Intent(inout)
-
- ! rtpthlp case (2 turbulent production terms)
- ! x'y' term tp1 is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of x'y' term tp1, substitute 0 for all
- ! the xam inputs and the wpxbp input to function term_tp.
- call stat_update_var_pt( ixapxbp_tp1, k, & ! Intent(in)
- term_tp( zero, zero, xbm(kp1), xbm(k), & ! Intent(in)
- zero, wpxap(k), gr%invrs_dzm(k) ), &
- zm ) ! Intent(inout)
-
- ! x'y' term tp2 is completely explicit; call stat_update_var_pt.
- ! Note: To find the contribution of x'y' term tp2, substitute 0 for all
- ! the xbm inputs and the wpxap input to function term_tp.
- call stat_update_var_pt( ixapxbp_tp2, k, & ! Intent(in)
- term_tp( xam(kp1), xam(k), zero, zero, & ! Intent(in)
- wpxbp(k), zero, gr%invrs_dzm(k) ), &
- zm ) ! Intent(inout)
-
- ! x'y' forcing term is completely explicit; call stat_update_var_pt.
- call stat_update_var_pt( ixapxbp_f, k, xapxbp_forcing(k), zm )
-
- endif ! l_stats_samp
-
- enddo ! k=2..gr%nz-1
-
-
- ! Boundary Conditions
- ! These are set so that the surface_varnce value of rtp2, thlp2, or rtpthlp
- ! (or sclrp2, sclrprtp, or sclrpthlp) can be used at the lowest boundary and the
- ! values of those variables can be set to their respective threshold minimum
- ! values (which is 0 in the case of the covariances) at the top boundary.
- ! Fixed-point boundary conditions are used for both the variances and the
- ! covariances.
-
- k_low = 1
- k_high = gr%nz
-
- ! The value of the field at the upper boundary will be set to it's threshold
- ! minimum value, as contained in the variable 'threshold'.
- call set_boundary_conditions_rhs( &
- xapxbp(1), k_low, threshold, k_high, &
- rhs(:,1) )
-
- return
- end subroutine xp2_xpyp_rhs
-
- !=============================================================================
- pure function term_ta_lhs( wp3_on_wp2_ztp1, wp3_on_wp2_zt, &
- rho_ds_ztp1, rho_ds_zt, invrs_rho_ds_zm, &
- a1_ztp1, a1, a1_zt, invrs_dzm, beta, level ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection of x_a'x_b': implicit portion of the code.
- !
- ! The d(x_a'x_b')/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'x_a'x_b' )/dz.
- !
- ! A substitution is made in order to close the turbulent advection term,
- ! such that:
- !
- ! w'x_a'x_b' = (1/3)*beta * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'
- ! + (1-(1/3)*beta) * (a_1)^2 * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b';
- !
- ! where a_1 is a variable that is a function of sigma_sqd_w. The turbulent
- ! advection term is rewritten as:
- !
- ! - (1/rho_ds)
- ! * d [ rho_ds * { (1/3)*beta * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'
- ! + (1-(1/3)*beta) * (a_1)^2 * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b' } ]
- ! / dz;
- !
- ! which produces an implicit and an explicit portion of this term. The
- ! implicit portion of this term is:
- !
- ! - (1/rho_ds)
- ! * d [ rho_ds * (1/3)*beta * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'(t+1) ]
- ! / dz.
- !
- ! Since (1/3)*beta is a constant, it can be pulled outside of the
- ! derivative. The implicit portion of this term becomes:
- !
- ! - (1/3)*beta/rho_ds
- ! * d [ rho_ds * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'(t+1) ] / dz.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of x_a'x_b' being used is
- ! from the next timestep, which is being advanced to in solving the
- ! d(x_a'x_b')/dt equation.
- !
- ! The implicit portion of this term is discretized as follows:
- !
- ! The values of x_a'x_b' are found on the momentum levels, as are the values
- ! of w'^2 and a_1. The values of w'^3 are found on the thermodynamic
- ! levels. Additionally, the values of rho_ds_zt are found on the
- ! thermodynamic levels, and the values of invrs_rho_ds_zm are found on the
- ! momentum levels. The variables x_a'x_b', w'^2, and a_1 are each
- ! interpolated to the intermediate thermodynamic levels. The values of the
- ! mathematical expression (called F here) within the dF/dz term are computed
- ! on the thermodynamic levels. Then the derivative (d/dz) of the
- ! expression (F) is taken over the central momentum level, where it is
- ! multiplied by (1/3)*beta and by invrs_rho_ds_zm, yielding the desired
- ! result. In this function, the values of F are as follows:
- !
- ! F = rho_ds_zt * a_1(t) * ( w'^3(t) / w'^2(t) ) * x_a'x_b'(t+1);
- !
- ! where the timestep index (t) stands for the index of the current timestep.
- !
- !
- ! ==a1p1========wp2p1========xapxbpp1================================ m(k+1)
- !
- ! ----a1(interp)--wp2(interp)--xapxbp(interp)--wp3p1---rho_ds_ztp1--- t(k+1)
- !
- ! ==a1==========wp2==========xapxbp=======dF/dz====invrs_rho_ds_zm=== m(k)
- !
- ! ----a1(interp)--wp2(interp)--xapxbp(interp)--wp3-----rho_ds_zt----- t(k)
- !
- ! ==a1m1========wp2m1========xapxbpm1================================ m(k-1)
- !
- ! The vertical indices m(k+1), t(k+1), m(k), t(k), and m(k-1) correspond
- ! with altitudes zm(k+1), zt(k+1), zm(k), zt(k), and zm(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: & ! gr%weights_zm2zt
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- one_third ! Constant(s)
-
- use crmx_model_flags, only: &
- l_standard_term_ta
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: max
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- integer, parameter :: &
- m_above = 1, & ! Index for upper momentum level grid weight.
- m_below = 2 ! Index for lower momentum level grid weight.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- wp3_on_wp2_ztp1, & ! w'^3 / w'^2 (k+1) [m/s]
- wp3_on_wp2_zt, & ! w'^3 / w'^2 (k) [m/s]
- rho_ds_ztp1, & ! Dry, static density at thermo. level (k+1) [kg/m^3]
- rho_ds_zt, & ! Dry, static density at thermo. level (k) [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum lev (k) [m^3/kg]
- a1_ztp1, & ! a_1 interpolated to thermo. level (k+1) [-]
- a1, & ! a_1(k) [-]
- a1_zt, & ! a_1 interpolated to thermo. level (k) [-]
- invrs_dzm, & ! Inverse of grid spacing [1/m]
- beta ! Model parameter [-]
-
- integer, intent(in) :: &
- level ! Central momentum level (on which calculation occurs).
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
- ! Local Variables
- integer :: &
- tkp1, & ! Thermodynamic level directly above central momentum level.
- tk ! Thermodynamic level directly below central momentum level.
-
-
- ! Thermodynamic level (k+1) is between momentum level (k+1)
- ! and momentum level (k).
- tkp1 = level + 1
-
- ! Thermodynamic level (k) is between momentum level (k)
- ! and momentum level (k-1).
- tk = level
-
- if ( l_standard_term_ta ) then
-
- ! The turbulent advection term is discretized normally, in accordance
- ! with the model equations found in the documentation and the description
- ! listed above.
-
- ! Momentum superdiagonal: [ x xapxbp(k+1,) ]
- lhs(kp1_mdiag) &
- = + one_third * beta &
- * invrs_rho_ds_zm &
- * invrs_dzm &
- * rho_ds_ztp1 * a1_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_above,tkp1)
-
- ! Momentum main diagonal: [ x xapxbp(k,) ]
- lhs(k_mdiag) &
- = + one_third * beta &
- * invrs_rho_ds_zm &
- * invrs_dzm &
- * ( rho_ds_ztp1 * a1_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_below,tkp1) &
- - rho_ds_zt * a1_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_above,tk) &
- )
-
- ! Momentum subdiagonal: [ x xapxbp(k-1,) ]
- lhs(km1_mdiag) &
- = - one_third * beta &
- * invrs_rho_ds_zm &
- * invrs_dzm &
- * rho_ds_zt * a1_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_below,tk)
-
- else
-
- ! Brian tried a new discretization for the turbulent advection term, for
- ! which the implicit portion of the term is:
- ! - (1/rho_ds)
- ! * d [ rho_ds * a_1 * (1/3)*beta * ( w'^3 / w'^2 ) * x_a'x_b' ] / dz.
- ! In order to help stabilize x_a'x_b', a_1 has been pulled outside the
- ! derivative.
-
- ! Momentum superdiagonal: [ x xapxbp(k+1,) ]
- lhs(kp1_mdiag) &
- = + one_third * beta &
- * invrs_rho_ds_zm * a1 &
- * invrs_dzm &
- * rho_ds_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_above,tkp1)
-
- ! Momentum main diagonal: [ x xapxbp(k,) ]
- lhs(k_mdiag) &
- = + one_third * beta &
- * invrs_rho_ds_zm * a1 &
- * invrs_dzm &
- * ( rho_ds_ztp1 &
- * wp3_on_wp2_ztp1 &
- * gr%weights_zm2zt(m_below,tkp1) &
- - rho_ds_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_above,tk) &
- )
-
- ! Momentum subdiagonal: [ x xapxbp(k-1,) ]
- lhs(km1_mdiag) &
- = - one_third * beta &
- * invrs_rho_ds_zm * a1 &
- * invrs_dzm &
- * rho_ds_zt &
- * wp3_on_wp2_zt &
- * gr%weights_zm2zt(m_below,tk)
-
- ! End of Brian's a1 change. 14 Feb 2008.
-
- endif
-
-
- return
- end function term_ta_lhs
-
- !-----------------------------------------------------------------------------
- pure function term_ta_lhs_upwind( a1_zm, a1_zm_p1, a1_zm_m1, &
- wp3_on_wp2_p1, wp3_on_wp2, wp3_on_wp2_m1, &
- invrs_dzt, invrs_dzt_p1, &
- invrs_rho_ds_zm, &
- rho_ds_zm_p1, rho_ds_zm, rho_ds_zm_m1, beta ) &
- result( lhs )
-
- ! Description:
- ! Turbulent advection of x_a'x_b' using an upwind differencing
- ! approximation rather than a centered difference.
- ! References:
- ! None
- !-----------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- one_third, & ! Constant(s)
- zero
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- a1_zm, & ! a_1(k) on momentum levels [-]
- a1_zm_p1, & ! a_1(k+1) on momentum levels [-]
- a1_zm_m1, & ! a_1(k-1) on momentum levels [-]
- wp3_on_wp2_p1, & ! Smoothed wp3 / wp2 on moment. levels (k+1) [m/s]
- wp3_on_wp2, & ! Smoothed wp3 / wp2 on moment. levels (k) [m/s]
- wp3_on_wp2_m1, & ! Smoothed wp3 / wp2 on moment. levels (k-1) [m/s]
- invrs_dzt, & ! Inverse of grid spacing (k) [1/m]
- invrs_dzt_p1, & ! Inverse of grid spacing (k+1) [1/m]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum lev (k) [m^3/kg]
- rho_ds_zm, & ! Density of air (k) [kg/m^3]
- rho_ds_zm_p1, & ! Density of air (k+1) [kg/m^3]
- rho_ds_zm_m1, & ! Density of air (k-1) [kg/m^3]
- beta ! Model parameter [-]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
-
- if ( wp3_on_wp2 > zero ) then
-
- ! Momentum main diagonal: [ x xapxbp(k+1,) ]
- lhs(kp1_mdiag) = zero
-
- ! Momentum main diagonal: [ x xapxbp(k,) ]
- lhs(k_mdiag) &
- = + one_third * beta &
- * invrs_dzt * invrs_rho_ds_zm &
- * rho_ds_zm * a1_zm * wp3_on_wp2
-
- ! Momentum subdiagonal: [ x xapxbp(k-1,) ]
- lhs(km1_mdiag) &
- = - one_third * beta &
- * invrs_dzt * invrs_rho_ds_zm &
- * rho_ds_zm_m1 * a1_zm_m1 * wp3_on_wp2_m1
-
- else ! "Wind" is blowing downward
-
- ! Momentum main diagonal: [ x xapxbp(k+1,) ]
- lhs(kp1_mdiag) &
- = + one_third * beta &
- * invrs_dzt_p1 * invrs_rho_ds_zm &
- * rho_ds_zm_p1 * a1_zm_p1 * wp3_on_wp2_p1
-
- ! Momentum main diagonal: [ x xapxbp(k,) ]
- lhs(k_mdiag) &
- = - one_third * beta &
- * invrs_dzt_p1 * invrs_rho_ds_zm &
- * rho_ds_zm * a1_zm * wp3_on_wp2
-
- ! Momentum subdiagonal: [ x xapxbp(k-1,) ]
- lhs(km1_mdiag) = zero
-
- end if
-
- return
- end function term_ta_lhs_upwind
-
- !=============================================================================
- pure function term_ta_rhs( wp2_ztp1, wp2_zt, &
- wp3_on_wp2_ztp1, wp3_on_wp2_zt, &
- rho_ds_ztp1, rho_ds_zt, invrs_rho_ds_zm, &
- a1_ztp1, a1, a1_zt, wpxbp_ztp1, wpxbp_zt, &
- wpxap_ztp1, wpxap_zt, invrs_dzm, beta ) &
- result( rhs )
-
- ! Description:
- ! Turbulent advection of x_a'x_b': explicit portion of the code.
- !
- ! The d(x_a'x_b')/dt equation contains a turbulent advection term:
- !
- ! - (1/rho_ds) * d( rho_ds * w'x_a'x_b' )/dz.
- !
- ! A substitution is made in order to close the turbulent advection term,
- ! such that:
- !
- ! w'x_a'x_b' = (1/3)*beta * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'
- ! + (1-(1/3)*beta) * (a_1)^2 * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b';
- !
- ! where a_1 is a variable that is a function of sigma_sqd_w. The turbulent
- ! advection term is rewritten as:
- !
- ! - (1/rho_ds)
- ! * d [ rho_ds * { (1/3)*beta * a_1 * ( w'^3 / w'^2 ) * x_a'x_b'
- ! + (1-(1/3)*beta) * (a_1)^2 * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b' } ]
- ! / dz;
- !
- ! which produces an implicit and an explicit portion of this term. The
- ! explicit portion of this term is:
- !
- ! - (1/rho_ds)
- ! * d [ rho_ds * (1-(1/3)*beta) * (a_1)^2 * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b' ] / dz.
- !
- ! Since (1-(1/3)*beta) is a constant, it can be pulled outside of the
- ! derivative. The explicit portion of this term becomes:
- !
- ! - (1-(1/3)*beta)/rho_ds
- ! * d [ rho_ds * (a_1)^2 * ( w'^3 / (w'^2)^2 ) * w'x_a' * w'x_b' ] / dz.
- !
- ! The explicit portion of this term is discretized as follows:
- !
- ! The values of w'x_a', w'x_b', w'^2, and a_1 are found on the momentum
- ! levels. The values of w'^3 are found on the thermodynamic levels.
- ! Additionally, the values of rho_ds_zt are found on the thermodynamic
- ! levels, and the values of invrs_rho_ds_zm are found on the momentum
- ! levels. The variables w'x_a', w'x_b', w'^2, and a_1 are each interpolated
- ! to the intermediate thermodynamic levels. The values of the mathematical
- ! expression (called F here) within the dF/dz term are computed on the
- ! thermodynamic levels. Then the derivative (d/dz) of the expression (F) is
- ! taken over the central momentum level, where it is multiplied by
- ! (1-(1/3)*beta), and by invrs_rho_ds_zm, yielding the desired result. In
- ! this function, the values of F are as follows:
- !
- ! F = rho_ds_zt * ( a_1(t) )^2 * ( w'^3(t) / ( w'^2(t) )^2 )
- ! * w'x_a'(t) * w'x_b'(t);
- !
- ! where the timestep index (t) stands for the index of the current timestep.
- !
- !
- ! =a1p1=======wp2p1=======wpxapp1=======wpxbpp1========================= m(k+1)
- !
- ! -a1(interp)-wp2(interp)-wpxap(interp)-wpxbp(interp)-wp3p1-rho_ds_ztp1- t(k+1)
- !
- ! =a1=========wp2=========wpxap=========wpxbp===dF/dz===invrs_rho_ds_zm= m(k)
- !
- ! -a1(interp)-wp2(interp)-wpxap(interp)-wpxbp(interp)-wp3---rho_ds_zt--- t(k)
- !
- ! =a1m1=======wp2m1=======wpxapm1=======wpxbpm1========================= m(k-1)
- !
- ! The vertical indices m(k+1), t(k+1), m(k), t(k), and m(k-1) correspond
- ! with altitudes zm(k+1), zt(k+1), zm(k), zt(k), and zm(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- one, & ! Constant(s)
- one_third
-
- use crmx_model_flags, only: &
- l_standard_term_ta
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: max
-
- ! Input variables
- real( kind = core_rknd ), intent(in) :: &
- wp2_ztp1, & ! w'^2 interpolated to thermo. level (k+1) [m^2/s^2]
- wp2_zt, & ! w'^2 interpolated to thermo. level (k) [m^2/s^2]
- wp3_on_wp2_ztp1, & ! Smoothed w'^3 / w'^2 on thermo. level (k+1)[m^2/s^2]
- wp3_on_wp2_zt, & ! Smoothed w'^3 / w'^2 on thermo. level (k) [m^2/s^2]
- rho_ds_ztp1, & ! Dry, static density at thermo. level (k+1) [kg/m^3]
- rho_ds_zt, & ! Dry, static density at thermo. level (k) [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ mome. lev (k) [m^3/kg]
- a1_ztp1, & ! a_1 interpolated to thermo. level (k+1) [-]
- a1, & ! a_1(k) [-]
- a1_zt, & ! a_1 interpolated to thermo. level (k) [-]
- wpxbp_ztp1, & ! w'x_b' interpolated to thermo. level (k+1) [m/s {x_bm units}]
- wpxbp_zt, & ! w'x_b' interpolated to thermo. level (k) [m/s {x_bm units}]
- wpxap_ztp1, & ! w'x_a' interpolated to thermo. level (k+1) [m/s {x_am units}]
- wpxap_zt, & ! w'x_a' interpolated to thermo. level (k) [m/s {x_am units}]
- invrs_dzm, & ! Inverse of grid spacing [1/m]
- beta ! Model parameter [-]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
-
- if ( l_standard_term_ta ) then
-
- ! The turbulent advection term is discretized normally, in accordance
- ! with the model equations found in the documentation and the description
- ! listed above.
-
- rhs &
- = - ( one - one_third * beta ) &
- * invrs_rho_ds_zm &
- * invrs_dzm &
- * ( rho_ds_ztp1 * a1_ztp1**2 &
- * wp3_on_wp2_ztp1 / wp2_ztp1 &
- * wpxap_ztp1 * wpxbp_ztp1 &
- - rho_ds_zt * a1_zt**2 &
- * wp3_on_wp2_zt / wp2_zt &
- * wpxap_zt * wpxbp_zt &
- )
-
- else
-
- ! Brian tried a new discretization for the turbulent advection term, for
- ! which the explicit portion of the term is:
- ! - (1/rho_ds)
- ! * d [ rho_ds * (a_1)^2 * (1-(1/3)*beta) * ( w'^3 / (w'^2)^2 )
- ! * w'x_a' * w'x_b' ] / dz.
- ! In order to help stabilize x_a'x_b', (a_1)^2 has been pulled outside
- ! the derivative.
-
- rhs &
- = - ( one - one_third * beta ) &
- * invrs_rho_ds_zm * a1**2 &
- * invrs_dzm &
- * ( rho_ds_ztp1 &
- * wp3_on_wp2_ztp1 / wp2_ztp1 &
- * wpxap_ztp1 * wpxbp_ztp1 &
- - rho_ds_zt &
- * wp3_on_wp2_zt / wp2_zt &
- * wpxap_zt * wpxbp_zt &
- )
-
- ! End of Brian's a1 change. 14 Feb 2008.
-
- endif
-
-
- return
- end function term_ta_rhs
-
- !=============================================================================
- pure function term_tp( xamp1, xam, xbmp1, xbm, &
- wpxbp, wpxap, invrs_dzm ) &
- result( rhs )
-
- ! Description:
- ! Turbulent production of x_a'x_b': explicit portion of the code.
- !
- ! The d(x_a'x_b')/dt equation contains a turbulent production term:
- !
- ! - w'x_b' d(x_am)/dz - w'x_a' d(x_bm)/dz.
- !
- ! This term is solved for completely explicitly and is discretized as
- ! follows:
- !
- ! The values of w'x_a' and w'x_b' are found on the momentum levels, whereas
- ! the values of x_am and x_bm are found on the thermodynamic levels. The
- ! derivatives of both x_am and x_bm are taken over the intermediate
- ! (central) momentum level. All of the remaining mathematical operations
- ! take place at the central momentum level, yielding the desired result.
- !
- ! ---------xamp1------------xbmp1-------------------------- t(k+1)
- !
- ! ===wpxap======d(xam)/dz=========d(xbm)/dz===wpxbp======== m(k)
- !
- ! ---------xam--------------xbm---------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- real( kind = core_rknd ), intent(in) :: &
- xam, & ! x_am(k) [{x_am units}]
- xamp1, & ! x_am(k+1) [{x_am units}]
- xbm, & ! x_bm(k) [{x_bm units}]
- xbmp1, & ! x_bm(k+1) [{x_bm units}]
- wpxbp, & ! w'x_b'(k) [m/s {x_bm units}]
- wpxap, & ! w'x_a'(k) [m/s {x_am units}]
- invrs_dzm ! Inverse of grid spacing (k) [1/m]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = - wpxbp * invrs_dzm * ( xamp1 - xam ) &
- - wpxap * invrs_dzm * ( xbmp1 - xbm )
-
- return
- end function term_tp
-
- !=============================================================================
- pure function term_dp1_lhs( Cn, tau_zm ) &
- result( lhs )
-
- ! Description:
- ! Dissipation term 1 for x_a'x_b': implicit portion of the code.
- !
- ! The d(x_a'x_b')/dt equation contains dissipation term 1:
- !
- ! - ( C_n / tau_zm ) x_a'x_b'.
- !
- ! For cases where x_a'x_b' is a variance (in other words, where x_a and x_b
- ! are the same variable), the term is damped to a certain positive
- ! threshold, such that:
- !
- ! - ( C_n / tau_zm ) * ( x_a'x_b' - threshold ).
- !
- ! However, if x_a'x_b' is u'^2 or v'^2, damping to a minimum threshold value
- ! is part of pressure term 1 and is handled as part of function 'term_pr1'.
- ! Thus, for u'^2 and v'^2, function 'term_dp1_lhs' is called, but function
- ! 'term_dp1_rhs' is not called, as function 'term_pr1' is called instead.
- !
- ! For cases where x_a'x_b' is a covariance (in other words, where x_a and
- ! x_b are different variables), threshold is set to 0, and the expression
- ! reverts to the form found in the first equation.
- !
- ! This term is broken into implicit and explicit portions. The equations
- ! for u'^2, v'^2, and any covariances only include the implicit portion.
- ! The implicit portion of this term is:
- !
- ! - ( C_n / tau_zm ) x_a'x_b'(t+1).
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "-" in front of the term
- ! is changed to a "+".
- !
- ! The timestep index (t+1) means that the value of x_a'x_b' being used is
- ! from the next timestep, which is being advanced to in solving the
- ! d(x_a'x_b')/dt equation.
- !
- ! The values of x_a'x_b' are found on momentum levels. The values of
- ! time-scale tau_zm are also found on momentum levels.
- !
- ! Note: For equations that use pressure term 1 (such as the equations for
- ! u'^2 and v'^2), C_n = ( 2*C_4 + C_14 ) / 3; which combines the
- ! implicit contributions for dissipation term 1 and pressure term 1
- ! into one expression. Otherwise, C_n = C_2.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- Cn, & ! Coefficient C_n [-]
- tau_zm ! Time-scale tau at momentum levels (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Momentum main diagonal: [ x xapxbp(k,) ]
- lhs &
- = + Cn / tau_zm
-
- return
- end function term_dp1_lhs
-
- !=============================================================================
- pure function term_dp1_rhs( Cn, tau_zm, threshold ) &
- result( rhs )
-
- ! Description:
- ! Dissipation term 1 for x_a'x_b': explicit portion of the code.
- !
- ! The d(x_a'x_b')/dt equation contains dissipation term 1:
- !
- ! - ( C_n / tau_zm ) x_a'x_b'.
- !
- ! For cases where x_a'x_b' is a variance (in other words, where x_a and x_b
- ! are the same variable), the term is damped to a certain positive
- ! threshold, such that:
- !
- ! - ( C_n / tau_zm ) * ( x_a'x_b' - threshold ).
- !
- ! However, if x_a'x_b' is u'^2 or v'^2, damping to a minimum threshold value
- ! is part of pressure term 1 and is handled as part of function 'term_pr1'.
- ! Thus, for u'^2 and v'^2, function 'term_dp1_lhs' is called, but function
- ! 'term_dp1_rhs' is not called, as function 'term_pr1' is called instead.
- !
- ! For cases where x_a'x_b' is a covariance (in other words, where x_a and
- ! x_b are different variables), threshold is set to 0, and the expression
- ! reverts to the form found in the first equation.
- !
- ! This term is broken into implicit and explicit portions. The equations
- ! for u'^2, v'^2, and any covariances only include the implicit portion.
- ! The explicit portion of this term is:
- !
- ! + ( C_n / tau_zm ) * threshold.
- !
- ! The values of time-scale tau_zm and the threshold are found on the
- ! momentum levels.
- !
- ! Note: The equations that use pressure term 1 (such as the equations for
- ! u'^2 and v'^2) do not call this function. Thus, within this
- ! function, C_n = C_2.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- Cn, & ! Coefficient C_n [-]
- tau_zm, & ! Time-scale tau at momentum levels (k) [s]
- threshold ! Minimum allowable magnitude value of x_a'x_b' [units vary]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs &
- = + ( Cn / tau_zm ) * threshold
-
- return
- end function term_dp1_rhs
-
- !=============================================================================
- pure function term_pr1( C4, C14, xbp2, wp2, tau_zm ) &
- result( rhs )
-
- ! Description:
- ! Pressure term 1 for x_a'x_b': explicit portion of the code.
- !
- ! Note: Pressure term 1 is only used when x_a'x_b' is either u'^2 or v'^2.
- ! For the following description, pressure term 2 for u'^2 is used as
- ! the example. Pressure term 2 for v'^2 is the same as pressure
- ! term 2 for u'^2, except that the v'^2 and u'^2 variables are
- ! switched.
- !
- ! The d(u'^2)/dt equation contains dissipation term 1:
- !
- ! - ( C_4 / tau_zm ) * ( u'^2 - (2/3)*em );
- !
- ! where em = (1/2) * ( u'^2 + v'^2 + w'^2 );
- !
- ! and with the substitution applied, dissipation term 1 becomes:
- !
- ! - ( C_4 / tau_zm ) * ( u'^2 - (1/3) * ( u'^2 + v'^2 + w'^2 ) ).
- !
- ! The d(u'^2)/dt equation also contains pressure term 1:
- !
- ! - (2/3) * epsilon;
- !
- ! where epsilon = C_14 * ( em / tau_zm ).
- !
- ! Additionally, since pressure term 1 is a damping term, em is damped only
- ! to it's minimum threshold value, em_min, where:
- !
- ! em_min = (1/2) * ( u'^2|_min + v'^2|_min + w'^2|_min )
- ! = (1/2) * ( w_tol^2 + w_tol^2 + w_tol^2 )
- ! = (3/2) * w_tol^2.
- !
- ! With the damping threshold applied, epsilon becomes:
- !
- ! epsilon = C_14 * ( ( em - em_min ) / tau_zm );
- !
- ! and with all substitutions applied, pressure term 1 becomes:
- !
- ! - (2/3) * ( C_14 / tau_zm )
- ! * [ (1/2) * ( u'^2 + v'^2 + w'^2 ) - (3/2) * w_tol^2 ].
- !
- ! Dissipation term 1 and pressure term 1 are combined and simplify to:
- !
- ! - [ ( 2*C_4 + C_14 ) / ( 3 * tau_zm ) ] * u'^2
- ! + [ ( C_4 - C_14 ) / ( 3 * tau_zm ) ] * ( v'^2 + w'^2 )
- ! + ( C_14 / tau_zm ) * w_tol^2.
- !
- ! The combined term has both implicit and explicit components.
- ! The implicit component is:
- !
- ! - [ ( 2*C_4 + C_14 ) / ( 3 * tau_zm ) ] * u'^2(t+1).
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "-" in front of the term
- ! is changed to a "+".
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(x_a'x_b')/dt equation.
- !
- ! The implicit component of the combined dp1 and pr1 term is solved in
- ! function "term_dp1_lhs" above, where "( 2*C_4 + C_14 ) / 3" is sent in
- ! as "C_n".
- !
- ! The explicit component of the combined dp1 and pr1 term is:
- !
- ! + [ ( C_4 - C_14 ) / ( 3 * tau_zm ) ] * ( v'^2(t) + w'^2(t) )
- ! + ( C_14 / tau_zm ) * w_tol^2;
- !
- ! and is discretized as follows:
- !
- ! The values for v'^2 and w'^2, as well as for tau_zm, are found on the
- ! momentum levels. The mathematical operations all take place on the
- ! momentum levels, yielding the desired result.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- w_tol_sqd, & ! Constant(s)
- one_third
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C4, & ! Model parameter C_4 [-]
- C14, & ! Model parameter C_14 [-]
- xbp2, & ! v'^2(k) (if solving for u'^2) or vice versa [m^2/s^2]
- wp2, & ! w'^2(k) [m^2/s^2]
- tau_zm ! Time-scale tau at momentum levels (k) [s]
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- rhs = + one_third * ( C4 - C14 ) * ( xbp2 + wp2 ) / tau_zm &
- + ( C14 / tau_zm ) * w_tol_sqd
-
- return
- end function term_pr1
-
- !=============================================================================
- pure function term_pr2( C5, thv_ds_zm, wpthvp, upwp, vpwp, &
- um, vm, invrs_dzm, kp1, k, &
- Lscalep1, Lscale, wp2_ztp1, wp2_zt ) &
- result( rhs )
-
- ! Description:
- ! Pressure term 2 for x_a'x_b': explicit portion of the code.
- !
- ! Note: Pressure term 2 is only used when x_a'x_b' is either u'^2 or v'^2.
- ! For the following description, pressure term 2 for u'^2 is used as
- ! the example. Pressure term 2 for v'^2 is the exact same as
- ! pressure term 2 for u'^2.
- !
- ! The d(u'^2)/dt equation contains pressure term 2:
- !
- ! + (2/3) C_5 [ (g/thv_ds) w'th_v' - u'w' du/dz - v'w' dv/dz ].
- !
- ! This term is solved for completely explicitly and is discretized as
- ! follows:
- !
- ! The values of w'th_v', u'w', and v'w' are found on the momentum levels,
- ! whereas the values of um and vm are found on the thermodynamic levels.
- ! Additionally, the values of thv_ds_zm are found on the momentum levels.
- ! The derivatives of both um and vm are taken over the intermediate
- ! (central) momentum level. All the remaining mathematical operations take
- ! place at the central momentum level, yielding the desired result.
- !
- ! -----ump1------------vmp1-------------------------------------- t(k+1)
- !
- ! =upwp====d(um)/dz========d(vm)/dz==vpwp===thv_ds_zm==wpthvp==== m(k)
- !
- ! -----um--------------vm---------------------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: & ! Constants
- grav, & ! Gravitational acceleration [m/s^2]
- one, &
- two_thirds, &
- zero, &
- zero_threshold
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: abs, max
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- C5, & ! Model parameter C_5 [-]
- thv_ds_zm, & ! Dry, base-state theta_v at momentum level (k) [K]
- wpthvp, & ! w'th_v'(k) [m/K/s]
- upwp, & ! u'w'(k) [m^2/s^2]
- vpwp, & ! v'w'(k) [m^2/s^2]
- invrs_dzm, & ! Inverse of the grid spacing (k) [1/m]
- Lscalep1, & ! Mixing length (k+1) [m]
- Lscale, & ! Mixing length (k) [m]
- wp2_ztp1, & ! w'^2(k+1) (thermo. levels) [m^2/s^2]
- wp2_zt ! w'^2(k) (thermo. levels) [m^2/s^2]
-
- ! Note: Entire arrays of um and vm are now required rather than um and vm
- ! only at levels k and k+1. The entire array is necessary when a vertical
- ! average calculation of d(um)/dz and d(vm)/dz is used. --ldgrant March 2010
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- um, & ! mean zonal wind [m/s]
- vm ! mean meridional wind [m/s]
-
- integer, intent(in) :: &
- kp1, & ! current level+1 in xp2_xpyp_uv_rhs loop
- k ! current level in xp2_xpyp_uv_rhs loop
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- ! Local Variable(s) --ldgrant, March 2010
- real( kind = core_rknd ), parameter :: &
- ! Constants empirically determined for experimental version of term_pr2
- ! ldgrant March 2010
- constant1 = one, & ! [m/s]
- constant2 = 1000.0_core_rknd, & ! [m]
- vert_avg_depth = 200.0_core_rknd ! Depth over which to average d(um)/dz and d(vm)/dz [m]
-
- real( kind = core_rknd ) :: &
- zt_high, & ! altitude above current altitude zt(k) [m]
- um_high, & ! um at altitude zt_high [m/s]
- vm_high, & ! vm at altitude zt_high [m/s]
- zt_low, & ! altitude below (or at) current altitude zt(k) [m]
- um_low, & ! um at altitude zt_low [m/s]
- vm_low ! vm at altitude zt_low [m/s]
-
- logical, parameter :: &
- l_use_experimental_term_pr2 = .false., & ! If true, use experimental version
- ! of term_pr2 calculation
- l_use_vert_avg_winds = .true. ! If true, use vert_avg_depth average
- ! calculation for d(um)/dz and d(vm)/dz
-
- !------ Begin code ------------
-
- if( .not. l_use_experimental_term_pr2 ) then
- ! use original version of term_pr2
-
- ! As applied to w'2
- rhs = + two_thirds * C5 &
- * ( ( grav / thv_ds_zm ) * wpthvp &
- - upwp * invrs_dzm * ( um(kp1) - um(k) ) &
- - vpwp * invrs_dzm * ( vm(kp1) - vm(k) ) &
- )
-
- else ! use experimental version of term_pr2 --ldgrant March 2010
-
- if( l_use_vert_avg_winds ) then
- ! We found that using a 200m running average of d(um)/dz and d(vm)/dz
- ! produces larger spikes in up2 and vp2 near the inversion for
- ! the stratocumulus cases.
- call find_endpts_for_vert_avg_winds &
- ( vert_avg_depth, k, um, vm, & ! intent(in)
- zt_high, um_high, vm_high, & ! intent(out)
- zt_low, um_low, vm_low ) ! intent(out)
-
- else ! Do not use a vertical average calculation for d(um)/dz and d(vm)/dz
- zt_high = gr%zt(kp1)
- um_high = um(kp1)
- vm_high = vm(kp1)
-
- zt_low = gr%zt(k)
- um_low = um(k)
- vm_low = vm(k)
- end if ! l_use_vert_avg_winds
-
- ! *****NOTES on experimental version*****
- ! Leah Grant and Vince Larson eliminated the contribution from wpthvp
- ! because terms with d(wp2)/dz include buoyancy effects and seem to
- ! produce better results.
- !
- ! We also eliminated the contribution from the momentum flux terms
- ! because they didn't contribute to the results.
- !
- ! The constant1 line does not depend on shear. This is important for
- ! up2 and vp2 generation in cases that have little shear such as FIRE.
- ! We also made the constant1 line proportional to d(Lscale)/dz to account
- ! for higher spikes in up2 and vp2 near a stronger inversion. This
- ! increases up2 and vp2 near the inversion for the stratocumulus cases,
- ! but overpredicts up2 and vp2 near cloud base in cumulus cases such
- ! as BOMEX where d(Lscale)/dz is large. Therefore, the d(Lscale)/dz
- ! contribution is commented out for now.
- !
- ! The constant2 line includes the possibility of shear generation of
- ! up2 and vp2, which is important for some cases. The current functional
- ! form used is:
- ! constant2 * |d(wp2)/dz| * |d(vm)/dz|
- ! We use |d(vm)/dz| instead of |d(um)/dz| + |d(vm)/dz| here because
- ! this allows for different profiles of up2 and vp2, which occur for
- ! many cases. In addition, we found that in buoyant cases, up2 is
- ! more related to d(vm)/dz and vp2 is more related to d(um)/dz. This
- ! occurs if horizontal rolls are oriented in the direction of the shear
- ! vector. However, in stably stratified cases, the opposite relation is
- ! true (horizontal rolls caused by shear are perpendicular to the shear
- ! vector). This effect is not yet accounted for.
- !
- ! For better results, we reduced the value of C5 from 5.2 to 3.0 and
- ! changed the eddy diffusivity coefficient Kh so that it is
- ! proportional to 1.5*wp2 rather than to em.
- rhs = + two_thirds * C5 &
- * ( constant1 * abs( wp2_ztp1 - wp2_zt ) * invrs_dzm &
- ! * abs( Lscalep1 - Lscale ) * invrs_dzm &
- + constant2 * abs( wp2_ztp1 - wp2_zt ) * invrs_dzm &
- * abs( vm_high - vm_low ) / ( zt_high - zt_low ) &
- + ( Lscalep1 + Lscale ) * zero &
- ! This line eliminates an Intel compiler
- ) ! warning that Lscalep1/Lscale are not
- ! used. -meyern
- end if ! .not. l_use_experimental_term_pr2
-
- ! Added by dschanen for ticket #36
- ! We have found that when shear generation is zero this term will only be
- ! offset by hole-filling (up2_pd/vp2_pd) and reduces turbulence
- ! unrealistically at lower altitudes to make up the difference.
- rhs = max( rhs, zero_threshold )
-
- return
- end function term_pr2
-
- !=============================================================================
- pure subroutine find_endpts_for_vert_avg_winds &
- ( vert_avg_depth, k, um, vm, & ! intent(in)
- zt_high, um_high, vm_high, & ! intent(out)
- zt_low, um_low, vm_low ) ! intent(out)
- ! Description:
- ! This subroutine determines values of um and vm which are
- ! +/- [vert_avg_depth/2] m above and below the current altitude zt(k).
- ! This is for the purpose of using a running vertical average
- ! calculation of d(um)/dz and d(vm)/dz in term_pr2 (over a depth
- ! vert_avg_depth). E.g. If a running average over 200m is desired,
- ! then this subroutine will determine the values of um and vm which
- ! are 100m above and below the current level.
- ! ldgrant March 2010
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- two ! Constant(s)
-
- use crmx_interpolation, only : &
- binary_search, lin_int ! Function(s)
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- vert_avg_depth ! Depth over which to average d(um)/dz
- ! and d(vm)/dz in term_pr2 [m]
-
- integer, intent(in) :: &
- k ! current level in xp2_xpyp_uv_rhs loop
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- um, & ! mean zonal wind [m/s]
- vm ! mean meridional wind [m/s]
-
- ! Output Variables
- real( kind = core_rknd ), intent(out) :: &
- zt_high, & ! current altitude zt(k) + depth [m]
- um_high, & ! um at altitude zt_high [m/s]
- vm_high, & ! vm at altitude zt_high [m/s]
- zt_low, & ! current altitude zt(k) - depth [m]
- um_low, & ! um at altitude zt_low [m/s]
- vm_low ! vm at altitude zt_low [m/s]
-
- ! Local Variables
- real( kind = core_rknd ) :: depth ! vert_avg_depth/2 [m]
-
- integer :: k_high, k_low
- ! Number of levels above (below) the current level where altitude is
- ! [depth] greater (less) than the current altitude
- ! [unless zt(k) < [depth] from an upper/lower boundary]
-
- !------ Begin code ------------
-
- depth = vert_avg_depth / two
-
- ! Find the grid level that contains the altitude greater than or
- ! equal to the current altitude + depth
- k_high = binary_search( gr%nz, gr%zt, gr%zt(k)+depth )
- ! If the current altitude + depth is greater than the highest
- ! altitude, binary_search returns a value of -1
- if ( k_high == -1 ) k_high = gr%nz
-
- if ( k_high == gr%nz ) then
- ! Current altitude + depth is higher than or exactly at the top grid level.
- ! Since this is a ghost point, use the altitude at grid level nzmax-1
- k_high = gr%nz-1
- zt_high = gr%zt(k_high)
- um_high = um(k_high)
- vm_high = vm(k_high)
- else if ( gr%zt(k_high) == gr%zt(k)+depth ) then
- ! Current altitude + depth falls exactly on another grid level.
- ! In this case, no interpolation is necessary.
- zt_high = gr%zt(k_high)
- um_high = um(k_high)
- vm_high = vm(k_high)
- else ! Do an interpolation to find um & vm at current altitude + depth.
- zt_high = gr%zt(k)+depth
- um_high = lin_int( zt_high, gr%zt(k_high), gr%zt(k_high-1), &
- um(k_high), um(k_high-1) )
- vm_high = lin_int( zt_high, gr%zt(k_high), gr%zt(k_high-1), &
- vm(k_high), vm(k_high-1) )
- end if ! k_high ...
-
-
- ! Find the grid level that contains the altitude less than or
- ! equal to the current altitude - depth
- k_low = binary_search( gr%nz, gr%zt, gr%zt(k)-depth )
- ! If the current altitude - depth is less than the lowest
- ! altitude, binary_search returns a value of -1
- if ( k_low == -1 ) k_low = 2
-
- if ( k_low == 2 ) then
- ! Current altitude - depth is less than or exactly at grid level 2.
- ! Since grid level 1 is a ghost point, use the altitude at grid level 2
- zt_low = gr%zt(k_low)
- um_low = um(k_low)
- vm_low = vm(k_low)
- else if ( gr%zt(k_low) == gr%zt(k)-depth ) then
- ! Current altitude - depth falls exactly on another grid level.
- ! In this case, no interpolation is necessary.
- zt_low = gr%zt(k_low)
- um_low = um(k_low)
- vm_low = vm(k_low)
- else ! Do an interpolation to find um at current altitude - depth.
- zt_low = gr%zt(k)-depth
- um_low = lin_int( zt_low, gr%zt(k_low), gr%zt(k_low-1), &
- um(k_low), um(k_low-1) )
- vm_low = lin_int( zt_low, gr%zt(k_low), gr%zt(k_low-1), &
- vm(k_low), vm(k_low-1) )
- end if ! k_low ...
-
- return
- end subroutine find_endpts_for_vert_avg_winds
-
- !=============================================================================
- subroutine pos_definite_variances( solve_type, dt, tolerance, &
- rho_ds_zm, rho_ds_zt, &
- xp2_np1 )
-
- ! Description:
- ! Use the hole filling code to make a variance term positive definite
- !-----------------------------------------------------------------------
-
- use crmx_fill_holes, only: fill_holes_driver
- use crmx_grid_class, only: gr
- use crmx_clubb_precision, only: time_precision, core_rknd
-
- use crmx_stats_variables, only: &
- zm, l_stats_samp, &
- irtp2_pd, ithlp2_pd, iup2_pd, ivp2_pd ! variables
- use crmx_stats_type, only: &
- stat_begin_update, stat_end_update ! subroutines
-
-
- implicit none
-
- ! External
- intrinsic :: any, real, trim
-
- ! Input variables
- integer, intent(in) :: &
- solve_type
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep [s]
-
- real( kind = core_rknd ), intent(in) :: &
- tolerance ! Threshold for xp2_np1 [units vary]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt ! Dry, static density on thermodynamic levels [kg/m^3]
-
- ! Input/Output variables
- real( kind = core_rknd ), intent(inout), dimension(gr%nz) :: &
- xp2_np1 ! Variance for [units vary]
-
- ! Local variables
- integer :: &
- ixp2_pd
-
- select case( solve_type )
- case ( xp2_xpyp_rtp2 )
- ixp2_pd = irtp2_pd
- case ( xp2_xpyp_thlp2 )
- ixp2_pd = ithlp2_pd
- case ( xp2_xpyp_up2 )
- ixp2_pd = iup2_pd
- case ( xp2_xpyp_vp2 )
- ixp2_pd = ivp2_pd
- case default
- ixp2_pd = 0 ! This includes the passive scalars
- end select
-
- if ( l_stats_samp ) then
- ! Store previous value for effect of the positive definite scheme
- call stat_begin_update( ixp2_pd, xp2_np1 / real( dt, kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- endif
-
-
- if ( any( xp2_np1 < tolerance ) ) then
-
- ! Call the hole-filling scheme.
- ! The first pass-through should draw from only two levels on either side
- ! of the hole.
- call fill_holes_driver( 2, tolerance, "zm", & ! Intent(in)
- rho_ds_zt, rho_ds_zm, & ! Intent(in)
- xp2_np1 ) ! Intent(inout)
-
- endif
-
- if ( l_stats_samp ) then
- ! Store previous value for effect of the positive definite scheme
- call stat_end_update( ixp2_pd, xp2_np1 / real( dt, kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- endif
-
-
- return
- end subroutine pos_definite_variances
-
- !============================================================================
- subroutine update_xp2_mc_tndcy( nz, dt, cloud_frac, rcm, rvm, thlm, &
- exner, rrainm_evap, pdf_params, &
- rtp2_mc_tndcy, thlp2_mc_tndcy )
- !Description:
- !This subroutine is for use when l_morr_xp2_mc_tndcy = .true.
- !The effects of rain evaporation on rtp2 and thlp2 are included by
- !assuming rain falls through the moist (cold) portion of the pdf.
- !This is accomplished by defining a precip_fraction and assuming a double
- !delta shaped pdf, such that the evaporation makes the moist component
- !moister and the colder component colder. --storer
-
- use crmx_pdf_parameter_module, only: pdf_parameter
-
- use crmx_constants_clubb, only: &
- cloud_frac_min, & !Variables
- Cp, &
- Lv
-
- use crmx_clubb_precision, only: &
- core_rknd, & ! Variable(s)
- time_precision
-
- implicit none
-
- !input parameters
- integer, intent(in) :: nz ! Points in the Vertical [-]
-
- real( kind = time_precision ), intent(in) :: dt ! Model timestep [s]
-
- real( kind = core_rknd ), dimension(nz), intent(in) :: &
- cloud_frac, & !Cloud fraction [-]
- rcm, & !Cloud water mixing ratio [kg/kg]
- rvm, & !Vapor water mixing ratio [kg/kg]
- thlm, & !Liquid potential temperature [K]
- exner, & !Exner function [-]
- rrainm_evap !Evaporation of rain [kg/kg/s]
-
- type(pdf_parameter), target, dimension(nz), intent(in) :: &
- pdf_params ! PDF parameters
-
- !input/output variables
- real( kind = core_rknd ), dimension(nz), intent(inout) :: &
- rtp2_mc_tndcy, & !Tendency of rtp2 due to evaporation [(kg/kg)^2/s]
- thlp2_mc_tndcy !Tendency of thlp2 due to evaporation [K^2/s]
-
- !local variables
- real( kind = core_rknd ), dimension(nz) :: &
- temp_rtp2, & !Used only to calculate rtp2_mc_tndcy [(kg/kg)^2]
- temp_thlp2, & !Used to calculate thlp2_mc_tndcy [K^2/s]
- precip_frac, & !Precipitation fraction [-]
- pf_const ! ( 1 - pf )/( pf ) [-]
-
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Calculate precip_frac
- precip_frac(nz) = 0.0_core_rknd
- do k = nz-1, 1, -1
- if ( cloud_frac(k) > cloud_frac_min ) then
- precip_frac(k) = cloud_frac(k)
- else
- precip_frac(k) = precip_frac(k+1)
- end if
- end do
-
- !Calculate increased variance (rtp2 and thlp2) due to rain evaporation
-
- where ( precip_frac > cloud_frac_min )
- pf_const = ( 1.0_core_rknd - precip_frac ) / precip_frac
- else where
- pf_const = 0.0_core_rknd
- end where
-
- ! Include effects of rain evaporation on rtp2
- temp_rtp2 = pdf_params%mixt_frac * ( ( pdf_params%rt1 - ( rcm + rvm ) )**2 &
- + pdf_params%varnce_rt1 ) + ( 1.0_core_rknd - pdf_params%mixt_frac ) &
- * ( ( pdf_params%rt2 - ( rcm + rvm ) )**2 + pdf_params%varnce_rt2 )
-
- rtp2_mc_tndcy = rrainm_evap**2 * pf_const * dt &
- + 2.0_core_rknd * abs(rrainm_evap) * sqrt(temp_rtp2 * pf_const)
- !use absolute value of evaporation, as evaporation will add
- !to rt1
-
- !Include the effects of rain evaporation on thlp2
- temp_thlp2 = pdf_params%mixt_frac * ( ( pdf_params%thl1 - thlm )**2 &
- + pdf_params%varnce_thl1 ) + ( 1.0_core_rknd - pdf_params%mixt_frac ) &
- * ( ( pdf_params%thl2 - thlm )**2 + pdf_params%varnce_thl2 )
-
- thlp2_mc_tndcy = ( rrainm_evap * Lv / ( Cp * exner) )**2 * pf_const * dt &
- + 2.0_core_rknd * rrainm_evap * Lv / ( Cp * exner ) &
- * sqrt(temp_thlp2 * pf_const)
-
- end subroutine update_xp2_mc_tndcy
-
-!===============================================================================
-
-end module crmx_advance_xp2_xpyp_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_anl_erf.F90 b/src/physics/spcam/crm/CLUBB/crmx_anl_erf.F90
deleted file mode 100644
index 4298620c1d..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_anl_erf.F90
+++ /dev/null
@@ -1,228 +0,0 @@
-! $Id: anl_erf.F90 5324 2011-07-27 21:05:45Z dschanen@uwm.edu $
-module crmx_anl_erf
-
- implicit none
-
- public :: erf
-
- interface erf
- module procedure dp_erf, sp_erf
- end interface
-
- private :: dp_erf, sp_erf
-
- private ! Default Scope
-
- contains
-
- function dp_erf( x ) result( erfx )
-!-----------------------------------------------------------------------
-! Description:
-! DP_ERF evaluates the error function DP_ERF(X).
-!
-! Original Author:
-! William Cody,
-! Mathematics and Computer Science Division,
-! Argonne National Laboratory,
-! Argonne, Illinois, 60439.
-!
-! References:
-! William Cody,
-! "Rational Chebyshev approximations for the error function",
-! Mathematics of Computation,
-! 1969, pages 631-638.
-!
-! Arguments:
-! Input, real ( kind = 8 ) X, the argument of ERF.
-! Output, real ( kind = 8 ) ERFX, the value of ERF(X).
-!-----------------------------------------------------------------------
-
- implicit none
-
- ! Input Variables(s)
- double precision, intent(in) :: x
-
- ! External
- intrinsic :: epsilon, exp, aint
-
- ! Local Constants
- real( kind = 8 ), parameter, dimension( 5 ) :: &
- a = (/ 3.16112374387056560D+00, &
- 1.13864154151050156D+02, &
- 3.77485237685302021D+02, &
- 3.20937758913846947D+03, &
- 1.85777706184603153D-01 /)
- real( kind = 8 ), parameter, dimension( 4 ) :: &
- b = (/ 2.36012909523441209D+01, &
- 2.44024637934444173D+02, &
- 1.28261652607737228D+03, &
- 2.84423683343917062D+03 /)
- real( kind = 8 ), parameter, dimension( 9 ) :: &
- c = (/ 5.64188496988670089D-01, &
- 8.88314979438837594D+00, &
- 6.61191906371416295D+01, &
- 2.98635138197400131D+02, &
- 8.81952221241769090D+02, &
- 1.71204761263407058D+03, &
- 2.05107837782607147D+03, &
- 1.23033935479799725D+03, &
- 2.15311535474403846D-08 /)
- real( kind = 8 ), parameter, dimension( 8 ) :: &
- d = (/ 1.57449261107098347D+01, &
- 1.17693950891312499D+02, &
- 5.37181101862009858D+02, &
- 1.62138957456669019D+03, &
- 3.29079923573345963D+03, &
- 4.36261909014324716D+03, &
- 3.43936767414372164D+03, &
- 1.23033935480374942D+03 /)
- real( kind = 8 ), parameter, dimension( 6 ) :: &
- p = (/ 3.05326634961232344D-01, &
- 3.60344899949804439D-01, &
- 1.25781726111229246D-01, &
- 1.60837851487422766D-02, &
- 6.58749161529837803D-04, &
- 1.63153871373020978D-02 /)
-
- real( kind = 8 ), parameter, dimension( 5 ) :: &
- q = (/ 2.56852019228982242D+00, &
- 1.87295284992346047D+00, &
- 5.27905102951428412D-01, &
- 6.05183413124413191D-02, &
- 2.33520497626869185D-03 /)
-
- real( kind = 8 ), parameter :: &
- SQRPI = 0.56418958354775628695D+00, &
- THRESH = 0.46875D+00, &
- XBIG = 26.543D+00
-
- ! Return type
- real( kind = 8 ) :: erfx
-
- ! Local variables
- real( kind = 8 ) :: &
- del, &
- xabs, &
- xden, &
- xnum, &
- xsq
-
- integer :: i ! Index
-
-!-------------------------------------------------------------------------------
- xabs = abs( x )
-
- !
- ! Evaluate ERF(X) for |X| <= 0.46875.
- !
- if ( xabs <= THRESH ) then
-
- if ( epsilon( xabs ) < xabs ) then
- xsq = xabs * xabs
- else
- xsq = 0.0D+00
- end if
-
- xnum = a(5) * xsq
- xden = xsq
- do i = 1, 3
- xnum = ( xnum + a(i) ) * xsq
- xden = ( xden + b(i) ) * xsq
- end do
-
- erfx = x * ( xnum + a(4) ) / ( xden + b(4) )
- !
- ! Evaluate ERFC(X) for 0.46875 <= |X| <= 4.0.
- !
- else if ( xabs <= 4.0D+00 ) then
-
- xnum = c(9) * xabs
- xden = xabs
- do i = 1, 7
- xnum = ( xnum + c(i) ) * xabs
- xden = ( xden + d(i) ) * xabs
- end do
-
- erfx = ( xnum + c(8) ) / ( xden + d(8) )
- xsq = aint( xabs * 16.0D+00 ) / 16.0D+00
- del = ( xabs - xsq ) * ( xabs + xsq )
- ! xsq * xsq in the exponential was changed to xsq**2.
- ! This seems to decrease runtime by about a half a percent.
- ! ~~EIHoppe//20090622
- erfx = exp( - xsq**2 ) * exp( - del ) * erfx
-
- erfx = ( 0.5D+00 - erfx ) + 0.5D+00
-
- if ( x < 0.0D+00 ) then
- erfx = - erfx
- end if
- !
- ! Evaluate ERFC(X) for 4.0 < |X|.
- !
- else
-
- if ( XBIG <= xabs ) then
-
- if ( 0.0D+00 < x ) then
- erfx = 1.0D+00
- else
- erfx = -1.0D+00
- end if
-
- else
-
- xsq = 1.0D+00 / ( xabs * xabs )
-
- xnum = p(6) * xsq
- xden = xsq
- do i = 1, 4
- xnum = ( xnum + p(i) ) * xsq
- xden = ( xden + q(i) ) * xsq
- end do
-
- erfx = xsq * ( xnum + p(5) ) / ( xden + q(5) )
- erfx = ( SQRPI - erfx ) / xabs
- xsq = aint( xabs * 16.0D+00 ) / 16.0D+00
- del = ( xabs - xsq ) * ( xabs + xsq )
- erfx = exp( - xsq * xsq ) * exp( - del ) * erfx
-
- erfx = ( 0.5D+00 - erfx ) + 0.5D+00
- if ( x < 0.0D+00 ) then
- erfx = - erfx
- end if
-
- end if
-
- end if
-
- return
- end function dp_erf
-
-!-----------------------------------------------------------------------
- function sp_erf( x ) result( erfx )
-
-! Description:
-! Return a truncation of the 64bit approx. of the error function.
-! Ideally we would probably use a 32bit table for our approx.
-
-! References:
-! None
-!-----------------------------------------------------------------------
-
- implicit none
-
- ! External
- intrinsic :: real
-
- ! Input Variables
- real( kind=4 ), intent(in) :: x
-
- ! Return type
- real( kind=4 ) :: erfx
-
- erfx = real( dp_erf( real(x, kind=8) ), kind=4 )
-
- return
- end function sp_erf
-
-end module crmx_anl_erf
diff --git a/src/physics/spcam/crm/CLUBB/crmx_array_index.F90 b/src/physics/spcam/crm/CLUBB/crmx_array_index.F90
deleted file mode 100644
index 41c5b7f38d..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_array_index.F90
+++ /dev/null
@@ -1,37 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: array_index.F90 5216 2011-06-06 18:58:41Z dschanen@uwm.edu $
-!-----------------------------------------------------------------------
-module crmx_array_index
-
-! Description:
-! Contains indices to variables in larger arrays.
-! Note that the 'ii' is necessary because 'i' is used in
-! statistics to track locations in the zt/zm/sfc derived types.
-
-! References:
-! None
-!-----------------------------------------------------------------------
- implicit none
-
- ! Variables
- ! Microphysics mixing ratios
- integer, public :: &
- iirrainm, iirsnowm, iiricem, iirgraupelm ! [kg/kg]
-!$omp threadprivate(iirrainm, iirsnowm, iiricem, iirgraupelm)
-
- ! Microphysics number concentration
- integer, public :: &
- iiNrm, iiNsnowm, iiNim, iiNgraupelm, iiNcm ! [#/kg]
-!$omp threadprivate(iiNrm, iiNsnowm, iiNim, iiNgraupelm, iiNcm)
-
- ! Scalar quantities
- integer, public :: &
- iisclr_rt, iisclr_thl, iisclr_CO2, & ! [kg/kg]/[K]/[1e6 mol/mol]
- iiedsclr_rt, iiedsclr_thl, iiedsclr_CO2 ! " "
-!$omp threadprivate(iisclr_rt, iisclr_thl, iisclr_CO2, &
-!$omp iiedsclr_rt, iiedsclr_thl, iiedsclr_CO2)
-
- private ! Default Scope
-
-end module crmx_array_index
-!-----------------------------------------------------------------------
diff --git a/src/physics/spcam/crm/CLUBB/crmx_calendar.F90 b/src/physics/spcam/crm/CLUBB/crmx_calendar.F90
deleted file mode 100644
index 28d7987614..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_calendar.F90
+++ /dev/null
@@ -1,250 +0,0 @@
-!$Id: calendar.F90 5867 2012-07-03 21:06:44Z dschanen@uwm.edu $
-module crmx_calendar
-
- implicit none
-
- public :: gregorian2julian_date, julian2gregorian_date, &
- leap_year, compute_current_date, &
- gregorian2julian_day
-
- private ! Default Scope
-
- ! Constant Parameters
-
- ! 3 Letter Month Abbreviations
- character(len=3), dimension(12), public, parameter :: &
- month_names = (/'JAN','FEB','MAR','APR','MAY','JUN', &
- 'JUL','AUG','SEP','OCT','NOV','DEC'/)
-
- ! Number of days per month (Jan..Dec) for a non leap year
- integer, public, dimension(12), parameter :: &
- days_per_month = (/31, 28, 31, 30, 31, 30, &
- 31, 31, 30, 31, 30, 31/)
-
- contains
-!-----------------------------------------------------------------------
- integer function gregorian2julian_date( day, month, year )
-!
-! Description:
-! Computes the Julian Date (gregorian2julian), or the number of days since
-! 1 January 4713 BC, given a Gregorian Calender date (day, month, year).
-!
-! Reference:
-! Fliegel, H. F. and van Flandern, T. C.,
-! Communications of the ACM, Vol. 11, No. 10 (October, 1968)
-!----------------------------------------------------------------------
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- day, & ! Gregorian Calendar Day for given Month [dd]
- month, & ! Gregorian Calendar Month for given Year [mm]
- year ! Gregorian Calendar Year [yyyy]
-
- ! Local Variables
- integer :: I,J,K
-
- I = year
- J = month
- K = day
-
- gregorian2julian_date = K-32075+1461*(I+4800+(J-14)/12)/4+367* &
- (J-2-(J-14)/12*12)/12-3*((I+4900+(J-14)/12)/100)/4
-
- return
- end function gregorian2julian_date
-
-!------------------------------------------------------------------
- subroutine julian2gregorian_date &
- ( julian_date, day, month, year )
-!
-! Description:
-! Computes the Gregorina Calendar date (day, month, year)
-! given the Julian date (julian_date).
-!
-! Reference:
-! Fliegel, H. F. and van Flandern, T. C.,
-! Communications of the ACM, Vol. 11, No. 10 (October, 1968)
-! http://portal.acm.org/citation.cfm?id=364097
-!------------------------------------------------------------------
- implicit none
-
- ! Input Variable(s)
- integer, intent(in) :: julian_date ! Julian date being converted from
-
- ! Output Variable(s)
- integer, intent(out):: &
- day, & ! Gregorian calender day for given Month [dd]
- month, & ! Gregorian calender month for given Year [mm]
- year ! Gregorian calender year [yyyy]
-
- ! Local Variables
- integer :: i, j, k, n, l
-
- ! ---- Begin Code ----
-
- L = julian_date+68569 ! Known magic number
- N = 4*L/146097 ! Known magic number
- L = L-(146097*N+3)/4 ! Known magic number
- I = 4000*(L+1)/1461001 ! Known magic number
- L = L-1461*I/4+31 ! Known magic number
- J = 80*L/2447 ! Known magic number
- K = L-2447*J/80 ! Known magic number
- L = J/11 ! Known magic number
- J = J+2-12*L ! Known magic number
- I = 100*(N-49)+I+L ! Known magic number
-
- year = I
- month = J
- day = K
-
- return
-
- end subroutine julian2gregorian_date
-
-!-----------------------------------------------------------------------------
- logical function leap_year( year )
-!
-! Description:
-! Determines if the given year is a leap year.
-!
-! References:
-! None
-!-----------------------------------------------------------------------------
- implicit none
-
- ! External
- intrinsic :: mod
-
- ! Input Variable(s)
- integer, intent(in) :: year ! Gregorian Calendar Year [yyyy]
-
- ! ---- Begin Code ----
-
- leap_year = ( (mod( year, 4 ) == 0) .and. &
- (.not.( mod( year, 100 ) == 0 .and. mod( year, 400 ) /= 0 ) ) )
-
- return
- end function leap_year
-
-!----------------------------------------------------------------------------
- subroutine compute_current_date( previous_day, previous_month, &
- previous_year, &
- seconds_since_previous_date, &
- current_day, current_month, &
- current_year, &
- seconds_since_current_date )
-!
-! Description:
-! Computes the current Gregorian date from a previous date and
-! the seconds that have transpired since that date.
-!
-! References:
-! None
-!----------------------------------------------------------------------------
- use crmx_clubb_precision, only: &
- time_precision ! Variable(s)
-
- use crmx_constants_clubb, only: &
- sec_per_day ! Variable(s)
-
- implicit none
-
- ! Input Variable(s)
-
- ! Previous date
- integer, intent(in) :: &
- previous_day, & ! Day of the month [dd]
- previous_month, & ! Month of the year [mm]
- previous_year ! Year [yyyy]
-
- real(kind=time_precision), intent(in) :: &
- seconds_since_previous_date ! [s]
-
- ! Output Variable(s)
-
- ! Current date
- integer, intent(out) :: &
- current_day, & ! Day of the month [dd]
- current_month, & ! Month of the year [mm]
- current_year ! Year [yyyy]
-
- real(kind=time_precision), intent(out) :: &
- seconds_since_current_date
-
- integer :: &
- days_since_1jan4713bc, &
- days_since_start
-
- ! ---- Begin Code ----
-
- ! Using Julian dates we are able to add the days that the model
- ! has been running
-
- ! Determine the Julian Date of the starting date,
- ! written in Gregorian (day, month, year) form
- days_since_1jan4713bc = gregorian2julian_date( previous_day, &
- previous_month, previous_year )
-
- ! Determine the amount of days that have passed since start date
- days_since_start = &
- floor( seconds_since_previous_date / sec_per_day )
-
- ! Set days_since_1jan4713 to the present Julian date
- days_since_1jan4713bc = days_since_1jan4713bc + days_since_start
-
- ! Set Present time to be seconds since the Julian date
- seconds_since_current_date = seconds_since_previous_date &
- - ( real( days_since_start, kind=time_precision ) * sec_per_day )
-
- call julian2gregorian_date &
- ( days_since_1jan4713bc, &
- current_day, current_month, current_year )
-
- return
- end subroutine compute_current_date
-
-!-------------------------------------------------------------------------------------
- integer function gregorian2julian_day( day, month, year )
-!
-! Description:
-! This subroutine determines the Julian day (1-366)
-! for a given Gregorian calendar date(e.g. July 1, 2008).
-!
-! References:
-! None
-!-------------------------------------------------------------------------------------
-
- implicit none
-
- ! External
- intrinsic :: sum
-
- ! Input Variable(s)
- integer, intent(in) :: &
- day, & ! Day of the Month [dd]
- month, & ! Month of the Year [mm]
- year ! Year [yyyy]
-
- ! ---- Begin Code ----
-
- ! Add the days from the previous months
- gregorian2julian_day = day + sum( days_per_month(1:month-1) )
-
- ! Kluge for a leap year
- ! If the date were 29 Feb 2000 this would not increment julian_day
- ! However 01 March 2000 would need the 1 day bump
- if ( leap_year( year ) .and. month > 2 ) then
- gregorian2julian_day = gregorian2julian_day + 1
- end if
-
- if ( ( leap_year( year ) .and. gregorian2julian_day > 366 ) .or. &
- ( .not. leap_year( year ) .and. gregorian2julian_day > 365 ) ) then
- stop "Problem with Julian day conversion in gregorian2julian_day."
- end if
-
- return
- end function gregorian2julian_day
-
-end module crmx_calendar
diff --git a/src/physics/spcam/crm/CLUBB/crmx_clip_explicit.F90 b/src/physics/spcam/crm/CLUBB/crmx_clip_explicit.F90
deleted file mode 100644
index ce73c9c88a..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_clip_explicit.F90
+++ /dev/null
@@ -1,859 +0,0 @@
-!-------------------------------------------------------------------------------
-! $Id: clip_explicit.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_clip_explicit
-
- implicit none
-
- private
-
- public :: clip_covars_denom, &
- clip_covar, &
- clip_variance, &
- clip_skewness, &
- clip_skewness_core
-
- ! Named constants to avoid string comparisons
- integer, parameter, public :: &
- clip_rtp2 = 1, & ! Named constant for rtp2 clipping
- clip_thlp2 = 2, & ! Named constant for thlp2 clipping
- clip_rtpthlp = 3, & ! Named constant for rtpthlp clipping
- clip_up2 = 5, & ! Named constant for up2 clipping
- clip_vp2 = 6, & ! Named constant for vp2 clipping
-! clip_scalar = 7, & ! Named constant for scalar clipping
- clip_wprtp = 8, & ! Named constant for wprtp clipping
- clip_wpthlp = 9, & ! Named constant for wpthlp clipping
- clip_upwp = 10, & ! Named constant for upwp clipping
- clip_vpwp = 11, & ! Named constant for vpwp clipping
- clip_wp2 = 12, & ! Named constant for wp2 clipping
- clip_wpsclrp = 13, & ! Named constant for wp scalar clipping
- clip_sclrp2 = 14, & ! Named constant for sclrp2 clipping
- clip_sclrprtp = 15, & ! Named constant for sclrprtp clipping
- clip_sclrpthlp = 16 ! Named constant for sclrpthlp clipping
-
- contains
-
- !=============================================================================
- subroutine clip_covars_denom( dt, rtp2, thlp2, up2, vp2, wp2, &
- sclrp2, wprtp_cl_num, wpthlp_cl_num, &
- wpsclrp_cl_num, upwp_cl_num, vpwp_cl_num, &
- wprtp, wpthlp, upwp, vpwp, wpsclrp )
-
- ! Description:
- ! Some of the covariances found in the CLUBB model code need to be clipped
- ! multiple times during each timestep to ensure that the correlation between
- ! the two relevant variables stays between -1 and 1 at all times during the
- ! model run. The covariances that need to be clipped multiple times are
- ! w'r_t', w'th_l', w'sclr', u'w', and v'w'. One of the times that each one
- ! of these covariances is clipped is immediately after each one is set.
- ! However, each covariance still needs to be clipped two more times during
- ! each timestep (once after advance_xp2_xpyp is called and once after
- ! advance_wp2_wp3 is called). This subroutine handles the times that the
- ! covariances are clipped away from the time that they are set. In other
- ! words, this subroutine clips the covariances after the denominator terms
- ! in the relevant correlation equation have been altered, ensuring that
- ! all correlations will remain between -1 and 1 at all times.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_parameters_model, only: &
- sclr_dim ! Variable(s)
-
- use crmx_model_flags, only: &
- l_tke_aniso ! Logical
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_modify ! Procedure(s)
-
- use crmx_stats_variables, only: &
- iwprtp_bt, & ! Variable(s)
- iwpthlp_bt, &
- zm, &
- l_stats_samp
-
- implicit none
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Timestep [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- rtp2, & ! r_t'^2 [(kg/kg)^2]
- thlp2, & ! theta_l'^2 [K^2]
- up2, & ! u'^2 [m^2/s^2]
- vp2, & ! v'^2 [m^2/s^2]
- wp2 ! w'^2 [m^2/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim), intent(in) :: &
- sclrp2 ! sclr'^2 [{units vary}^2]
-
- integer, intent(in) :: &
- wprtp_cl_num, &
- wpthlp_cl_num, &
- wpsclrp_cl_num, &
- upwp_cl_num, &
- vpwp_cl_num
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wprtp, & ! w'r_t' [(kg/kg) m/s]
- wpthlp, & ! w'theta_l' [K m/s]
- upwp, & ! u'w' [m^2/s^2]
- vpwp ! v'w' [m^2/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim), intent(inout) :: &
- wpsclrp ! w'sclr' [units m/s]
-
- ! Local Variables
- logical :: &
- l_first_clip_ts, & ! First instance of clipping in a timestep.
- l_last_clip_ts ! Last instance of clipping in a timestep.
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wprtp_chnge, & ! Net change in w'r_t' due to clipping [(kg/kg) m/s]
- wpthlp_chnge, & ! Net change in w'th_l' due to clipping [K m/s]
- upwp_chnge, & ! Net change in u'w' due to clipping [m^2/s^2]
- vpwp_chnge ! Net change in v'w' due to clipping [m^2/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim) :: &
- wpsclrp_chnge ! Net change in w'sclr' due to clipping [{units vary}]
-
- integer :: i ! scalar array index.
-
- ! ---- Begin Code ----
-
- !!! Clipping for w'r_t'
- !
- ! Clipping w'r_t' at each vertical level, based on the
- ! correlation of w and r_t at each vertical level, such that:
- ! corr_(w,r_t) = w'r_t' / [ sqrt(w'^2) * sqrt(r_t'^2) ];
- ! -1 <= corr_(w,r_t) <= 1.
- !
- ! Since w'^2, r_t'^2, and w'r_t' are each advanced in different
- ! subroutines from each other in advance_clubb_core, clipping for w'r_t'
- ! is done three times during each timestep (once after each variable has
- ! been updated).
- !
- ! This subroutine handles the first and third instances of
- ! w'r_t' clipping.
- ! The first instance of w'r_t' clipping takes place after
- ! r_t'^2 is updated in advance_xp2_xpyp.
- ! The third instance of w'r_t' clipping takes place after
- ! w'^2 is updated in advance_wp2_wp3.
-
- ! Include effect of clipping in wprtp time tendency budget term.
- if ( l_stats_samp ) then
-
- ! if wprtp_cl_num == 1 do nothing since
- ! iwprtp_bt stat_begin_update is called outside of this method
-
- if ( wprtp_cl_num == 2 ) then
- ! wprtp total time tendency (effect of clipping)
- call stat_modify( iwprtp_bt, -wprtp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- elseif ( wprtp_cl_num == 3 ) then
- ! wprtp total time tendency (effect of clipping)
- call stat_modify( iwprtp_bt, -wprtp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- endif
- endif
-
- ! Used within subroutine clip_covar.
- if ( wprtp_cl_num == 1 ) then
- l_first_clip_ts = .true.
- l_last_clip_ts = .false.
- elseif ( wprtp_cl_num == 2 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .false.
- elseif ( wprtp_cl_num == 3 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- endif
-
- ! Clip w'r_t'
- call clip_covar( clip_wprtp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, rtp2, & ! intent(in)
- wprtp, wprtp_chnge ) ! intent(inout)
-
- if ( l_stats_samp ) then
- if ( wprtp_cl_num == 1 ) then
- ! wprtp total time tendency (effect of clipping)
- call stat_modify( iwprtp_bt, wprtp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- elseif ( wprtp_cl_num == 2 ) then
- ! wprtp total time tendency (effect of clipping)
- call stat_modify( iwprtp_bt, wprtp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- ! if wprtp_cl_num == 3 do nothing since
- ! iwprtp_bt stat_end_update is called outside of this method
-
- endif
- endif
-
-
- !!! Clipping for w'th_l'
- !
- ! Clipping w'th_l' at each vertical level, based on the
- ! correlation of w and th_l at each vertical level, such that:
- ! corr_(w,th_l) = w'th_l' / [ sqrt(w'^2) * sqrt(th_l'^2) ];
- ! -1 <= corr_(w,th_l) <= 1.
- !
- ! Since w'^2, th_l'^2, and w'th_l' are each advanced in different
- ! subroutines from each other in advance_clubb_core, clipping for w'th_l'
- ! is done three times during each timestep (once after each variable has
- ! been updated).
- !
- ! This subroutine handles the first and third instances of
- ! w'th_l' clipping.
- ! The first instance of w'th_l' clipping takes place after
- ! th_l'^2 is updated in advance_xp2_xpyp.
- ! The third instance of w'th_l' clipping takes place after
- ! w'^2 is updated in advance_wp2_wp3.
-
- ! Include effect of clipping in wpthlp time tendency budget term.
- if ( l_stats_samp ) then
-
- ! if wpthlp_cl_num == 1 do nothing since
- ! iwpthlp_bt stat_begin_update is called outside of this method
-
- if ( wpthlp_cl_num == 2 ) then
- ! wpthlp total time tendency (effect of clipping)
- call stat_modify( iwpthlp_bt, -wpthlp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- elseif ( wpthlp_cl_num == 3 ) then
- ! wpthlp total time tendency (effect of clipping)
- call stat_modify( iwpthlp_bt, -wpthlp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- endif
- endif
-
- ! Used within subroutine clip_covar.
- if ( wpthlp_cl_num == 1 ) then
- l_first_clip_ts = .true.
- l_last_clip_ts = .false.
- elseif ( wpthlp_cl_num == 2 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .false.
- elseif ( wpthlp_cl_num == 3 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- endif
-
- ! Clip w'th_l'
- call clip_covar( clip_wpthlp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, thlp2, & ! intent(in)
- wpthlp, wpthlp_chnge ) ! intent(inout)
-
-
- if ( l_stats_samp ) then
- if ( wpthlp_cl_num == 1 ) then
- ! wpthlp total time tendency (effect of clipping)
- call stat_modify( iwpthlp_bt, wpthlp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- elseif ( wpthlp_cl_num == 2 ) then
- ! wpthlp total time tendency (effect of clipping)
- call stat_modify( iwpthlp_bt, wpthlp / real( dt, kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
-
- ! if wpthlp_cl_num == 3 do nothing since
- ! iwpthlp_bt stat_end_update is called outside of this method
-
- endif
- endif
-
-
- !!! Clipping for w'sclr'
- !
- ! Clipping w'sclr' at each vertical level, based on the
- ! correlation of w and sclr at each vertical level, such that:
- ! corr_(w,sclr) = w'sclr' / [ sqrt(w'^2) * sqrt(sclr'^2) ];
- ! -1 <= corr_(w,sclr) <= 1.
- !
- ! Since w'^2, sclr'^2, and w'sclr' are each advanced in different
- ! subroutines from each other in advance_clubb_core, clipping for w'sclr'
- ! is done three times during each timestep (once after each variable has
- ! been updated).
- !
- ! This subroutine handles the first and third instances of
- ! w'sclr' clipping.
- ! The first instance of w'sclr' clipping takes place after
- ! sclr'^2 is updated in advance_xp2_xpyp.
- ! The third instance of w'sclr' clipping takes place after
- ! w'^2 is updated in advance_wp2_wp3.
-
- ! Used within subroutine clip_covar.
- if ( wpsclrp_cl_num == 1 ) then
- l_first_clip_ts = .true.
- l_last_clip_ts = .false.
- elseif ( wpsclrp_cl_num == 2 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .false.
- elseif ( wpsclrp_cl_num == 3 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- endif
-
- ! Clip w'sclr'
- do i = 1, sclr_dim, 1
- call clip_covar( clip_wpsclrp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2(:), sclrp2(:,i), & ! intent(in)
- wpsclrp(:,i), wpsclrp_chnge(:,i) ) ! intent(inout)
- enddo
-
-
- !!! Clipping for u'w'
- !
- ! Clipping u'w' at each vertical level, based on the
- ! correlation of u and w at each vertical level, such that:
- ! corr_(u,w) = u'w' / [ sqrt(u'^2) * sqrt(w'^2) ];
- ! -1 <= corr_(u,w) <= 1.
- !
- ! Since w'^2, u'^2, and u'w' are each advanced in different
- ! subroutines from each other in advance_clubb_core, clipping for u'w'
- ! is done three times during each timestep (once after each variable has
- ! been updated).
- !
- ! This subroutine handles the first and second instances of
- ! u'w' clipping.
- ! The first instance of u'w' clipping takes place after
- ! u'^2 is updated in advance_xp2_xpyp.
- ! The second instance of u'w' clipping takes place after
- ! w'^2 is updated in advance_wp2_wp3.
-
- ! Used within subroutine clip_covar.
- if ( upwp_cl_num == 1 ) then
- l_first_clip_ts = .true.
- l_last_clip_ts = .false.
- elseif ( upwp_cl_num == 2 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .false.
- elseif ( upwp_cl_num == 3 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- endif
-
- ! Clip u'w'
- if ( l_tke_aniso ) then
- call clip_covar( clip_upwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, up2, & ! intent(in)
- upwp, upwp_chnge ) ! intent(inout)
- else
- ! In this case, up2 = wp2, and the variable `up2' does not interact
- call clip_covar( clip_upwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, wp2, & ! intent(in)
- upwp, upwp_chnge ) ! intent(inout)
- end if
-
-
-
- !!! Clipping for v'w'
- !
- ! Clipping v'w' at each vertical level, based on the
- ! correlation of v and w at each vertical level, such that:
- ! corr_(v,w) = v'w' / [ sqrt(v'^2) * sqrt(w'^2) ];
- ! -1 <= corr_(v,w) <= 1.
- !
- ! Since w'^2, v'^2, and v'w' are each advanced in different
- ! subroutines from each other in advance_clubb_core, clipping for v'w'
- ! is done three times during each timestep (once after each variable has
- ! been updated).
- !
- ! This subroutine handles the first and second instances of
- ! v'w' clipping.
- ! The first instance of v'w' clipping takes place after
- ! v'^2 is updated in advance_xp2_xpyp.
- ! The second instance of v'w' clipping takes place after
- ! w'^2 is updated in advance_wp2_wp3.
-
- ! Used within subroutine clip_covar.
- if ( vpwp_cl_num == 1 ) then
- l_first_clip_ts = .true.
- l_last_clip_ts = .false.
- elseif ( vpwp_cl_num == 2 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .false.
- elseif ( vpwp_cl_num == 3 ) then
- l_first_clip_ts = .false.
- l_last_clip_ts = .true.
- endif
-
- if ( l_tke_aniso ) then
- call clip_covar( clip_vpwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, vp2, & ! intent(in)
- vpwp, vpwp_chnge ) ! intent(inout)
- else
- ! In this case, vp2 = wp2, and the variable `vp2' does not interact
- call clip_covar( clip_vpwp, l_first_clip_ts, & ! intent(in)
- l_last_clip_ts, dt, wp2, wp2, & ! intent(in)
- vpwp, vpwp_chnge ) ! intent(inout)
- end if
-
-
- return
- end subroutine clip_covars_denom
-
- !=============================================================================
- subroutine clip_covar( solve_type, l_first_clip_ts, &
- l_last_clip_ts, dt, xp2, yp2, &
- xpyp, xpyp_chnge )
-
- ! Description:
- ! Clipping the value of covariance x'y' based on the correlation between x
- ! and y.
- !
- ! The correlation between variables x and y is:
- !
- ! corr_(x,y) = x'y' / [ sqrt(x'^2) * sqrt(y'^2) ];
- !
- ! where x'^2 is the variance of x, y'^2 is the variance of y, and x'y' is
- ! the covariance of x and y.
- !
- ! The correlation of two variables must always have a value between -1
- ! and 1, such that:
- !
- ! -1 <= corr_(x,y) <= 1.
- !
- ! Therefore, there is an upper limit on x'y', such that:
- !
- ! x'y' <= [ sqrt(x'^2) * sqrt(y'^2) ];
- !
- ! and a lower limit on x'y', such that:
- !
- ! x'y' >= -[ sqrt(x'^2) * sqrt(y'^2) ].
- !
- ! The values of x'y', x'^2, and y'^2 are all found on momentum levels.
- !
- ! The value of x'y' may need to be clipped whenever x'y', x'^2, or y'^2 is
- ! updated.
- !
- ! The following covariances are found in the code:
- !
- ! w'r_t', w'th_l', w'sclr', (computed in advance_xm_wpxp);
- ! r_t'th_l', sclr'r_t', sclr'th_l', (computed in advance_xp2_xpyp);
- ! u'w', v'w', w'edsclr' (computed in advance_windm_edsclrm).
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- max_mag_correlation ! Constant(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_modify, &
- stat_end_update
-
- use crmx_stats_variables, only: &
- zm, & ! Variable(s)
- iwprtp_cl, &
- iwpthlp_cl, &
- irtpthlp_cl, &
- l_stats_samp
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Variable being solved; used for STATS.
-
- logical, intent(in) :: &
- l_first_clip_ts, & ! First instance of clipping in a timestep.
- l_last_clip_ts ! Last instance of clipping in a timestep.
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep; used here for STATS [s]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- xp2, & ! Variance of x, x'^2 (momentum levels) [{x units}^2]
- yp2 ! Variance of y, y'^2 (momentum levels) [{y units}^2]
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- xpyp ! Covariance of x and y, x'y' (momentum levels) [{x units}*{y units}]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(out) :: &
- xpyp_chnge ! Net change in x'y' due to clipping [{x units}*{y units}]
-
-
- ! Local Variable
- integer :: k ! Array index
-
- integer :: &
- ixpyp_cl
-
- ! ---- Begin Code ----
-
- select case ( solve_type )
- case ( clip_wprtp ) ! wprtp clipping budget term
- ixpyp_cl = iwprtp_cl
- case ( clip_wpthlp ) ! wpthlp clipping budget term
- ixpyp_cl = iwpthlp_cl
- case ( clip_rtpthlp ) ! rtpthlp clipping budget term
- ixpyp_cl = irtpthlp_cl
- case default ! scalars (or upwp/vpwp) are involved
- ixpyp_cl = 0
- end select
-
-
- if ( l_stats_samp ) then
- if ( l_first_clip_ts ) then
- call stat_begin_update( ixpyp_cl, xpyp / real( dt, kind = core_rknd ), zm )
- else
- call stat_modify( ixpyp_cl, -xpyp / real( dt, kind = core_rknd ), zm )
- endif
- endif
-
- ! The value of x'y' at the surface (or lower boundary) is a set value that
- ! is either specified or determined elsewhere in a surface subroutine. It
- ! is ensured elsewhere that the correlation between x and y at the surface
- ! (or lower boundary) is between -1 and 1. Thus, the covariance clipping
- ! code does not need to be invoked at the lower boundary. Likewise, the
- ! value of x'y' is set at the upper boundary, so the covariance clipping
- ! code does not need to be invoked at the upper boundary.
- ! Note that if clipping were applied at the lower boundary, momentum will
- ! not be conserved, therefore it should never be added.
- do k = 2, gr%nz-1, 1
-
- ! Clipping for xpyp at an upper limit corresponding with a correlation
- ! between x and y of max_mag_correlation.
- if ( xpyp(k) > max_mag_correlation * sqrt( xp2(k) * yp2(k) ) ) then
-
- xpyp_chnge(k) = max_mag_correlation * sqrt( xp2(k) * yp2(k) ) - xpyp(k)
-
- xpyp(k) = max_mag_correlation * sqrt( xp2(k) * yp2(k) )
-
- ! Clipping for xpyp at a lower limit corresponding with a correlation
- ! between x and y of -max_mag_correlation.
- elseif ( xpyp(k) < -max_mag_correlation * sqrt( xp2(k) * yp2(k) ) ) then
-
- xpyp_chnge(k) = -max_mag_correlation * sqrt( xp2(k) * yp2(k) ) - xpyp(k)
-
- xpyp(k) = -max_mag_correlation * sqrt( xp2(k) * yp2(k) )
-
- else
-
- xpyp_chnge(k) = 0.0_core_rknd
-
- endif
-
- enddo ! k = 2..gr%nz
-
- ! Since there is no covariance clipping at the upper or lower boundaries,
- ! the change in x'y' due to covariance clipping at those levels is 0.
- xpyp_chnge(1) = 0.0_core_rknd
- xpyp_chnge(gr%nz) = 0.0_core_rknd
-
- if ( l_stats_samp ) then
- if ( l_last_clip_ts ) then
- call stat_end_update( ixpyp_cl, xpyp / real( dt, kind = core_rknd ), zm )
- else
- call stat_modify( ixpyp_cl, xpyp / real( dt, kind = core_rknd ), zm )
- endif
- endif
-
-
- return
- end subroutine clip_covar
-
- !=============================================================================
- subroutine clip_variance( solve_type, dt, threshold, &
- xp2 )
-
- ! Description:
- ! Clipping the value of variance x'^2 based on a minimum threshold value.
- ! The threshold value must be greater than or equal to 0.
- !
- ! The values of x'^2 are found on the momentum levels.
- !
- ! The following variances are found in the code:
- !
- ! r_t'^2, th_l'^2, u'^2, v'^2, sclr'^2, (computed in advance_xp2_xpyp);
- ! w'^2 (computed in advance_wp2_wp3).
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_end_update
-
- use crmx_stats_variables, only: &
- zm, & ! Variable(s)
- iwp2_cl, &
- irtp2_cl, &
- ithlp2_cl, &
- iup2_cl, &
- ivp2_cl, &
- l_stats_samp
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- solve_type ! Variable being solved; used for STATS.
-
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep; used here for STATS [s]
-
- real( kind = core_rknd ), intent(in) :: &
- threshold ! Minimum value of x'^2 [{x units}^2]
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- xp2 ! Variance of x, x'^2 (momentum levels) [{x units}^2]
-
- ! Local Variables
- integer :: k ! Array index
-
-
- integer :: &
- ixp2_cl
-
- ! ---- Begin Code ----
-
- select case ( solve_type )
- case ( clip_wp2 ) ! wp2 clipping budget term
- ixp2_cl = iwp2_cl
- case ( clip_rtp2 ) ! rtp2 clipping budget term
- ixp2_cl = irtp2_cl
- case ( clip_thlp2 ) ! thlp2 clipping budget term
- ixp2_cl = ithlp2_cl
- case ( clip_up2 ) ! up2 clipping budget term
- ixp2_cl = iup2_cl
- case ( clip_vp2 ) ! vp2 clipping budget term
- ixp2_cl = ivp2_cl
- case default ! scalars are involved
- ixp2_cl = 0
- end select
-
-
- if ( l_stats_samp ) then
- call stat_begin_update( ixp2_cl, xp2 / real( dt, kind = core_rknd ), zm )
- endif
-
- ! Limit the value of x'^2 at threshold.
- ! The value of x'^2 at the surface (or lower boundary) is a set value that
- ! is determined elsewhere in a surface subroutine. Thus, the variance
- ! clipping code does not need to be invoked at the lower boundary.
- ! Likewise, the value of x'^2 is set at the upper boundary, so the variance
- ! clipping code does not need to be invoked at the upper boundary.
- do k = 2, gr%nz-1, 1
- if ( xp2(k) < threshold ) then
- xp2(k) = threshold
- endif
- enddo
-
- if ( l_stats_samp ) then
- call stat_end_update( ixp2_cl, xp2 / real( dt, kind = core_rknd ), zm )
- endif
-
-
- return
- end subroutine clip_variance
-
- !=============================================================================
- subroutine clip_skewness( dt, sfc_elevation, wp2_zt, wp3 )
-
- ! Description:
- ! Clipping the value of w'^3 based on the skewness of w, Sk_w.
- !
- ! Aditionally, to prevent possible crashes due to wp3 growing too large,
- ! abs(wp3) will be clipped to 100.
- !
- ! The skewness of w is:
- !
- ! Sk_w = w'^3 / (w'^2)^(3/2).
- !
- ! The value of Sk_w is limited to a range between an upper limit and a lower
- ! limit. The values of the limits depend on whether the level altitude is
- ! within 100 meters of the surface.
- !
- ! For altitudes less than or equal to 100 meters above ground level (AGL):
- !
- ! -0.2_core_rknd*sqrt(2) <= Sk_w <= 0.2_core_rknd*sqrt(2);
- !
- ! while for all altitudes greater than 100 meters AGL:
- !
- ! -4.5_core_rknd <= Sk_w <= 4.5_core_rknd.
- !
- ! Therefore, there is an upper limit on w'^3, such that:
- !
- ! w'^3 <= threshold_magnitude * (w'^2)^(3/2);
- !
- ! and a lower limit on w'^3, such that:
- !
- ! w'^3 >= -threshold_magnitude * (w'^2)^(3/2).
- !
- ! The values of w'^3 are found on the thermodynamic levels, while the values
- ! of w'^2 are found on the momentum levels. Therefore, the values of w'^2
- ! are interpolated to the thermodynamic levels before being used to
- ! calculate the upper and lower limits for w'^3.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_stats_type, only: &
- stat_begin_update, & ! Procedure(s)
- stat_end_update
-
- use crmx_stats_variables, only: &
- zt, & ! Variable(s)
- iwp3_cl, &
- l_stats_samp
-
- implicit none
-
- ! External
- intrinsic :: sign, sqrt, real
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep; used here for STATS [s]
-
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2_zt ! w'^2 interpolated to thermodyamic levels [m^2/s^2]
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp3 ! w'^3 (thermodynamic levels) [m^3/s^3]
-
- ! ---- Begin Code ----
-
- if ( l_stats_samp ) then
- call stat_begin_update( iwp3_cl, wp3 / real( dt, kind = core_rknd ), zt )
- endif
-
- call clip_skewness_core( sfc_elevation, wp2_zt, wp3 )
-
- if ( l_stats_samp ) then
- call stat_end_update( iwp3_cl, wp3 / real( dt, kind = core_rknd ), zt )
- endif
-
- return
- end subroutine clip_skewness
-
-!=============================================================================
- subroutine clip_skewness_core( sfc_elevation, wp2_zt, wp3 )
-!
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_constants_clubb, only: &
- Skw_max_mag_sqd ! [-]
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: sign, sqrt, real
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wp2_zt ! w'^2 interpolated to thermodyamic levels [m^2/s^2]
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wp3 ! w'^3 (thermodynamic levels) [m^3/s^3]
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wp2_zt_cubed, & ! Variance of vertical velocity cubed (w^2_{zt}^3) [m^6/s^6]
- wp3_lim_sqd ! Keeps absolute value of Sk_w from becoming > limit [m^6/s^6]
-
- integer :: k ! Vertical array index.
-
- real( kind = core_rknd ), parameter :: &
- wp3_max = 100._core_rknd ! Threshold for wp3 [m^3/s^3]
-
- ! ---- Begin Code ----
-
- ! Compute the upper and lower limits of w'^3 at every level,
- ! based on the skewness of w, Sk_w, such that:
- ! Sk_w = w'^3 / (w'^2)^(3/2);
- ! -4.5 <= Sk_w <= 4.5;
- ! or, if the level altitude is within 100 meters of the surface,
- ! -0.2*sqrt(2) <= Sk_w <= 0.2*sqrt(2).
-
- ! The normal magnitude limit of skewness of w in the CLUBB code is 4.5.
- ! However, according to Andre et al. (1976b & 1978), wp3 should not exceed
- ! [2*(wp2^3)]^(1/2) at any level. However, this term should be multiplied
- ! by 0.2 close to the surface to include surface effects. There already is
- ! a wp3 clipping term in place for all other altitudes, but this term will
- ! be included for the surface layer only. Therefore, the lowest level wp3
- ! should not exceed 0.2 * sqrt(2) * wp2^(3/2). Brian Griffin. 12/18/05.
-
- ! To lower compute time, we squared both sides of the equation and compute
- ! wp2^3 only once. -dschanen 9 Oct 2008
-
- wp2_zt_cubed(1:gr%nz) = wp2_zt(1:gr%nz)**3
-
- do k = 1, gr%nz, 1
- if ( gr%zt(k) - sfc_elevation <= 100.0_core_rknd ) then ! Clip for 100 m. AGL.
- !wp3_upper_lim(k) = 0.2_core_rknd * sqrt_2 * wp2_zt(k)**(3.0_core_rknd/2.0_core_rknd)
- !wp3_lower_lim(k) = -0.2_core_rknd * sqrt_2 * wp2_zt(k)**(3.0_core_rknd/2.0_core_rknd)
- wp3_lim_sqd(k) = 0.08_core_rknd * wp2_zt_cubed(k) ! Where 0.08_core_rknd
- ! == (sqrt(2)*0.2_core_rknd)**2 known magic number
- else ! Clip skewness consistently with a.
- !wp3_upper_lim(k) = 4.5_core_rknd * wp2_zt(k)**(3.0_core_rknd/2.0_core_rknd)
- !wp3_lower_lim(k) = -4.5_core_rknd * wp2_zt(k)**(3.0_core_rknd/2.0_core_rknd)
- wp3_lim_sqd(k) = Skw_max_mag_sqd * wp2_zt_cubed(k) ! Skw_max_mag = 4.5_core_rknd^2
- endif
- enddo
-
- ! Clipping for w'^3 at an upper and lower limit corresponding with
- ! the appropriate value of Sk_w.
- where ( wp3**2 > wp3_lim_sqd ) &
- ! Set the magnitude to the wp3 limit and apply the sign of the current wp3
- wp3 = sign( sqrt( wp3_lim_sqd ), wp3 )
-
- ! Clipping abs(wp3) to 100. This keeps wp3 from growing too large in some
- ! deep convective cases, which helps prevent these cases from blowing up.
- where ( abs(wp3) > wp3_max ) &
- wp3 = sign( wp3_max , wp3 ) ! Known magic number
-
- end subroutine clip_skewness_core
-
-!===============================================================================
-
-end module crmx_clip_explicit
diff --git a/src/physics/spcam/crm/CLUBB/crmx_clip_semi_implicit.F90 b/src/physics/spcam/crm/CLUBB/crmx_clip_semi_implicit.F90
deleted file mode 100644
index 4447d88325..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_clip_semi_implicit.F90
+++ /dev/null
@@ -1,660 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: clip_semi_implicit.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_clip_semi_implicit
-
- ! Description of the semi-implicit clipping code:
- ! The semi-implicit clipping code is based on an upper threshold and/or a
- ! lower threshold value for variable f.
- !
- ! The semi-implicit clipping code is used when the value of variable f should
- ! not exceed the designated threshold(s) when it is advanced to timestep
- ! index (t+1).
- !
- !
- ! Clipping at an Upper Threshold:
- !
- ! When there is an upper threshold to be applied, the equation for the clipped
- ! value of the variable f, f_clipped, is:
- !
- ! f_clipped(t+1) = MIN( f_unclipped(t+1), upper_threshold )
- ! = ( f_unclipped(t+1) - upper_threshold )
- ! * H(upper_threshold-f_unclipped(t+1))
- ! + upper_threshold;
- !
- ! where f_unclipped is the value of the variable f without clipping, and
- ! H(upper_threshold-f_unclipped(t+1)) is the Heaviside Step function. The
- ! clipping term is turned into a time tendency term, such that:
- !
- ! (df/dt)_clipping = (1/dt_clip)
- ! * ( f_clipped(t+1) - f_unclipped(t+1) );
- !
- ! where dt_clip is the time scale for the clipping term. The difference
- ! between the threshold value and f_unclipped is defined as f_diff:
- !
- ! f_diff = upper_threshold - f_unclipped.
- !
- ! The clipping time tendency is now simplified as:
- !
- ! (df/dt)_clipping = + (1/dt_clip)
- ! * { f_diff(t+1) * [ 1 - H(f_diff(t+1)) ] }.
- !
- ! Function R(f_diff) is defined as:
- !
- ! R(f_diff) = { f_diff * [ 1 - H(f_diff) ] }.
- !
- ! The clipping time tendency is now written as:
- !
- ! (df/dt)_clipping = + (1/dt_clip) * R(f_diff(t+1)).
- !
- ! In order to solve for f_unclipped (and f_diff) at timestep index (t+1), the
- ! clipping term must be linearized. A Taylor Series expansion (truncated
- ! after the first derivative term) of R(f_diff) around f_diff = f_diff(t) is
- ! used to linearize the term. However, the Heaviside Step function,
- ! H(f_diff), is not differentiable when f_diff(t) = 0, as the function jumps
- ! at that point. Likewise, the function R(f_diff) is not differentiable when
- ! f_diff(t) = 0, as the function has a corner at that point. Therefore, a new
- ! function, F_R(f_diff) is used as an approximation of R(f_diff). Function
- ! F_R(f_diff) is a three-piece function that has the exact same value as
- ! R(f_diff) when f_diff <= -sigma or f_diff >= sigma (sigma is an arbitrarily
- ! declared value). However, when -sigma < f_diff < sigma, a parabolic
- ! function is used to approximate the corner found in R(f_diff). The
- ! parabolic function needs to have the same values at f_diff = -sigma and
- ! f_diff = sigma as does R(f_diff). Furthermore, the derivative of the
- ! parabolic function (with respect to f_diff) needs to have the same values at
- ! f_diff = -sigma and f_diff = sigma as does d(R)/d(f_diff). The parabolic
- ! function that satisfies these properities is:
- ! f_diff - (sigma/4) * [ 1 + (f_diff/sigma) ]^2.
- ! Therefore:
- !
- ! | f_diff; where f_diff <= -sigma
- ! |
- ! F_R(f_diff) = | f_diff - (sigma/4) * [ 1 + (f_diff/sigma) ]^2;
- ! | where -sigma < f_diff < sigma
- ! |
- ! | 0; where f_diff >= sigma; and
- !
- ! | 1; where f_diff <= -sigma
- ! |
- ! ( d F_R / d f_diff ) = | 1 - (1/2) * [ 1 + (f_diff/sigma) ];
- ! | where -sigma < f_diff < sigma
- ! |
- ! | 0; where f_diff >= sigma.
- !
- ! Since, R(f_diff(t+1)) approx.= F_R(f_diff(t+1)), the Taylor Series expansion
- ! is done for F_R(f_diff) around f_diff = f_diff(t) in order to linearize the
- ! term:
- !
- ! F_R(f_diff(t+1)) approx.=
- ! A_fnc + B_fnc * ( f_diff(t+1) - f_diff(t) );
- !
- ! where A_fnc is defined as F_R(f_diff(t)) and B_fnc is defined as
- ! ( d F_R / d f_diff )|_(f_diff=f_diff(t)).
- !
- ! The approximation is substituted into the (df/dt)_clipping equation. The
- ! rate of change of variable f due to clipping with the upper threshold is:
- !
- ! (df/dt)_clipping
- ! = + (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t)
- ! + B_fnc * upper_threshold - B_fnc * f_unclipped(t+1) }.
- !
- ! The implicit (LHS) portion of the equation for clipping with the upper
- ! threshold is:
- !
- ! - (1/dt_clip) * B_fnc * f_unclipped(t+1).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The explicit (RHS) portion of the equation for clipping with the upper
- ! threshold is:
- !
- ! + (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t) + B_fnc * upper_threshold }.
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(f)/dt equation.
- !
- !
- ! Clipping at a Lower Threshold:
- !
- ! When there is a lower threshold to be applied, the equation for the clipped
- ! value of the variable f, f_clipped, is:
- !
- ! f_clipped(t+1) = MAX( f_unclipped(t+1), lower_threshold )
- ! = ( f_unclipped(t+1) - lower_threshold )
- ! * H(f_unclipped(t+1)-lower_threshold)
- ! + lower_threshold;
- !
- ! where f_unclipped is the value of the variable f without clipping, and
- ! H(f_unclipped(t+1)-lower_threshold) is the Heaviside Step function. The
- ! clipping term is turned into a time tendency term, such that:
- !
- ! (df/dt)_clipping = (1/dt_clip)
- ! * ( f_clipped(t+1) - f_unclipped(t+1) );
- !
- ! where dt_clip is the time scale for the clipping term. The difference
- ! between f_unclipped and the threshold value is defined as f_diff:
- !
- ! f_diff = f_unclipped - lower_threshold.
- !
- ! The clipping time tendency is now simplified as:
- !
- ! (df/dt)_clipping = - (1/dt_clip)
- ! * { f_diff(t+1) * [ 1 - H(f_diff(t+1)) ] }.
- !
- ! Function R(f_diff) is defined as:
- !
- ! R(f_diff) = { f_diff * [ 1 - H(f_diff) ] }.
- !
- ! The clipping time tendency is now written as:
- !
- ! (df/dt)_clipping = - (1/dt_clip) * R(f_diff(t+1)).
- !
- ! The linearization process is the same for the lower threshold as it is for
- ! the upper threshold. The formulas for A_fnc and B_fnc are the same, but the
- ! values (based on a different f_diff) are different. The rate of change of
- ! variable f due to clipping with the lower threshold is:
- !
- ! (df/dt)_clipping
- ! = - (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t)
- ! - B_fnc * lower_threshold + B_fnc * f_unclipped(t+1) }.
- !
- ! The implicit (LHS) portion of the equation for clipping with the lower
- ! threshold is:
- !
- ! - (1/dt_clip) * B_fnc * f_unclipped(t+1).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The explicit (RHS) portion of the equation for clipping with the lower
- ! threshold is:
- !
- ! - (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t) - B_fnc * lower_threshold }.
- !
- ! All variables in these equations are on the same vertical levels as the
- ! variable f.
- !
- !
- ! Adjustable parameters:
- !
- ! sigma: sigma is the amount on either side of the threshold value to which
- ! the parabolic function portion of F_R(f_diff) is applied. The value
- ! of sigma must be greater than 0. A proportionally larger value of
- ! sigma can be used to effect values of f that are near the threshold,
- ! but not to it or over it. The close-to-threshold values will be
- ! nudged away from the threshold.
- !
- ! dt_clip: dt_clip is the clipping time scale. It can be set equal to the
- ! model timestep, dt, but it doesn't have to be. Smaller values of
- ! dt_clip produce a greater effect on the clipping term.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- implicit none
-
- private
-
- public :: clip_semi_imp_lhs, &
- clip_semi_imp_rhs
-
- private :: compute_clip_lhs, &
- compute_fncts_A_B
-
- ! Constant parameters.
-
- ! sigma coefficient: A coefficient with dimensionless units that must have a
- ! value greater than 0. The value should be kept below 1.
- ! The larger the value of sigma_coef, the larger the value
- ! of sigma, and the larger the range of close-to-threshold
- ! values that will be effected (nudged away from the
- ! threshold) by the semi-implicit clipping.
- real( kind = core_rknd ), parameter :: sigma_coef = 0.15_core_rknd
-
- ! dt_clip coefficient: A coefficient with dimensionless units that must have
- ! a value greater than 0. A value of 1 will set the
- ! clipping time scale, dt_clip, equal to the model
- ! timestep, dt. The smaller the value of dt_clip_coef,
- ! the smaller the value of dt_clip, and the larger the
- ! magnitude of (df/dt)_clipping.
- real(kind=time_precision), parameter :: dt_clip_coef = 1.0_time_precision
-
- contains
-
- !=============================================================================
- function clip_semi_imp_lhs( dt, f_unclipped, &
- l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold ) &
- result( lhs )
-
- ! Description:
- ! The implicit portion of the semi-implicit clipping code.
- !
- ! The implicit (LHS) portion of the equation for clipping with the upper
- ! threshold is:
- !
- ! - (1/dt_clip) * B_fnc * f_unclipped(t+1).
- !
- ! The implicit (LHS) portion of the equation for clipping with the lower
- ! threshold is:
- !
- ! - (1/dt_clip) * B_fnc * f_unclipped(t+1).
- !
- ! Note: When either term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of either term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of f being used is from the
- ! next timestep, which is being advanced to in solving the d(f)/dt equation.
- !
- ! While the formulas are the same for both the upper threshold and the lower
- ! threshold, the values of A_fnc, B_fnc, and f_diff will differ between the
- ! two thresholds.
- !
- ! The overall implicit (LHS) portion for the clipping term is the sum of the
- ! implicit portion from the upper threshold and the implicit portion from
- ! the lower threshold.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)implicit none
-
- implicit none
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep. [s]
-
- real( kind = core_rknd ), intent(in) :: &
- f_unclipped, & ! The unclipped value of variable f at timestep (t). [f units]
- upper_threshold, & ! Greatest allowable value of variable f. [f units]
- lower_threshold ! Smallest allowable value of variable f. [f units]
-
- logical, intent(in) :: &
- l_upper_thresh, & ! Flag for having an upper threshold value.
- l_lower_thresh ! Flag for having a lower threshold value.
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs
-
- ! Local Variables
- real(kind=time_precision) :: &
- dt_clip ! Time scale for semi-implicit clipping term. [s]
-
- real( kind = core_rknd ) :: &
- f_diff, & ! Difference between the threshold value and f_unclipped. [f units]
- A_fnc, & ! Function that approximates { f_diff * [ 1 - H(f_diff) ] }. [f units]
- B_fnc, & ! Derivative w/ respect to f_diff of function A_fnc. []
- lhs_upper, & ! Contribution of upper threshold to implicit portion (LHS). [s^-1]
- lhs_lower ! Contribution of lower threshold to implicit portion (LHS). [s^-1]
-
-
- ! Compute the clipping time scale, dt_clip.
- dt_clip = dt_clip_coef * dt
-
-
- ! Upper Threshold
- if ( l_upper_thresh ) then
-
- ! f_diff is the difference between the threshold value and f_unclipped.
- ! In regards to the upper threshold, it is defined as
- ! upper_threshold - f_unclipped.
- f_diff = upper_threshold - f_unclipped
-
- ! Compute the values of functions A_fnc and B_fnc evaluated at f_diff(t)
- ! for the upper threshold.
- call compute_fncts_A_B( l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold, &
- f_diff, A_fnc, B_fnc )
-
- ! Compute the implicit (LHS) contribution from clipping for the upper
- ! threshold.
- lhs_upper = compute_clip_lhs( dt_clip, B_fnc )
-
- else
-
- lhs_upper = 0.0_core_rknd
-
- endif
-
-
- ! Lower Threshold
- if ( l_lower_thresh ) then
-
- ! f_diff is the difference between the threshold value and f_unclipped.
- ! In regards to the lower threshold, it is defined as
- ! f_unclipped - lower_threshold.
- f_diff = f_unclipped - lower_threshold
-
- ! Compute the values of functions A_fnc and B_fnc evaluated at f_diff(t)
- ! for the lower threshold.
- call compute_fncts_A_B( l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold, &
- f_diff, A_fnc, B_fnc )
-
- ! Compute the implicit (LHS) contribution from clipping for the lower
- ! threshold.
- lhs_lower = compute_clip_lhs( dt_clip, B_fnc )
-
- else
-
- lhs_lower = 0.0_core_rknd
-
- endif
-
-
- ! Total implicit (LHS) contribution to clipping.
- ! Main diagonal: [ x f_unclipped(k,) ]
- lhs = lhs_upper + lhs_lower
-
-
- end function clip_semi_imp_lhs
-
- !=============================================================================
- pure function compute_clip_lhs( dt_clip, B_fnc ) &
- result( lhs_contribution )
-
- ! Description:
- ! Calculation of the implicit portion of the semi-implicit clipping term.
- !
- ! The implicit portion of the semi-implicit clipping term is:
- !
- ! - (1/dt_clip) * B_fnc * f_unclipped(t+1).
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of f being used is from the
- ! next timestep, which is being advanced to in solving the d(f)/dt equation.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt_clip ! Time scale for semi-implicit clipping term. [s]
-
- real( kind = core_rknd ), intent(in) :: &
- B_fnc ! Derivative w/ respect to f_diff of function A_fnc. []
-
- ! Return Variable
- real( kind = core_rknd ) :: lhs_contribution
-
-
- ! Main diagonal: [ x f_unclipped(k,) ]
- lhs_contribution &
- = + (1.0_core_rknd/real( dt_clip, kind = core_rknd ) * B_fnc )
-
-
- end function compute_clip_lhs
-
- !=============================================================================
- function clip_semi_imp_rhs( dt, f_unclipped, &
- l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold ) &
- result( rhs )
-
- ! Description:
- ! The explicit portion of the semi-implicit clipping code.
- !
- ! The explicit (RHS) portion of the equation for clipping with the upper
- ! threshold is:
- !
- ! + (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t) + B_fnc * upper_threshold }.
- !
- ! The explicit (RHS) portion of the equation for clipping with the lower
- ! threshold is:
- !
- ! - (1/dt_clip)
- ! * { A_fnc - B_fnc * f_diff(t) - B_fnc * lower_threshold }.
- !
- ! Timestep index (t) stands for the index of the current timestep.
- !
- ! The values of A_fnc, B_fnc, and f_diff will differ between the two
- ! thresholds.
- !
- ! The overall explicit (RHS) portion for the clipping term is the sum of the
- ! explicit portion from the upper threshold and the explicit portion from
- ! the lower threshold.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real(kind=time_precision), intent(in) :: &
- dt ! Model timestep. [s]
-
- real( kind = core_rknd ), intent(in) :: &
- f_unclipped, & ! The unclipped value of variable f at timestep (t). [f units]
- upper_threshold, & ! Greatest allowable value of variable f. [f units]
- lower_threshold ! Smallest allowable value of variable f. [f units]
-
- logical, intent(in) :: &
- l_upper_thresh, & ! Flag for having an upper threshold value.
- l_lower_thresh ! Flag for having a lower threshold value.
-
- ! Return Variable
- real( kind = core_rknd ) :: rhs
-
- ! Local Variables
- real(kind=time_precision) :: &
- dt_clip ! Time scale for semi-implicit clipping term. [s]
-
- real( kind = core_rknd ) :: &
- f_diff, & ! Difference between the threshold value and f_unclipped. [f units]
- A_fnc, & ! Function that approximates { f_diff * [ 1 - H(f_diff) ] }. [f units]
- B_fnc, & ! Derivative w/ respect to f_diff of function A_fnc. []
- rhs_upper, & ! Contribution of upper threshold to explicit portion (RHS). [s^-1]
- rhs_lower ! Contribution of lower threshold to explicit portion (RHS). [s^-1]
-
-
- ! Compute the clipping time scale, dt_clip.
- dt_clip = dt_clip_coef * dt
-
-
- ! Upper Threshold
- if ( l_upper_thresh ) then
-
- ! f_diff is the difference between the threshold value and f_unclipped.
- ! In regards to the upper threshold, it is defined as
- ! upper_threshold - f_unclipped.
- f_diff = upper_threshold - f_unclipped
-
- ! Compute the values of functions A_fnc and B_fnc evaluated at f_diff(t)
- ! for the upper threshold.
- call compute_fncts_A_B( l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold, &
- f_diff, A_fnc, B_fnc )
-
- ! Compute the explicit (RHS) contribution from clipping for the upper
- ! threshold.
- rhs_upper &
- = + (1.0_core_rknd/real( dt_clip, kind = core_rknd ) &
- * ( A_fnc - B_fnc * f_diff + B_fnc * upper_threshold ) )
-
- else
-
- rhs_upper = 0.0_core_rknd
-
- endif
-
-
- ! Lower Threshold
- if ( l_lower_thresh ) then
-
- ! f_diff is the difference between the threshold value and f_unclipped.
- ! In regards to the lower threshold, it is defined as
- ! f_unclipped - lower_threshold.
- f_diff = f_unclipped - lower_threshold
-
- ! Compute the values of functions A_fnc and B_fnc evaluated at f_diff(t)
- ! for the lower threshold.
- call compute_fncts_A_B( l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold, &
- f_diff, A_fnc, B_fnc )
-
- ! Compute the explicit (RHS) contribution from clipping for the lower
- ! threshold.
- rhs_lower &
- = - (1.0_core_rknd/ real( dt_clip, kind = core_rknd )) &
- * ( A_fnc - B_fnc * f_diff - B_fnc * lower_threshold )
-
- else
-
- rhs_lower = 0.0_core_rknd
-
- endif
-
-
- ! Total explicit (RHS) contribution to clipping.
- rhs = rhs_upper + rhs_lower
-
-
- end function clip_semi_imp_rhs
-
- !=============================================================================
- subroutine compute_fncts_A_B( l_upper_thresh, upper_threshold, &
- l_lower_thresh, lower_threshold, &
- f_diff, A_fnc, B_fnc )
-
- ! Description:
- ! This subroutine computes the values of two functions used in semi-implicit
- ! clipping. Both of the functions are based on the values of f_diff(t) and
- ! the parameter sigma. One function is A_fnc, which is F_R(f_diff)
- ! evaluated at f_diff = f_diff(t). F_R(f_diff) is a three-piece function
- ! that is used to approximate function R(f_diff). The other function is
- ! B_fnc, the derivative with respect to f_diff of function A_fnc. In other
- ! words, B_fnc is ( d F_R / d f_diff ) evaluated at f_diff = f_diff(t).
- !
- ! The equation for A_fnc is:
- !
- ! | f_diff(t); where f_diff(t) <= -sigma
- ! |
- ! A_fnc = | f_diff(t) - (sigma/4) * [ 1 + (f_diff(t)/sigma) ]^2;
- ! | where -sigma < f_diff(t) < sigma
- ! |
- ! | 0; where f_diff(t) >= sigma;
- !
- ! while the equation for B_fnc is:
- !
- ! | 1; where f_diff(t) <= -sigma
- ! |
- ! B_fnc = | 1 - (1/2) * [ 1 + (f_diff(t)/sigma) ];
- ! | where -sigma < f_diff(t) < sigma
- ! |
- ! | 0; where f_diff(t) >= sigma;
- !
- ! where timestep index (t) stands for the index of the current timestep.
-
- ! References:
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: eps ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variable
- real( kind = core_rknd ), intent(in) :: &
- f_diff, & ! Difference between the threshold value and f_unclipped. [f units]
- upper_threshold, & ! Greatest allowable value of variable f. [f units]
- lower_threshold ! Smallest allowable value of variable f. [f units]
-
- logical, intent(in) :: &
- l_upper_thresh, & ! Flag for having an upper threshold value.
- l_lower_thresh ! Flag for having a lower threshold value.
-
- ! Output Variables
- real( kind = core_rknd ), intent(out) :: &
- A_fnc, & ! Function that approximates { f_diff * [ 1 - H(f_diff) ] }. [f units]
- B_fnc ! Derivative w/ respect to f_diff of function A_fnc. []
-
- ! Local Variables
- real( kind = core_rknd ) :: sigma_val, & ! Value of parameter sigma. [f units]
- thresh_avg_mag ! Average magnitude of threshold(s). [f units]
-
- thresh_avg_mag = 0.0_core_rknd ! Default Initialization
-
- ! Find the average magnitude of the threshold.
- ! In cases where only one threshold applies, the average magnitude of the
- ! threshold must be greater than 0.
- ! Note: The constant eps is there in case only one threshold applies, and
- ! it has a value of 0 (or very close to 0). However, eps is a very
- ! small number, and therefore it will not start curbing values until
- ! they get extremely close to the threshold. A larger constant value
- ! may work better.
- if ( l_upper_thresh .and. l_lower_thresh ) then
- ! Both thresholds apply.
- thresh_avg_mag = 0.5_core_rknd * ( abs(upper_threshold) &
- + abs(lower_threshold) )
- elseif ( l_upper_thresh ) then
- ! Only the upper threshold applies.
- thresh_avg_mag = max( abs(upper_threshold), eps )
- elseif ( l_lower_thresh ) then
- ! Only the lower threshold applies.
- thresh_avg_mag = max( abs(lower_threshold), eps )
- endif
-
- ! Compute the value of sigma based on the magnitude of the threshold(s) for
- ! variable f and the sigma coefficient. The value of sigma must always be
- ! positive.
- sigma_val = sigma_coef * thresh_avg_mag
-
- ! A_fnc is a three-piece function that approximates function
- ! R(f_diff(t)) = { f_diff(t) * [ 1 - H(f_diff(t)) ] }. This is needed
- ! because the R(f_diff(t)) is not differentiable at point f_diff(t) = 0, as
- ! the function has a corner at that point. Function A_fnc is differentiable
- ! at all points. It is evaluated for f_diff at timestep index (t).
- if ( f_diff <= -sigma_val ) then
- A_fnc = f_diff
- elseif ( f_diff >= sigma_val ) then
- A_fnc = 0.0_core_rknd
- else ! -sigma_val < f_diff < sigma_val
- A_fnc = f_diff - ( (sigma_val/4.0_core_rknd) &
- * ( 1.0_core_rknd + f_diff/sigma_val )**2 )
- endif
-
- ! B_fnc is the derivative with respect to f_diff of function A_fnc. It is
- ! evaluated for f_diff at timestep index (t).
- if ( f_diff <= -sigma_val ) then
- B_fnc = 1.0_core_rknd
- elseif ( f_diff >= sigma_val ) then
- B_fnc = 0.0_core_rknd
- else ! -sigma_val < f_diff < sigma_val
- B_fnc = 1.0_core_rknd - (1.0_core_rknd/2.0_core_rknd)*( 1.0_core_rknd + f_diff/sigma_val )
- endif
-
-
- end subroutine compute_fncts_A_B
-
-!===============================================================================
-
-end module crmx_clip_semi_implicit
diff --git a/src/physics/spcam/crm/CLUBB/crmx_clubb_core.F90 b/src/physics/spcam/crm/CLUBB/crmx_clubb_core.F90
deleted file mode 100644
index 3e768ff032..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_clubb_core.F90
+++ /dev/null
@@ -1,3105 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: clubb_core.F90 6146 2013-04-05 18:02:22Z raut@uwm.edu $
-!-----------------------------------------------------------------------
-module crmx_clubb_core
-
-! Description:
-! The module containing the `core' of the CLUBB parameterization.
-! A host model implementing CLUBB should only require this subroutine
-! and the functions and subroutines it calls.
-!
-! References:
-! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
-! Method and Model Description'' Golaz, et al. (2002)
-! JAS, Vol. 59, pp. 3540--3551.
-!
-! Copyright Notice:
-!
-! This code and the source code it references are (C) 2006-2013
-! Jean-Christophe Golaz, Vincent E. Larson, Brian M. Griffin,
-! David P. Schanen, Adam J. Smith, and Michael J. Falk.
-!
-! The distribution of this code and derived works thereof
-! should include this notice.
-!
-! Portions of this code derived from other sources (Hugh Morrison,
-! ACM TOMS, Numerical Recipes, et cetera) are the intellectual
-! property of their respective authors as noted and are also subject
-! to copyright.
-!-----------------------------------------------------------------------
-
- implicit none
-
- public :: &
- setup_clubb_core, &
- advance_clubb_core, &
- cleanup_clubb_core, &
- set_Lscale_max
-
- private ! Default Scope
-
- contains
-
- !-----------------------------------------------------------------------
-
- !#######################################################################
- !#######################################################################
- ! If you change the argument list of advance_clubb_core you also have to
- ! change the calls to this function in the host models CAM, WRF, SAM
- ! and GFDL.
- !#######################################################################
- !#######################################################################
- subroutine advance_clubb_core &
- ( l_implemented, dt, fcor, sfc_elevation, &
- thlm_forcing, rtm_forcing, um_forcing, vm_forcing, &
- sclrm_forcing, edsclrm_forcing, wprtp_forcing, &
- wpthlp_forcing, rtp2_forcing, thlp2_forcing, &
- rtpthlp_forcing, wm_zm, wm_zt, &
- wpthlp_sfc, wprtp_sfc, upwp_sfc, vpwp_sfc, &
- wpsclrp_sfc, wpedsclrp_sfc, &
- p_in_Pa, rho_zm, rho, exner, &
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, &
- invrs_rho_ds_zt, thv_ds_zm, thv_ds_zt, &
- rfrzm, radf, &
- um, vm, upwp, vpwp, up2, vp2, &
- thlm, rtm, wprtp, wpthlp, &
- wp2, wp3, rtp2, thlp2, rtpthlp, &
- sclrm, &
-#ifdef GFDL
- sclrm_trsport_only, & ! h1g, 2010-06-16
-#endif
- sclrp2, sclrprtp, sclrpthlp, &
- wpsclrp, edsclrm, err_code, &
-#ifdef GFDL
- RH_crit, do_liquid_only_in_clubb, & ! h1g, 2010-06-16
-#endif
- rcm, wprcp, cloud_frac, ice_supersat_frac, &
- rcm_in_layer, cloud_cover, &
-#if defined(CLUBB_CAM) || defined(GFDL) || defined(CLUBB_SAM)
- khzm, khzt, qclvar, &
-#endif
- pdf_params )
-
- ! Description:
- ! Subroutine to advance the model one timestep
-
- ! References:
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
- !-----------------------------------------------------------------------
-
- ! Modules to be included
-
- use crmx_constants_clubb, only: &
- w_tol, & ! Variable(s)
- em_min, &
- thl_tol, &
- rt_tol, &
- w_tol_sqd, &
- ep2, &
- Cp, &
- Lv, &
- ep1, &
- eps, &
- p0, &
- kappa, &
- fstderr, &
- zero_threshold, &
- three_halves
-
- use crmx_parameters_tunable, only: &
- gamma_coefc, & ! Variable(s)
- gamma_coefb, &
- gamma_coef, &
- taumax, &
- c_K, &
- mu, &
- Lscale_mu_coef, &
- Lscale_pert_coef
-
- use crmx_parameters_model, only: &
- sclr_dim, & ! Variable(s)
- edsclr_dim, &
- sclr_tol, &
- ts_nudge, &
- rtm_min, &
- rtm_nudge_max_altitude
-
- use crmx_model_flags, only: &
- l_tke_aniso, & ! Variable(s)
- l_gamma_Skw, &
- l_trapezoidal_rule_zt, &
- l_trapezoidal_rule_zm, &
- l_call_pdf_closure_twice, &
- l_host_applies_sfc_fluxes, &
- l_use_cloud_cover, &
- l_rtm_nudge
-
- use crmx_grid_class, only: &
- gr, & ! Variable(s)
- zm2zt, & ! Procedure(s)
- zt2zm, &
- ddzm
-
- use crmx_numerical_check, only: &
- parameterization_check, & ! Procedure(s)
- calculate_spurious_source
-
- use crmx_variables_diagnostic_module, only: &
- Skw_zt, & ! Variable(s)
- Skw_zm, &
- sigma_sqd_w_zt, &
- wp4, &
- thlpthvp, &
- rtpthvp, &
- rtprcp, &
- thlprcp, &
- rcp2, &
- rsat, &
- pdf_params_zm, &
- wprtp2, &
- wp2rtp, &
- wpthlp2, &
- wp2thlp, &
- wprtpthlp, &
- wpthvp, &
- wp2thvp, &
- wp2rcp
-
- use crmx_variables_diagnostic_module, only: &
- thvm, &
- em, &
- Lscale, &
- Lscale_up, &
- Lscale_down, &
- tau_zm, &
- tau_zt, &
- Kh_zm, &
- Kh_zt, &
- vg, &
- ug, &
- um_ref, &
- vm_ref
-
- use crmx_variables_diagnostic_module, only: &
- wp2_zt, &
- thlp2_zt, &
- wpthlp_zt, &
- wprtp_zt, &
- rtp2_zt, &
- rtpthlp_zt, &
- up2_zt, &
- vp2_zt, &
- upwp_zt, &
- vpwp_zt, &
- rtm_ref, &
- thlm_ref
-
- use crmx_variables_diagnostic_module, only: &
- wpedsclrp, &
- sclrpthvp, & ! sclr'th_v'
- sclrprcp, & ! sclr'rc'
- wp2sclrp, & ! w'^2 sclr'
- wpsclrp2, & ! w'sclr'^2
- wpsclrprtp, & ! w'sclr'rt'
- wpsclrpthlp, & ! w'sclr'thl'
- wp3_zm, & ! wp3 interpolated to momentum levels
- Skw_velocity, & ! Skewness velocity [m/s]
- a3_coef, & ! The a3 coefficient [-]
- a3_coef_zt ! The a3 coefficient interp. to the zt grid [-]
-
- use crmx_variables_diagnostic_module, only: &
- wp3_on_wp2, & ! Variable(s)
- wp3_on_wp2_zt
-
- use crmx_pdf_parameter_module, only: &
- pdf_parameter ! Type
-
-#ifdef GFDL
- use advance_sclrm_Nd_module, only: & ! h1g, 2010-06-16 begin mod
- advance_sclrm_Nd_diffusion_OG, &
- advance_sclrm_Nd_upwind, &
- advance_sclrm_Nd_semi_implicit ! h1g, 2010-06-16 end mod
-#endif
-
- use crmx_advance_xm_wpxp_module, only: &
- ! Variable(s)
- advance_xm_wpxp ! Compute mean/flux terms
-
- use crmx_advance_xp2_xpyp_module, only: &
- ! Variable(s)
- advance_xp2_xpyp ! Computes variance terms
-
- use crmx_surface_varnce_module, only: &
- surface_varnce ! Procedure
-
- use crmx_pdf_closure_module, only: &
- ! Procedure
- pdf_closure ! Prob. density function
-
- use crmx_mixing_length, only: &
- compute_length ! Procedure
-
- use crmx_advance_windm_edsclrm_module, only: &
- advance_windm_edsclrm ! Procedure(s)
-
- use crmx_saturation, only: &
- ! Procedure
- sat_mixrat_liq ! Saturation mixing ratio
-
- use crmx_advance_wp2_wp3_module, only: &
- advance_wp2_wp3 ! Procedure
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- core_rknd
-
- use crmx_error_code, only : &
- clubb_no_error ! Constant(s)
-
- use crmx_error_code, only : &
- clubb_at_least_debug_level, & ! Procedure(s)
- reportError, &
- fatal_error
-
- use crmx_Skw_module, only: &
- Skw_func ! Procedure
-
- use crmx_clip_explicit, only: &
- clip_covars_denom ! Procedure(s)
-
- use crmx_T_in_K_module, only: &
- ! Read values from namelist
- thlm2T_in_K ! Procedure
-
- use crmx_stats_subs, only: &
- stats_accumulate ! Procedure
-
- use crmx_stats_type, only: &
- stat_update_var_pt, & ! Procedure(s)
- stat_update_var, &
- stat_begin_update, &
- stat_begin_update_pt, &
- stat_end_update, &
- stat_end_update_pt
-
- use crmx_stats_variables, only: &
- irtp2_bt, & ! Variable(s)
- ithlp2_bt, &
- irtpthlp_bt, &
- iwp2_bt, &
- iwp3_bt, &
- ivp2_bt, &
- iup2_bt, &
- iwprtp_bt, &
- iwpthlp_bt, &
- irtm_bt, &
- ithlm_bt, &
- ivm_bt, &
- ium_bt, &
- ircp2, &
- iwp4, &
- irsat, &
- irvm, &
- irel_humidity, &
- iwpthlp_zt
-
- use crmx_stats_variables, only: &
- iwprtp_zt, &
- iup2_zt, &
- ivp2_zt, &
- iupwp_zt, &
- ivpwp_zt, &
- ithlp2_sf, &
- irtp2_sf, &
- irtpthlp_sf, &
- iup2_sf, &
- ivp2_sf, &
- iwp2_sf, &
- l_stats_samp, &
- l_stats, &
- zt, &
- zm, &
- sfc, &
- irtm_spur_src, &
- ithlm_spur_src
-
- use crmx_stats_variables, only: &
- irfrzm ! Variable(s)
-
- use crmx_stats_variables, only: &
- iSkw_velocity, & ! Variable(s)
- igamma_Skw_fnc, &
- iLscale_pert_1, &
- iLscale_pert_2
-
- use crmx_fill_holes, only: &
- vertical_integral ! Procedure(s)
-
- use crmx_sigma_sqd_w_module, only: &
- compute_sigma_sqd_w ! Procedure(s)
-
- implicit none
-
- !!! External
- intrinsic :: sqrt, min, max, exp, mod, real
-
- ! Constant Parameters
- logical, parameter :: &
- l_avg_Lscale = .true., & ! Lscale is calculated in subroutine compute_length; if l_avg_Lscale
- ! is true, compute_length is called two additional times with
- ! perturbed values of rtm and thlm. An average value of Lscale
- ! from the three calls to compute_length is then calculated.
- ! This reduces temporal noise in RICO, BOMEX, LBA, and other cases.
- l_Lscale_plume_centered = .false. ! Alternate that uses the PDF to
- ! compute the perturbed values
-
- logical, parameter :: &
- l_use_ice_latent = .false. !Includes the effects of ice latent heating in turbulence terms
-! l_use_ice_latent = .true. !Includes the effects of ice latent heating in turbulence terms +++mhwang test
-
- logical, parameter :: &
- l_iter_xp2_xpyp = .true. ! Set to true when rtp2/thlp2/rtpthlp, et cetera are prognostic
-
- !!! Input Variables
- logical, intent(in) :: &
- l_implemented ! Is this part of a larger host model (T/F) ?
-
- real(kind=time_precision), intent(in) :: &
- dt ! Current timestep duration [s]
-
- real( kind = core_rknd ), intent(in) :: &
- fcor, & ! Coriolis forcing [s^-1]
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- thlm_forcing, & ! theta_l forcing (thermodynamic levels) [K/s]
- rtm_forcing, & ! r_t forcing (thermodynamic levels) [(kg/kg)/s]
- um_forcing, & ! u wind forcing (thermodynamic levels) [m/s/s]
- vm_forcing, & ! v wind forcing (thermodynamic levels) [m/s/s]
- wprtp_forcing, & ! forcing (momentum levels) [m*K/s^2]
- wpthlp_forcing, & ! forcing (momentum levels) [m*(kg/kg)/s^2]
- rtp2_forcing, & ! forcing (momentum levels) [(kg/kg)^2/s]
- thlp2_forcing, & ! forcing (momentum levels) [K^2/s]
- rtpthlp_forcing, & ! forcing (momentum levels) [K*(kg/kg)/s]
- wm_zm, & ! w mean wind component on momentum levels [m/s]
- wm_zt, & ! w mean wind component on thermo. levels [m/s]
- p_in_Pa, & ! Air pressure (thermodynamic levels) [Pa]
- rho_zm, & ! Air density on momentum levels [kg/m^3]
- rho, & ! Air density on thermodynamic levels [kg/m^3]
- exner, & ! Exner function (thermodynamic levels) [-]
- rho_ds_zm, & ! Dry, static density on momentum levels [kg/m^3]
- rho_ds_zt, & ! Dry, static density on thermo. levels [kg/m^3]
- invrs_rho_ds_zm, & ! Inv. dry, static density @ momentum levs. [m^3/kg]
- invrs_rho_ds_zt, & ! Inv. dry, static density @ thermo. levs. [m^3/kg]
- thv_ds_zm, & ! Dry, base-state theta_v on momentum levs. [K]
- thv_ds_zt, & ! Dry, base-state theta_v on thermo. levs. [K]
- rfrzm ! Total ice-phase water mixing ratio [kg/kg]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- radf ! Buoyancy production at the CL top due to LW radiative cooling [m^2/s^3]
-
- real( kind = core_rknd ), intent(in) :: &
- wpthlp_sfc, & ! w' theta_l' at surface [(m K)/s]
- wprtp_sfc, & ! w' r_t' at surface [(kg m)/( kg s)]
- upwp_sfc, & ! u'w' at surface [m^2/s^2]
- vpwp_sfc ! v'w' at surface [m^2/s^2]
-
- ! Passive scalar variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz,sclr_dim) :: &
- sclrm_forcing ! Passive scalar forcing [{units vary}/s]
-
- real( kind = core_rknd ), intent(in), dimension(sclr_dim) :: &
- wpsclrp_sfc ! Scalar flux at surface [{units vary} m/s]
-
- ! Eddy passive scalar variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz,edsclr_dim) :: &
- edsclrm_forcing ! Eddy passive scalar forcing [{units vary}/s]
-
- real( kind = core_rknd ), intent(in), dimension(edsclr_dim) :: &
- wpedsclrp_sfc ! Eddy-Scalar flux at surface [{units vary} m/s]
-
- !!! Input/Output Variables
- ! These are prognostic or are planned to be in the future
- real( kind = core_rknd ), intent(inout), dimension(gr%nz) :: &
- um, & ! u mean wind component (thermodynamic levels) [m/s]
- upwp, & ! u'w' (momentum levels) [m^2/s^2]
- vm, & ! v mean wind component (thermodynamic levels) [m/s]
- vpwp, & ! v'w' (momentum levels) [m^2/s^2]
- up2, & ! u'^2 (momentum levels) [m^2/s^2]
- vp2, & ! v'^2 (momentum levels) [m^2/s^2]
- rtm, & ! total water mixing ratio, r_t (thermo. levels) [kg/kg]
- wprtp, & ! w' r_t' (momentum levels) [(kg/kg) m/s]
- thlm, & ! liq. water pot. temp., th_l (thermo. levels) [K]
- wpthlp, & ! w' th_l' (momentum levels) [(m/s) K]
- rtp2, & ! r_t'^2 (momentum levels) [(kg/kg)^2]
- thlp2, & ! th_l'^2 (momentum levels) [K^2]
- rtpthlp, & ! r_t' th_l' (momentum levels) [(kg/kg) K]
- wp2, & ! w'^2 (momentum levels) [m^2/s^2]
- wp3 ! w'^3 (thermodynamic levels) [m^3/s^3]
-
- ! Passive scalar variables
- real( kind = core_rknd ), intent(inout), dimension(gr%nz,sclr_dim) :: &
- sclrm, & ! Passive scalar mean (thermo. levels) [units vary]
- wpsclrp, & ! w'sclr' (momentum levels) [{units vary} m/s]
- sclrp2, & ! sclr'^2 (momentum levels) [{units vary}^2]
- sclrprtp, & ! sclr'rt' (momentum levels) [{units vary} (kg/kg)]
- sclrpthlp ! sclr'thl' (momentum levels) [{units vary} K]
-
-#ifdef GFDL
- real( kind = core_rknd ), intent(inout), dimension(gr%nz,sclr_dim) :: & ! h1g, 2010-06-16
- sclrm_trsport_only ! Passive scalar concentration due to pure transport [{units vary}/s]
-#endif
-
- ! Eddy passive scalar variable
- real( kind = core_rknd ), intent(inout), dimension(gr%nz,edsclr_dim) :: &
- edsclrm ! Eddy passive scalar mean (thermo. levels) [units vary]
-
- ! Variables that need to be output for use in other parts of the CLUBB
- ! code, such as microphysics (rcm, pdf_params), forcings (rcm), and/or
- ! BUGSrad (cloud_cover).
- real( kind = core_rknd ), intent(out), dimension(gr%nz) :: &
- rcm, & ! cloud water mixing ratio, r_c (thermo. levels) [kg/kg]
- rcm_in_layer, & ! rcm in cloud layer [kg/kg]
- cloud_cover ! cloud cover [-]
-
- type(pdf_parameter), dimension(gr%nz), intent(out) :: &
- pdf_params ! PDF parameters [units vary]
-
- ! Variables that need to be output for use in host models
- real( kind = core_rknd ), intent(out), dimension(gr%nz) :: &
- wprcp, & ! w'r_c' (momentum levels) [(kg/kg) m/s]
- cloud_frac, & ! cloud fraction (thermodynamic levels) [-]
- ice_supersat_frac ! ice cloud fraction (thermodynamic levels) [-]
-
- ! Eric Raut declared this variable solely for output to disk
- real( kind = core_rknd ), dimension(gr%nz) :: &
- rc_coef ! Coefficient of X' R_l' in Eq. (34) [-]
-
-#if defined(CLUBB_CAM) || defined(GFDL) || defined(CLUBB_SAM)
- real( kind = core_rknd ), intent(out), dimension(gr%nz) :: &
- khzt, & ! eddy diffusivity on thermo levels
- khzm, & ! eddy diffusivity on momentum levels
- qclvar ! cloud water variance
-#endif
-
- !!! Output Variable
- ! Diagnostic, for if some calculation goes amiss.
- integer, intent(inout) :: err_code
-
-#ifdef GFDL
- ! hlg, 2010-06-16
- real( kind = core_rknd ), intent(inOUT), dimension(gr%nz, min(1,sclr_dim) , 2) :: &
- RH_crit ! critical relative humidity for droplet and ice nucleation
-! ---> h1g, 2012-06-14
- logical, intent(in) :: do_liquid_only_in_clubb
-! <--- h1g, 2012-06-14
-#endif
-
- !!! Local Variables
- integer :: i, k, &
- err_code_pdf_closure, err_code_surface
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- sigma_sqd_w, & ! PDF width parameter (momentum levels) [-]
- sqrt_em_zt, & ! sqrt( em ) on zt levels; where em is TKE [m/s]
- gamma_Skw_fnc, & ! Gamma as a function of skewness [???]
- Lscale_pert_1, Lscale_pert_2, & ! For avg. calculation of Lscale [m]
- thlm_pert_1, thlm_pert_2, & ! For avg. calculation of Lscale [K]
- rtm_pert_1, rtm_pert_2, & ! For avg. calculation of Lscale [kg/kg]
- thlm_pert_pos_rt, thlm_pert_neg_rt, & ! For avg. calculation of Lscale [K]
- rtm_pert_pos_rt, rtm_pert_neg_rt ! For avg. calculation of Lscale [kg/kg]
- !Lscale_weight Uncomment this if you need to use this vairable at some point.
-
- ! For pdf_closure
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim) :: &
- wpsclrp_zt, & ! w' sclr' on thermo. levels
- sclrp2_zt, & ! sclr'^2 on thermo. levels
- sclrprtp_zt, & ! sclr' r_t' on thermo. levels
- sclrpthlp_zt ! sclr' th_l' on thermo. levels
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- p_in_Pa_zm, & ! Pressure interpolated to momentum levels [Pa]
- exner_zm, & ! Exner interpolated to momentum levels [-]
- w1_zm, & ! Mean w (1st PDF component) [m/s]
- w2_zm, & ! Mean w (2nd PDF component) [m/s]
- varnce_w1_zm, & ! Variance of w (1st PDF component) [m^2/s^2]
- varnce_w2_zm, & ! Variance of w (2nd PDF component) [m^2/s^2]
- mixt_frac_zm ! Weight of 1st PDF component (Sk_w dependent) [-]
-
- integer :: &
- wprtp_cl_num, & ! Instance of w'r_t' clipping (1st or 3rd).
- wpthlp_cl_num, & ! Instance of w'th_l' clipping (1st or 3rd).
- wpsclrp_cl_num, & ! Instance of w'sclr' clipping (1st or 3rd).
- upwp_cl_num, & ! Instance of u'w' clipping (1st or 2nd).
- vpwp_cl_num ! Instance of v'w' clipping (1st or 2nd).
-
- ! These local variables are declared because they originally belong on the momentum
- ! grid levels, but pdf_closure outputs them on the thermodynamic grid levels.
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wp4_zt, & ! w'^4 (on thermo. grid) [m^4/s^4]
- wpthvp_zt, & ! Buoyancy flux (on thermo. grid) [(K m)/s]
- rtpthvp_zt, & ! r_t' th_v' (on thermo. grid) [(kg K)/kg]
- thlpthvp_zt, & ! th_l' th_v' (on thermo. grid) [K^2]
- wprcp_zt, & ! w' r_c' (on thermo. grid) [(m kg)/(s kg)]
- rtprcp_zt, & ! r_t' r_c' (on thermo. grid) [(kg^2)/(kg^2)]
- thlprcp_zt, & ! th_l' r_c' (on thermo. grid) [(K kg)/kg]
- rcp2_zt, & ! r_c'^2 (on thermo. grid) [(kg^2)/(kg^2)]
- rc_coef_zt ! X'R_l' coef. (on thermo. grid) [-]
-
- real( kind = core_rknd ), dimension(gr%nz, sclr_dim) :: &
- sclrpthvp_zt, & ! sclr'th_v' (on thermo. grid)
- sclrprcp_zt ! sclr'rc' (on thermo. grid)
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- wprtp2_zm, & ! w'rt'^2 on momentum grid [m kg^2/kg^2]
- wp2rtp_zm, & ! w'^2 rt' on momentum grid [m^2 kg/kg]
- wpthlp2_zm, & ! w'thl'^2 on momentum grid [m K^2/s]
- wp2thlp_zm, & ! w'^2 thl' on momentum grid [m^2 K/s^2]
- wprtpthlp_zm, & ! w'rt'thl' on momentum grid [m kg K/kg s]
- cloud_frac_zm, & ! Cloud Fraction on momentum grid [-]
- ice_supersat_frac_zm, & ! Ice Cloud Fraction on momentum grid [-]
- rtm_zm, & ! Total water mixing ratio [kg/kg]
- thlm_zm, & ! Liquid potential temperature [kg/kg]
- rcm_zm, & ! Liquid water mixing ratio on momentum grid [kg/kg]
- wp2thvp_zm, & ! w'^2 th_v' on momentum grid [m^2 K/s^2]
- wp2rcp_zm, & ! w'^2 rc' on momentum grid [m^2 kg/kg s^2]
- sign_rtpthlp ! sign of the covariance rtpthlp [-]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim) :: &
- wpsclrprtp_zm, & ! w'sclr'rt' on momentum grid
- wpsclrp2_zm, & ! w'sclr'^2 on momentum grid
- wpsclrpthlp_zm, & ! w'sclr'thl' on momentum grid
- wp2sclrp_zm, & ! w'^2 sclr' on momentum grid
- sclrm_zm ! Passive scalar mean on momentum grid
-
- real( kind = core_rknd ) :: &
- rtm_integral_before, &
- rtm_integral_after, &
- rtm_integral_forcing, &
- rtm_flux_top, &
- rtm_flux_sfc, &
- rtm_spur_src, &
- thlm_integral_before, &
- thlm_integral_after, &
- thlm_integral_forcing, &
- thlm_flux_top, &
- thlm_flux_sfc, &
- thlm_spur_src, &
- mu_pert_1, mu_pert_2, & ! For l_avg_Lscale
- mu_pert_pos_rt, mu_pert_neg_rt ! For l_Lscale_plume_centered
-
- !The following variables are defined for use when l_use_ice_latent = .true.
- type(pdf_parameter), dimension(gr%nz) :: &
- pdf_params_frz, &
- pdf_params_zm_frz
-
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- rtm_frz, &
- thlm_frz, &
- wp4_zt_frz, &
- wprtp2_frz, &
- wp2rtp_frz, &
- wpthlp2_frz, &
- wp2thlp_frz, &
- wprtpthlp_frz, &
- cloud_frac_frz, &
- ice_supersat_frac_frz, &
- rcm_frz, &
- wpthvp_frz, &
- wpthvp_zt_frz, &
- wp2thvp_frz, &
- wp2thvp_zm_frz, &
- rtpthvp_frz, &
- rtpthvp_zt_frz, &
- thlpthvp_frz, &
- thlpthvp_zt_frz, &
- wprcp_zt_frz, &
- wp2rcp_frz
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- rtprcp_zt_frz, &
- thlprcp_zt_frz, &
- rcp2_zt_frz, &
- rc_coef_zt_frz, &
- wp4_frz, &
- wprtp2_zm_frz, &
- wp2rtp_zm_frz, &
- wpthlp2_zm_frz, &
- wp2thlp_zm_frz, &
- wprtpthlp_zm_frz, &
- cloud_frac_zm_frz, &
- ice_supersat_frac_zm_frz, &
- rcm_zm_frz, &
- wprcp_frz, &
- wp2rcp_zm_frz, &
- rtprcp_frz, &
- thlprcp_frz, &
- rcp2_frz, &
- rtm_zm_frz, &
- thlm_zm_frz, &
- rc_coef_frz
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim) :: &
- wpsclrprtp_frz, &
- wpsclrp2_frz, &
- sclrpthvp_zt_frz, &
- wpsclrpthlp_frz, &
- sclrprcp_zt_frz, &
- wp2sclrp_frz, &
- wpsclrprtp_zm_frz, &
- wpsclrp2_zm_frz, &
- sclrpthvp_frz, &
- wpsclrpthlp_zm_frz, &
- sclrprcp_frz, &
- wp2sclrp_zm_frz
-
-
- !----- Begin Code -----
-
- if ( l_stats .and. l_stats_samp ) then
- ! Spurious source will only be calculated if rtm_ma and thlm_ma are zero.
- ! Therefore, wm must be zero or l_implemented must be true.
- if ( l_implemented .or. ( all( wm_zt == 0._core_rknd ) .and. &
- all( wm_zm == 0._core_rknd ) ) ) then
- ! Get the vertical integral of rtm and thlm before this function begins
- ! so that spurious source can be calculated
- rtm_integral_before &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- rtm(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
-
- thlm_integral_before &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- thlm(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
- end if
- end if
-
- !----------------------------------------------------------------
- ! Test input variables
- !----------------------------------------------------------------
- if ( clubb_at_least_debug_level( 2 ) ) then
- call parameterization_check &
- ( thlm_forcing, rtm_forcing, um_forcing, vm_forcing, & ! intent(in)
- wm_zm, wm_zt, p_in_Pa, rho_zm, rho, exner, & ! intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! intent(in)
- invrs_rho_ds_zt, thv_ds_zm, thv_ds_zt, & ! intent(in)
- wpthlp_sfc, wprtp_sfc, upwp_sfc, vpwp_sfc, & ! intent(in)
- um, upwp, vm, vpwp, up2, vp2, & ! intent(in)
- rtm, wprtp, thlm, wpthlp, & ! intent(in)
- wp2, wp3, rtp2, thlp2, rtpthlp, & ! intent(in)
- "beginning of ", & ! intent(in)
- wpsclrp_sfc, wpedsclrp_sfc, & ! intent(in)
- sclrm, wpsclrp, sclrp2, sclrprtp, sclrpthlp, & ! intent(in)
- sclrm_forcing, edsclrm, edsclrm_forcing, & ! intent(in)
- err_code ) ! Intent(inout)
- end if
- !-----------------------------------------------------------------------
-
- if ( l_stats_samp ) then
- call stat_update_var( irfrzm, rfrzm, & ! In
- zt ) ! Out
- end if
-
- ! Set up budget stats variables.
- if ( l_stats_samp ) then
-
- call stat_begin_update( iwp2_bt, wp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( ivp2_bt, vp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( iup2_bt, up2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( iwprtp_bt, wprtp / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( iwpthlp_bt, wpthlp / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( irtp2_bt, rtp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( ithlp2_bt, thlp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_begin_update( irtpthlp_bt, rtpthlp / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
-
- call stat_begin_update( irtm_bt, rtm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_begin_update( ithlm_bt, thlm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_begin_update( ium_bt, um / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_begin_update( ivm_bt, vm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_begin_update( iwp3_bt, wp3 / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
-
- end if
-
- ! SET SURFACE VALUES OF FLUXES (BROUGHT IN)
- ! We only do this for host models that do not apply the flux
- ! elsewhere in the code (e.g. WRF). In other cases the _sfc variables will
- ! only be used to compute the variance at the surface. -dschanen 8 Sept 2009
- if ( .not. l_host_applies_sfc_fluxes ) then
-
- wpthlp(1) = wpthlp_sfc
- wprtp(1) = wprtp_sfc
- upwp(1) = upwp_sfc
- vpwp(1) = vpwp_sfc
-
- ! Set fluxes for passive scalars (if enabled)
- if ( sclr_dim > 0 ) then
- wpsclrp(1,1:sclr_dim) = wpsclrp_sfc(1:sclr_dim)
- end if
-
- if ( edsclr_dim > 0 ) then
- wpedsclrp(1,1:edsclr_dim) = wpedsclrp_sfc(1:edsclr_dim)
- end if
-
- else
-
- wpthlp(1) = 0.0_core_rknd
- wprtp(1) = 0.0_core_rknd
- upwp(1) = 0.0_core_rknd
- vpwp(1) = 0.0_core_rknd
-
- ! Set fluxes for passive scalars (if enabled)
- if ( sclr_dim > 0 ) then
- wpsclrp(1,1:sclr_dim) = 0.0_core_rknd
- end if
-
- if ( edsclr_dim > 0 ) then
- wpedsclrp(1,1:edsclr_dim) = 0.0_core_rknd
- end if
-
- end if ! ~l_host_applies_sfc_fluxes
-
- !---------------------------------------------------------------------------
- ! Interpolate wp3 to momentum levels, and wp2 to thermodynamic levels
- ! and then compute Skw for m & t grid
- !---------------------------------------------------------------------------
-
- wp2_zt = max( zm2zt( wp2 ), w_tol_sqd ) ! Positive definite quantity
- wp3_zm = zt2zm( wp3 )
-
- Skw_zt(1:gr%nz) = Skw_func( wp2_zt(1:gr%nz), wp3(1:gr%nz) )
- Skw_zm(1:gr%nz) = Skw_func( wp2(1:gr%nz), wp3_zm(1:gr%nz) )
-
- ! The right hand side of this conjunction is only for reducing cpu time,
- ! since the more complicated formula is mathematically equivalent
- if ( l_gamma_Skw .and. ( gamma_coef /= gamma_coefb ) ) then
- !----------------------------------------------------------------
- ! Compute gamma as a function of Skw - 14 April 06 dschanen
- !----------------------------------------------------------------
-
- gamma_Skw_fnc = gamma_coefb + (gamma_coef-gamma_coefb) &
- *exp( -(1.0_core_rknd/2.0_core_rknd) * (Skw_zm/gamma_coefc)**2 )
-
- else
-
- gamma_Skw_fnc = gamma_coef
-
- end if
-
- ! Compute sigma_sqd_w (dimensionless PDF width parameter)
- sigma_sqd_w = compute_sigma_sqd_w( gamma_Skw_fnc, wp2, thlp2, rtp2, wpthlp, wprtp )
-
- if ( l_stats_samp ) then
- call stat_update_var( igamma_Skw_fnc, gamma_Skw_fnc, zm )
- endif
-
- ! Smooth in the vertical
- sigma_sqd_w = zt2zm( zm2zt( sigma_sqd_w ) )
-
- ! Interpolate the the zt grid
- sigma_sqd_w_zt = max( zm2zt( sigma_sqd_w ), zero_threshold ) ! Pos. def. quantity
-
- ! Compute the a3 coefficient (formula 25 in `Equations for CLUBB')
-! a3_coef = 3.0_core_rknd * sigma_sqd_w*sigma_sqd_w &
-! + 6.0_core_rknd*(1.0_core_rknd-sigma_sqd_w)*sigma_sqd_w &
-! + (1.0_core_rknd-sigma_sqd_w)*(1.0_core_rknd-sigma_sqd_w) &
-! - 3.0_core_rknd
-
- ! This is a simplified version of the formula above.
- a3_coef = -2._core_rknd * ( 1._core_rknd - sigma_sqd_w )**2
-
- ! We found we obtain fewer spikes in wp3 when we clip a3 to be no greater
- ! than -1.4 -dschanen 4 Jan 2011
- a3_coef = max( a3_coef, -1.4_core_rknd ) ! Known magic number
-
- a3_coef_zt = zm2zt( a3_coef )
-
- !---------------------------------------------------------------------------
- ! Interpolate thlp2, rtp2, and rtpthlp to thermodynamic levels,
- !---------------------------------------------------------------------------
-
- ! Iterpolate variances to the zt grid (statistics and closure)
- thlp2_zt = max( zm2zt( thlp2 ), thl_tol**2 ) ! Positive def. quantity
- rtp2_zt = max( zm2zt( rtp2 ), rt_tol**2 ) ! Positive def. quantity
- rtpthlp_zt = zm2zt( rtpthlp )
-
- ! Compute skewness velocity for stats output purposes
- if ( iSkw_velocity > 0 ) then
- Skw_velocity = ( 1.0_core_rknd / ( 1.0_core_rknd - sigma_sqd_w(1:gr%nz) ) ) &
- * ( wp3_zm(1:gr%nz) / max( wp2(1:gr%nz), w_tol_sqd ) )
- end if
-
- ! Compute wp3 / wp2 on zt levels. Always use the interpolated value in the
- ! denominator since it's less likely to create spikes
- wp3_on_wp2_zt = ( wp3(1:gr%nz) / max( wp2_zt(1:gr%nz), w_tol_sqd ) )
-
- ! Clip wp3_on_wp2_zt if it's too large
- do k=1, gr%nz
- if( wp3_on_wp2_zt(k) < 0._core_rknd ) then
- wp3_on_wp2_zt = max( -1000._core_rknd, wp3_on_wp2_zt )
- else
- wp3_on_wp2_zt = min( 1000._core_rknd, wp3_on_wp2_zt )
- end if
- end do
-
- ! Compute wp3_on_wp2 by interpolating wp3_on_wp2_zt
- wp3_on_wp2 = zt2zm( wp3_on_wp2_zt )
-
- ! Smooth again as above
- wp3_on_wp2_zt = zm2zt( wp3_on_wp2 )
-
- !----------------------------------------------------------------
- ! Call closure scheme
- !----------------------------------------------------------------
-
- ! Put passive scalar input on the t grid for the PDF
- do i = 1, sclr_dim, 1
- wpsclrp_zt(:,i) = zm2zt( wpsclrp(:,i) )
- sclrp2_zt(:,i) = max( zm2zt( sclrp2(:,i) ), zero_threshold ) ! Pos. def. quantity
- sclrprtp_zt(:,i) = zm2zt( sclrprtp(:,i) )
- sclrpthlp_zt(:,i) = zm2zt( sclrpthlp(:,i) )
- end do ! i = 1, sclr_dim, 1
-
-
- do k = 1, gr%nz, 1
-
- call pdf_closure &
- ( p_in_Pa(k), exner(k), thv_ds_zt(k), wm_zt(k), & ! intent(in)
- wp2_zt(k), wp3(k), sigma_sqd_w_zt(k), & ! intent(in)
- Skw_zt(k), rtm(k), rtp2_zt(k), & ! intent(in)
- zm2zt( wprtp, k ), thlm(k), thlp2_zt(k), & ! intent(in)
- zm2zt( wpthlp, k ), rtpthlp_zt(k), sclrm(k,:), & ! intent(in)
- wpsclrp_zt(k,:), sclrp2_zt(k,:), sclrprtp_zt(k,:), & ! intent(in)
- sclrpthlp_zt(k,:), k, & ! intent(in)
-#ifdef GFDL
- RH_crit(k, : , :), do_liquid_only_in_clubb, & ! intent(in) h1g, 2010-06-16
-#endif
- wp4_zt(k), wprtp2(k), wp2rtp(k), & ! intent(out)
- wpthlp2(k), wp2thlp(k), wprtpthlp(k), & ! intent(out)
- cloud_frac(k), ice_supersat_frac(k), & ! intent(out)
- rcm(k), wpthvp_zt(k), wp2thvp(k), rtpthvp_zt(k), & ! intent(out)
- thlpthvp_zt(k), wprcp_zt(k), wp2rcp(k), rtprcp_zt(k),& ! intent(out)
- thlprcp_zt(k), rcp2_zt(k), pdf_params(k), & ! intent(out)
- err_code_pdf_closure, & ! intent(out)
- wpsclrprtp(k,:), wpsclrp2(k,:), sclrpthvp_zt(k,:), & ! intent(out)
- wpsclrpthlp(k,:), sclrprcp_zt(k,:), wp2sclrp(k,:), & ! intent(out)
- rc_coef_zt(k) ) ! intent(out)
-
- ! Subroutine may produce NaN values, and if so, exit
- ! gracefully.
- ! Joshua Fasching March 2008
-
- if ( fatal_error( err_code_pdf_closure ) ) then
-
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "At grid level = ",k
- end if
-
- err_code = err_code_pdf_closure
- end if
-
- end do ! k = 1, gr%nz, 1
-
- if( l_rtm_nudge ) then
- ! Nudge rtm to prevent excessive drying
- where( rtm < rtm_min .and. gr%zt < rtm_nudge_max_altitude )
- rtm = rtm + (rtm_ref - rtm) * ( real( dt, kind = core_rknd ) / ts_nudge )
- end where
- end if
-
-
- if ( l_call_pdf_closure_twice ) then
- ! Call pdf_closure a second time on momentum levels, to
- ! output (rather than interpolate) the variables which
- ! belong on the momentum levels.
-
- ! Interpolate sclrm to the momentum level for use in
- ! the second call to pdf_closure
- do i = 1, sclr_dim
- sclrm_zm(:,i) = zt2zm( sclrm(:,i) )
- ! Clip if extrap. causes sclrm_zm to be less than sclr_tol
- sclrm_zm(gr%nz,i) = max( sclrm_zm(gr%nz,i), sclr_tol(i) )
- end do ! i = 1, sclr_dim
-
- ! Interpolate pressure, p_in_Pa, to momentum levels.
- ! The pressure at thermodynamic level k = 1 has been set to be the surface
- ! (or model lower boundary) pressure. Since the surface (or model lower
- ! boundary) is located at momentum level k = 1, the pressure there is
- ! p_sfc, which is p_in_Pa(1). Thus, p_in_Pa_zm(1) = p_in_Pa(1).
- p_in_Pa_zm(:) = zt2zm( p_in_Pa )
- p_in_Pa_zm(1) = p_in_Pa(1)
-
- ! Clip pressure if the extrapolation leads to a negative value of pressure
- p_in_Pa_zm(gr%nz) = max( p_in_Pa_zm(gr%nz), 0.5_core_rknd*p_in_Pa(gr%nz) )
- ! Set exner at momentum levels, exner_zm, based on p_in_Pa_zm.
- exner_zm(:) = (p_in_Pa_zm(:)/p0)**kappa
-
- rtm_zm = zt2zm( rtm )
- ! Clip if extrapolation at the top level causes rtm_zm to be < rt_tol
- rtm_zm(gr%nz) = max( rtm_zm(gr%nz), rt_tol )
- thlm_zm = zt2zm( thlm )
- ! Clip if extrapolation at the top level causes thlm_zm to be < thl_tol
- thlm_zm(gr%nz) = max( thlm_zm(gr%nz), thl_tol )
-
- ! Call pdf_closure to output the variables which belong on the momentum grid.
- do k = 1, gr%nz, 1
-
- call pdf_closure &
- ( p_in_Pa_zm(k), exner_zm(k), thv_ds_zm(k), wm_zm(k), & ! intent(in)
- wp2(k), wp3_zm(k), sigma_sqd_w(k), & ! intent(in)
- Skw_zm(k), rtm_zm(k), rtp2(k), & ! intent(in)
- wprtp(k), thlm_zm(k), thlp2(k), & ! intent(in)
- wpthlp(k), rtpthlp(k), sclrm_zm(k,:), & ! intent(in)
- wpsclrp(k,:), sclrp2(k,:), sclrprtp(k,:), & ! intent(in)
- sclrpthlp(k,:), k, & ! intent(in)
-#ifdef GFDL
- RH_crit(k, : , :), do_liquid_only_in_clubb, & ! intent(in) h1g, 2010-06-16
-#endif
- wp4(k), wprtp2_zm(k), wp2rtp_zm(k), & ! intent(out)
- wpthlp2_zm(k), wp2thlp_zm(k), wprtpthlp_zm(k), & ! intent(out)
- cloud_frac_zm(k), ice_supersat_frac_zm(k), & ! intent(out)
- rcm_zm(k), wpthvp(k), wp2thvp_zm(k), rtpthvp(k), & ! intent(out)
- thlpthvp(k), wprcp(k), wp2rcp_zm(k), rtprcp(k), & ! intent(out)
- thlprcp(k), rcp2(k), pdf_params_zm(k), & ! intent(out)
- err_code_pdf_closure, & ! intent(out)
- wpsclrprtp_zm(k,:), wpsclrp2_zm(k,:), sclrpthvp(k,:), & ! intent(out)
- wpsclrpthlp_zm(k,:), sclrprcp(k,:), wp2sclrp_zm(k,:), & ! intent(out)
- rc_coef(k) ) ! intent(out)
-
- ! Subroutine may produce NaN values, and if so, exit
- ! gracefully.
- ! Joshua Fasching March 2008
-
-
- if ( fatal_error( err_code_pdf_closure ) ) then
-
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "At grid level = ",k
- end if
-
- err_code = err_code_pdf_closure
- end if
-
- end do ! k = 1, gr%nz, 1
-
- else ! l_call_pdf_closure_twice is false
-
- ! Interpolate momentum variables output from the first call to
- ! pdf_closure back to momentum grid.
- ! Since top momentum level is higher than top thermo level,
- ! Set variables at top momentum level to 0.
-
- ! Only do this for wp4 and rcp2 if we're saving stats, since they are not
- ! used elsewhere in the parameterization
- if ( iwp4 > 0 ) then
- wp4 = max( zt2zm( wp4_zt ), zero_threshold ) ! Pos. def. quantity
- wp4(gr%nz) = 0.0_core_rknd
- end if
-
-#ifndef CLUBB_SAM
- if ( ircp2 > 0 ) then
-#endif
- rcp2 = max( zt2zm( rcp2_zt ), zero_threshold ) ! Pos. def. quantity
-#ifndef CLUBB_SAM
- rcp2(gr%nz) = 0.0_core_rknd
- end if
-#endif
-
- wpthvp = zt2zm( wpthvp_zt )
- wpthvp(gr%nz) = 0.0_core_rknd
- thlpthvp = zt2zm( thlpthvp_zt )
- thlpthvp(gr%nz) = 0.0_core_rknd
- rtpthvp = zt2zm( rtpthvp_zt )
- rtpthvp(gr%nz) = 0.0_core_rknd
- wprcp = zt2zm( wprcp_zt )
- wprcp(gr%nz) = 0.0_core_rknd
- rc_coef = zt2zm( rc_coef_zt )
- rc_coef(gr%nz) = 0.0_core_rknd
- rtprcp = zt2zm( rtprcp_zt )
- rtprcp(gr%nz) = 0.0_core_rknd
- thlprcp = zt2zm( thlprcp_zt )
- thlprcp(gr%nz) = 0.0_core_rknd
-
- ! Interpolate passive scalars back onto the m grid
- do i = 1, sclr_dim
- sclrpthvp(:,i) = zt2zm( sclrpthvp_zt(:,i) )
- sclrpthvp(gr%nz,i) = 0.0_core_rknd
- sclrprcp(:,i) = zt2zm( sclrprcp_zt(:,i) )
- sclrprcp(gr%nz,i) = 0.0_core_rknd
- end do ! i=1, sclr_dim
-
- end if ! l_call_pdf_closure_twice
-
- ! If l_trapezoidal_rule_zt is true, call trapezoidal_rule_zt for
- ! thermodynamic-level variables output from pdf_closure.
- ! ldgrant June 2009
- if ( l_trapezoidal_rule_zt ) then
- call trapezoidal_rule_zt &
- ( l_call_pdf_closure_twice, & ! intent(in)
- wprtp2, wpthlp2, & ! intent(inout)
- wprtpthlp, cloud_frac, ice_supersat_frac, & ! intent(inout)
- rcm, wp2thvp, wpsclrprtp, wpsclrp2, & ! intent(inout)
- wpsclrpthlp, pdf_params, & ! intent(inout)
- wprtp2_zm, wpthlp2_zm, & ! intent(inout)
- wprtpthlp_zm, cloud_frac_zm, & ! intent(inout)
- ice_supersat_frac_zm, rcm_zm, wp2thvp_zm, & ! intent(inout)
- wpsclrprtp_zm, wpsclrp2_zm, wpsclrpthlp_zm, & ! intent(inout)
- pdf_params_zm ) ! intent(inout)
- end if ! l_trapezoidal_rule_zt
-
- ! If l_trapezoidal_rule_zm is true, call trapezoidal_rule_zm for
- ! the important momentum-level variabes output from pdf_closure.
- ! ldgrant Feb. 2010
- if ( l_trapezoidal_rule_zm ) then
- call trapezoidal_rule_zm &
- ( wpthvp_zt, thlpthvp_zt, rtpthvp_zt, & ! intent(in)
- wpthvp, thlpthvp, rtpthvp ) ! intent(inout)
- end if ! l_trapezoidal_rule_zm
-
- ! Vince Larson clipped rcm in order to prevent rvm < 0. 5 Apr 2008.
- ! This code won't work unless rtm >= 0 !!!
- ! We do not clip rcm_in_layer because rcm_in_layer only influences
- ! radiation, and we do not want to bother recomputing it.
- ! Code is duplicated from below to ensure that relative humidity
- ! is calculated properly. 3 Sep 2009
- call clip_rcm( rtm, 'rtm < rcm after pdf_closure', & ! intent (in)
- rcm ) ! intent (inout)
-
- ! Compute variables cloud_cover and rcm_in_layer.
- ! Added July 2009
- call compute_cloud_cover &
- ( pdf_params, cloud_frac, rcm, & ! intent(in)
- cloud_cover, rcm_in_layer ) ! intent(out)
-
- ! Use cloud_cover and rcm_in_layer to help boost cloud_frac and rcm to help
- ! increase cloudiness at coarser grid resolutions.
- if ( l_use_cloud_cover ) then
- cloud_frac = cloud_cover
- !ice_supersat_frac = cloud_cover !?-mark
- rcm = rcm_in_layer
- end if
-
- ! Clip cloud fraction here if it still exceeds 1.0 due to round off
- cloud_frac = min( 1.0_core_rknd, cloud_frac )
- ! Ditto with ice cloud fraction
- ice_supersat_frac = min( 1.0_core_rknd, ice_supersat_frac )
-
- if (l_use_ice_latent) then
- !A third call to pdf_closure, with terms modified to include the effects
- !of latent heating due to ice. Thlm and rtm add the effects of ice, and
- !the terms are all renamed with "_frz" appended. The modified terms will
- !be fed into the calculations of the turbulence terms. storer-3/14/13
-
- thlm_frz = thlm - (Lv / (Cp*exner) ) * rfrzm ! Add effects of ice latent heat
- ! Ice is treated as liquid water here
- rtm_frz = rtm + rfrzm
-
-
- do k = 1, gr%nz, 1
-
- call pdf_closure &
- ( p_in_Pa(k), exner(k), thv_ds_zt(k), wm_zt(k), & ! intent(in)
- wp2_zt(k), wp3(k), sigma_sqd_w_zt(k), & ! intent(in)
- Skw_zt(k), rtm_frz(k), rtp2_zt(k), & ! intent(in)
- zm2zt( wprtp, k ), thlm_frz(k), thlp2_zt(k), & ! intent(in)
- zm2zt( wpthlp, k ), rtpthlp_zt(k), sclrm(k,:), & ! intent(in)
- wpsclrp_zt(k,:), sclrp2_zt(k,:), sclrprtp_zt(k,:), & ! intent(in)
- sclrpthlp_zt(k,:), k, & ! intent(in)
-#ifdef GFDL
- RH_crit(k, : , :), do_liquid_only_in_clubb, & ! intent(in) h1g, 2010-06-16
-#endif
- wp4_zt_frz(k), wprtp2_frz(k), wp2rtp_frz(k), & ! intent(out)
- wpthlp2_frz(k), wp2thlp_frz(k), wprtpthlp_frz(k), & ! intent(out)
- cloud_frac_frz(k), ice_supersat_frac_frz(k), & ! intent(out)
- rcm_frz(k), wpthvp_zt_frz(k), wp2thvp_frz(k), rtpthvp_zt_frz(k), & ! intent(out)
- thlpthvp_zt_frz(k), wprcp_zt_frz(k), wp2rcp_frz(k), rtprcp_zt_frz(k), & ! intent(out)
- thlprcp_zt_frz(k), rcp2_zt_frz(k), pdf_params_frz(k), & ! intent(out)
- err_code_pdf_closure, & ! intent(out)
- wpsclrprtp_frz(k,:), wpsclrp2_frz(k,:), sclrpthvp_zt_frz(k,:), & ! intent(out)
- wpsclrpthlp_frz(k,:), sclrprcp_zt_frz(k,:), wp2sclrp_frz(k,:), & ! intent(out)
- rc_coef_zt_frz(k) ) ! intent(out)
-
- ! Subroutine may produce NaN values, and if so, exit gracefully.
- ! Joshua Fasching March 2008
-
- if ( fatal_error( err_code_pdf_closure ) ) then
-
- if ( clubb_at_least_debug_level ( 1 ) )then
- write(fstderr,*) "At grid level = ", k
- end if
-
- err_code = err_code_pdf_closure
- end if
-
- end do !k=1, gr%nz, 1
-
-
- if( l_rtm_nudge ) then
- ! Nudge rtm to prevent excessive drying
- where( rtm < rtm_min .and. gr%zt < rtm_nudge_max_altitude )
- rtm = rtm + (rtm_ref - rtm) * ( real( dt, kind = core_rknd ) / ts_nudge )
- end where
- end if
-
- rtm_zm_frz = zt2zm( rtm_frz )
- ! Clip if extrapolation at the top level causes rtm_zm to be < rt_tol
- rtm_zm_frz(gr%nz) = max( rtm_zm_frz(gr%nz), rt_tol )
- thlm_zm_frz = zt2zm( thlm_frz )
- ! Clip if extrapolation at the top level causes thlm_zm to be < thl_tol
- thlm_zm_frz(gr%nz) = max( thlm_zm_frz(gr%nz), thl_tol )
-
- if ( l_call_pdf_closure_twice ) then
- ! Call pdf_closure again to output the variables which belong on the momentum grid.
- do k=1, gr%nz, 1
- call pdf_closure &
- ( p_in_Pa_zm(k), exner_zm(k), thv_ds_zm(k), wm_zm(k), & ! intent(in)
- wp2(k), wp3_zm(k), sigma_sqd_w(k), & ! intent(in)
- Skw_zm(k), rtm_zm_frz(k), rtp2(k), & ! intent(in)
- wprtp(k), thlm_zm_frz(k), thlp2(k), & ! intent(in)
- wpthlp(k), rtpthlp(k), sclrm_zm(k,:), & ! intent(in)
- wpsclrp(k,:), sclrp2(k,:), sclrprtp(k,:), & ! intent(in)
- sclrpthlp(k,:), k, & ! intent(in)
-#ifdef GFDL
- RH_crit(k, : , :), do_liquid_only_in_clubb, & ! intent(in) h1g, 2010-06-16
-#endif
- wp4_frz(k), wprtp2_zm_frz(k), wp2rtp_zm_frz(k), & ! intent(out)
- wpthlp2_zm_frz(k), wp2thlp_zm_frz(k), wprtpthlp_zm_frz(k), & ! intent(out)
- cloud_frac_zm_frz(k), ice_supersat_frac_zm_frz(k), & ! intent(out)
- rcm_zm_frz(k), wpthvp_frz(k), wp2thvp_zm_frz(k), rtpthvp_frz(k), & ! intent(out)
- thlpthvp_frz(k), wprcp_frz(k), wp2rcp_zm_frz(k), rtprcp_frz(k), & ! intent(out)
- thlprcp_frz(k), rcp2_frz(k), pdf_params_zm_frz(k), & ! intent(out)
- err_code_pdf_closure, & ! intent(out)
- wpsclrprtp_zm_frz(k,:), wpsclrp2_zm_frz(k,:), sclrpthvp_frz(k,:), & ! intent(out)
- wpsclrpthlp_zm_frz(k,:), sclrprcp_frz(k,:), wp2sclrp_zm_frz(k,:), & ! intent(out)
- rc_coef_frz(k) ) ! intent(out)
-
- ! Subroutine may produce NaN values, and if so, exit
- ! gracefully.
- ! Joshua Fasching March 2008
-
-
- if ( fatal_error( err_code_pdf_closure ) ) then
-
- if ( clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "At grid level = ",k
- end if
-
- err_code = err_code_pdf_closure
- end if
-
- end do ! k = 1, gr%nz, 1
- else ! l_call_pdf_closure_twice is false
-
- wpthvp_frz = zt2zm( wpthvp_zt_frz )
- wpthvp_frz(gr%nz) = 0.0_core_rknd
- thlpthvp_frz = zt2zm( thlpthvp_zt_frz )
- thlpthvp_frz(gr%nz) = 0.0_core_rknd
- rtpthvp_frz = zt2zm( rtpthvp_zt_frz )
- rtpthvp_frz(gr%nz) = 0.0_core_rknd
-
- end if ! l_call_pdf_closure_twice
-
- if ( l_trapezoidal_rule_zt ) then
- call trapezoidal_rule_zt &
- ( l_call_pdf_closure_twice, & ! intent(in)
- wprtp2_frz, wpthlp2_frz, & ! intent(inout)
- wprtpthlp_frz, cloud_frac_frz, ice_supersat_frac_frz, & ! intent(inout)
- rcm_frz, wp2thvp_frz, wpsclrprtp_frz, wpsclrp2_frz, & ! intent(inout)
- wpsclrpthlp_frz, pdf_params_frz, & ! intent(inout)
- wprtp2_zm_frz, wpthlp2_zm_frz, & ! intent(inout)
- wprtpthlp_zm_frz, cloud_frac_zm_frz, & ! intent(inout)
- ice_supersat_frac_zm_frz, rcm_zm_frz, wp2thvp_zm_frz, & ! intent(inout)
- wpsclrprtp_zm_frz, wpsclrp2_zm_frz, wpsclrpthlp_zm_frz, & ! intent(inout)
- pdf_params_zm_frz ) ! intent(inout)
- end if ! l_trapezoidal_rule_zt
-
- ! If l_trapezoidal_rule_zm is true, call trapezoidal_rule_zm for
- ! the important momentum-level variabes output from pdf_closure.
- ! ldgrant Feb. 2010
- if ( l_trapezoidal_rule_zm ) then
- call trapezoidal_rule_zm &
- ( wpthvp_zt_frz, thlpthvp_zt_frz, rtpthvp_zt_frz, & ! intent(in)
- wpthvp_frz, thlpthvp_frz, rtpthvp_frz ) ! intent(inout)
- end if ! l_trapezoidal_rule_zm
-
- end if ! l_use_ice_latent = .true.
-
-
-
-
-
- !----------------------------------------------------------------
- ! Compute thvm
- !----------------------------------------------------------------
-
- thvm = thlm + ep1 * thv_ds_zt * rtm &
- + ( Lv/(Cp*exner) - ep2 * thv_ds_zt ) * rcm
-
- !----------------------------------------------------------------
- ! Compute tke (turbulent kinetic energy)
- !----------------------------------------------------------------
-
- if ( .not. l_tke_aniso ) then
- ! tke is assumed to be 3/2 of wp2
- em = three_halves * wp2 ! Known magic number
- else
- em = 0.5_core_rknd * ( wp2 + vp2 + up2 )
- end if
-
- !----------------------------------------------------------------
- ! Compute mixing length
- !----------------------------------------------------------------
-
- if ( l_avg_Lscale .and. .not. l_Lscale_plume_centered ) then
- ! Call compute length two additional times with perturbed values
- ! of rtm and thlm so that an average value of Lscale may be calculated.
- if ( l_use_ice_latent ) then
- !Include the effects of ice in the length scale calculation
-
- thlm_pert_1 = thlm_frz + Lscale_pert_coef * sqrt( max( thlp2, thl_tol**2 ) )
- rtm_pert_1 = rtm_frz + Lscale_pert_coef * sqrt( max( rtp2, rt_tol**2 ) )
- mu_pert_1 = mu / Lscale_mu_coef
-
- thlm_pert_2 = thlm_frz - Lscale_pert_coef * sqrt( max( thlp2, thl_tol**2 ) )
- rtm_pert_2 = rtm_frz - Lscale_pert_coef * sqrt( max( rtp2, rt_tol**2 ) )
- mu_pert_2 = mu * Lscale_mu_coef
- else
- thlm_pert_1 = thlm + Lscale_pert_coef * sqrt( max( thlp2, thl_tol**2 ) )
- rtm_pert_1 = rtm + Lscale_pert_coef * sqrt( max( rtp2, rt_tol**2 ) )
- mu_pert_1 = mu / Lscale_mu_coef
-
- thlm_pert_2 = thlm - Lscale_pert_coef * sqrt( max( thlp2, thl_tol**2 ) )
- rtm_pert_2 = rtm - Lscale_pert_coef * sqrt( max( rtp2, rt_tol**2 ) )
- mu_pert_2 = mu * Lscale_mu_coef
- end if
-
- call compute_length( thvm, thlm_pert_1, rtm_pert_1, em, & ! intent(in)
- p_in_Pa, exner, thv_ds_zt, mu_pert_1, l_implemented, & ! intent(in)
- err_code, & ! intent(inout)
- Lscale_pert_1, Lscale_up, Lscale_down ) ! intent(out)
-
- call compute_length( thvm, thlm_pert_2, rtm_pert_2, em, & ! intent(in)
- p_in_Pa, exner, thv_ds_zt, mu_pert_2, l_implemented, & ! intent(in)
- err_code, & ! intent(inout)
- Lscale_pert_2, Lscale_up, Lscale_down ) ! intent(out)
-
- else if ( l_avg_Lscale .and. l_Lscale_plume_centered ) then
- ! Take the values of thl and rt based one 1st or 2nd plume
-
- do k = 1, gr%nz, 1
- sign_rtpthlp(k) = sign(1.0_core_rknd, rtpthlp(k))
- end do
-
- if ( l_use_ice_latent ) then
- where ( pdf_params_frz%rt1 > pdf_params_frz%rt2 )
- rtm_pert_pos_rt = pdf_params_frz%rt1 &
- + Lscale_pert_coef * sqrt( max( pdf_params_frz%varnce_rt1, rt_tol**2 ) )
- thlm_pert_pos_rt = pdf_params_frz%thl1 + ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params_frz%varnce_thl1, thl_tol**2 ) ) )
- thlm_pert_neg_rt = pdf_params_frz%thl2 - ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params_frz%varnce_thl2, thl_tol**2 ) ) )
- rtm_pert_neg_rt = pdf_params_frz%rt2 &
- - Lscale_pert_coef * sqrt( max( pdf_params_frz%varnce_rt2, rt_tol**2 ) )
- !Lscale_weight = pdf_params%mixt_frac
- else where
- rtm_pert_pos_rt = pdf_params_frz%rt2 &
- + Lscale_pert_coef * sqrt( max( pdf_params_frz%varnce_rt2, rt_tol**2 ) )
- thlm_pert_pos_rt = pdf_params_frz%thl2 + ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params_frz%varnce_thl2, thl_tol**2 ) ) )
- thlm_pert_neg_rt = pdf_params_frz%thl1 - ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params_frz%varnce_thl1, thl_tol**2 ) ) )
- rtm_pert_neg_rt = pdf_params_frz%rt1 &
- - Lscale_pert_coef * sqrt( max( pdf_params_frz%varnce_rt1, rt_tol**2 ) )
- !Lscale_weight = 1.0_core_rknd - pdf_params%mixt_frac
- end where
- else
- where ( pdf_params%rt1 > pdf_params%rt2 )
- rtm_pert_pos_rt = pdf_params%rt1 &
- + Lscale_pert_coef * sqrt( max( pdf_params%varnce_rt1, rt_tol**2 ) )
- thlm_pert_pos_rt = pdf_params%thl1 + ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params%varnce_thl1, thl_tol**2 ) ) )
- thlm_pert_neg_rt = pdf_params%thl2 - ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params%varnce_thl2, thl_tol**2 ) ) )
- rtm_pert_neg_rt = pdf_params%rt2 &
- - Lscale_pert_coef * sqrt( max( pdf_params%varnce_rt2, rt_tol**2 ) )
- !Lscale_weight = pdf_params%mixt_frac
- else where
- rtm_pert_pos_rt = pdf_params%rt2 &
- + Lscale_pert_coef * sqrt( max( pdf_params%varnce_rt2, rt_tol**2 ) )
- thlm_pert_pos_rt = pdf_params%thl2 + ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params%varnce_thl2, thl_tol**2 ) ) )
- thlm_pert_neg_rt = pdf_params%thl1 - ( sign_rtpthlp * Lscale_pert_coef &
- * sqrt( max( pdf_params%varnce_thl1, thl_tol**2 ) ) )
- rtm_pert_neg_rt = pdf_params%rt1 &
- - Lscale_pert_coef * sqrt( max( pdf_params%varnce_rt1, rt_tol**2 ) )
- !Lscale_weight = 1.0_core_rknd - pdf_params%mixt_frac
- end where
- end if
- mu_pert_pos_rt = mu / Lscale_mu_coef
- mu_pert_neg_rt = mu * Lscale_mu_coef
-
- ! Call length with perturbed values of thl and rt
- call compute_length( thvm, thlm_pert_pos_rt, rtm_pert_pos_rt, em, & ! intent(in)
- p_in_Pa, exner, thv_ds_zt, mu_pert_pos_rt, l_implemented, & ! intent(in)
- err_code, & ! intent(inout)
- Lscale_pert_1, Lscale_up, Lscale_down ) ! intent(out)
-
- call compute_length( thvm, thlm_pert_neg_rt, rtm_pert_neg_rt, em, & ! intent(in)
- p_in_Pa, exner, thv_ds_zt, mu_pert_neg_rt, l_implemented, & ! intent(in)
- err_code, & ! intent(inout)
- Lscale_pert_2, Lscale_up, Lscale_down ) ! intent(out)
- else
- Lscale_pert_1 = -999._core_rknd
- Lscale_pert_2 = -999._core_rknd
-
- end if ! l_avg_Lscale
-
- if ( l_stats_samp ) then
- call stat_update_var( iLscale_pert_1, Lscale_pert_1, zt )
- call stat_update_var( iLscale_pert_2, Lscale_pert_2, zt )
- end if ! l_stats_samp
-
- ! ********** NOTE: **********
- ! This call to compute_length must be last. Otherwise, the values of
- ! Lscale_up and Lscale_down in stats will be based on perturbation length scales
- ! rather than the mean length scale.
- call compute_length( thvm, thlm, rtm, em, & ! intent(in)
- p_in_Pa, exner, thv_ds_zt, mu, l_implemented, & ! intent(in)
- err_code, & ! intent(inout)
- Lscale, Lscale_up, Lscale_down ) ! intent(out)
-
- if ( l_avg_Lscale ) then
- if ( l_Lscale_plume_centered ) then
- ! Weighted average of mean, pert_1, & pert_2
-! Lscale = 0.5_core_rknd * ( Lscale + Lscale_weight*Lscale_pert_1 &
-! + (1.0_core_rknd-Lscale_weight)*Lscale_pert_2 )
-
- ! Weighted average of just the perturbed values
-! Lscale = Lscale_weight*Lscale_pert_1 + (1.0_core_rknd-Lscale_weight)*Lscale_pert_2
-
- ! Un-weighted average of just the perturbed values
- Lscale = 0.5_core_rknd*( Lscale_pert_1 + Lscale_pert_2 )
- else
- Lscale = (1.0_core_rknd/3.0_core_rknd) * ( Lscale + Lscale_pert_1 + Lscale_pert_2 )
- end if
- end if
-
- !----------------------------------------------------------------
- ! Dissipation time
- !----------------------------------------------------------------
-! Vince Larson replaced the cutoff of em_min by w_tol**2. 7 Jul 2007
-! This is to prevent tau from being too large (producing little damping)
-! in stably stratified layers with little turbulence.
-! sqrt_em_zt = SQRT( MAX( em_min, zm2zt( em ) ) )
-! tau_zt = MIN( Lscale / sqrt_em_zt, taumax )
-! tau_zm &
-! = MIN( ( zt2zm( Lscale ) / SQRT( MAX( em_min, em ) ) ), taumax )
-! Addition by Brian: Model constant em_min is now set to (3/2)*w_tol_sqd.
-! Thus, em_min can replace w_tol_sqd here.
- sqrt_em_zt = SQRT( MAX( em_min, zm2zt( em ) ) )
-
- tau_zt = MIN( Lscale / sqrt_em_zt, taumax )
- tau_zm = MIN( ( MAX( zt2zm( Lscale ), zero_threshold ) &
- / SQRT( MAX( em_min, em ) ) ), taumax )
-! End Vince Larson's replacement.
-
- ! Modification to damp noise in stable region
-! Vince Larson commented out because it may prevent turbulence from
-! initiating in unstable regions. 7 Jul 2007
-! do k = 1, gr%nz
-! if ( wp2(k) <= 0.005_core_rknd ) then
-! tau_zt(k) = taumin
-! tau_zm(k) = taumin
-! end if
-! end do
-! End Vince Larson's commenting.
-
- !----------------------------------------------------------------
- ! Eddy diffusivity coefficient
- !----------------------------------------------------------------
- ! c_K is 0.548 usually (Duynkerke and Driedonks 1987)
- ! CLUBB uses a smaller value to better fit empirical data.
-
- Kh_zt = c_K * Lscale * sqrt_em_zt
- Kh_zm = c_K * max( zt2zm( Lscale ), zero_threshold ) &
- * sqrt( max( em, em_min ) )
-
-#if defined(CLUBB_CAM) || defined(GFDL) || defined (CLUBB_SAM)
- khzt(:) = Kh_zt(:)
- khzm(:) = Kh_zm(:)
- qclvar(:) = rcp2_zt(:)
-#endif
-
- !----------------------------------------------------------------
- ! Set Surface variances
- !----------------------------------------------------------------
-
- ! Surface variances should be set here, before the call to either
- ! advance_xp2_xpyp or advance_wp2_wp3.
- ! Surface effects should not be included with any case where the lowest
- ! level is not the ground level. Brian Griffin. December 22, 2005.
- if ( gr%zm(1) == sfc_elevation ) then
-
- ! Reflect surface varnce changes in budget
- if ( l_stats_samp ) then
- call stat_begin_update_pt( ithlp2_sf, 1, & ! intent(in)
- thlp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_begin_update_pt( irtp2_sf, 1, & ! intent(in)
- rtp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_begin_update_pt( irtpthlp_sf, 1, & ! intent(in)
- rtpthlp(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_begin_update_pt( iup2_sf, 1, & ! intent(in)
- up2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_begin_update_pt( ivp2_sf, 1, & ! intent(in)
- vp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_begin_update_pt( iwp2_sf, 1, & ! intent(in)
- wp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- end if
-
- call surface_varnce( upwp_sfc, vpwp_sfc, wpthlp_sfc, wprtp_sfc, & ! intent(in)
- um(2), vm(2), wpsclrp_sfc, & ! intent(in)
- wp2(1), up2(1), vp2(1), & ! intent(out)
- thlp2(1), rtp2(1), rtpthlp(1), err_code_surface,& ! intent(out)
- sclrp2(1,1:sclr_dim), & ! intent(out)
- sclrprtp(1,1:sclr_dim), & ! intent(out)
- sclrpthlp(1,1:sclr_dim) ) ! intent(out)
-
- if ( fatal_error( err_code_surface ) ) then
- call reportError( err_code_surface )
- err_code = err_code_surface
- end if
-
- ! Update surface stats
- if ( l_stats_samp ) then
- call stat_end_update_pt( ithlp2_sf, 1, & ! intent(in)
- thlp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_end_update_pt( irtp2_sf, 1, & ! intent(in)
- rtp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_end_update_pt( irtpthlp_sf, 1, & ! intent(in)
- rtpthlp(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_end_update_pt( iup2_sf, 1, & ! intent(in)
- up2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_end_update_pt( ivp2_sf, 1, & ! intent(in)
- vp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- call stat_end_update_pt( iwp2_sf, 1, & ! intent(in)
- wp2(1) / real( dt , kind = core_rknd ), & ! intent(in)
- zm ) ! intent(inout)
- end if
-
- else
-
- ! Variances for cases where the lowest level is not at the surface.
- ! Eliminate surface effects on lowest level variances.
- wp2(1) = w_tol_sqd
- up2(1) = w_tol_sqd
- vp2(1) = w_tol_sqd
- thlp2(1) = thl_tol**2
- rtp2(1) = rt_tol**2
- rtpthlp(1) = 0.0_core_rknd
-
- do i = 1, sclr_dim, 1
- sclrp2(1,i) = 0.0_core_rknd
- sclrprtp(1,i) = 0.0_core_rknd
- sclrpthlp(1,i) = 0.0_core_rknd
- end do
-
- end if ! gr%zm(1) == sfc_elevation
-
-
- !#######################################################################
- !############## ADVANCE PROGNOSTIC VARIABLES ONE TIMESTEP ##############
- !#######################################################################
-
- ! Store the saturation mixing ratio for output purposes. Brian
- ! Compute rsat if either rsat or rel_humidity is to be saved. ldgrant
- if ( ( irsat > 0 ) .or. ( irel_humidity > 0 ) ) then
- rsat = sat_mixrat_liq( p_in_Pa, thlm2T_in_K( thlm, exner, rcm ) )
- end if
-
-
- if ( l_stats_samp ) then
- call stat_update_var( irvm, rtm - rcm, zt )
-
- ! Output relative humidity (q/q∗ where q∗ is the saturation mixing ratio over liquid)
- ! Added an extra check for irel_humidity > 0; otherwise, if both irsat = 0 and
- ! irel_humidity = 0, rsat is not computed, leading to a floating-point exception
- ! when stat_update_var is called for rel_humidity. ldgrant
- if ( irel_humidity > 0 ) then
- call stat_update_var( irel_humidity, (rtm - rcm) / rsat, zt)
- end if ! irel_humidity > 0
- end if ! l_stats_samp
-
- !----------------------------------------------------------------
- ! Advance rtm/wprtp and thlm/wpthlp one time step
- !----------------------------------------------------------------
- if ( l_call_pdf_closure_twice ) then
- w1_zm = pdf_params_zm%w1
- w2_zm = pdf_params_zm%w2
- varnce_w1_zm = pdf_params_zm%varnce_w1
- varnce_w2_zm = pdf_params_zm%varnce_w2
- mixt_frac_zm = pdf_params_zm%mixt_frac
- else
- w1_zm = zt2zm( pdf_params%w1 )
- w2_zm = zt2zm( pdf_params%w2 )
- varnce_w1_zm = zt2zm( pdf_params%varnce_w1 )
- varnce_w2_zm = zt2zm( pdf_params%varnce_w2 )
- mixt_frac_zm = zt2zm( pdf_params%mixt_frac )
- end if
-
- if ( l_use_ice_latent ) then
- !calculate turbulence with terms including ice latent heating
- call advance_xm_wpxp( dt, sigma_sqd_w, wm_zm, wm_zt, wp2, & ! intent(in)
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, Kh_zt, & ! intent(in)
- tau_zm, Skw_zm, rtpthvp_frz, rtm_forcing, & ! intent(in)
- wprtp_forcing, rtm_ref, thlpthvp_frz, & ! intent(in)
- thlm_forcing, wpthlp_forcing, thlm_ref, & ! intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! intent(in)
- invrs_rho_ds_zt, thv_ds_zm, rtp2, thlp2, & ! intent(in)
- w1_zm, w2_zm, varnce_w1_zm, varnce_w2_zm, & ! intent(in)
- mixt_frac_zm, l_implemented, & ! intent(in)
- sclrpthvp, sclrm_forcing, sclrp2, & ! intent(in)
- rtm, wprtp, thlm, wpthlp, & ! intent(inout)
- err_code, & ! intent(inout)
- sclrm, wpsclrp ) ! intent(inout)
- else
- call advance_xm_wpxp( dt, sigma_sqd_w, wm_zm, wm_zt, wp2, & ! intent(in)
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, Kh_zt, & ! intent(in)
- tau_zm, Skw_zm, rtpthvp, rtm_forcing, & ! intent(in)
- wprtp_forcing, rtm_ref, thlpthvp, & ! intent(in)
- thlm_forcing, wpthlp_forcing, thlm_ref, & ! intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! intent(in)
- invrs_rho_ds_zt, thv_ds_zm, rtp2, thlp2, & ! intent(in)
- w1_zm, w2_zm, varnce_w1_zm, varnce_w2_zm, & ! intent(in)
- mixt_frac_zm, l_implemented, & ! intent(in)
- sclrpthvp, sclrm_forcing, sclrp2, & ! intent(in)
- rtm, wprtp, thlm, wpthlp, & ! intent(inout)
- err_code, & ! intent(inout)
- sclrm, wpsclrp ) ! intent(inout)
- end if
-
- ! Vince Larson clipped rcm in order to prevent rvm < 0. 5 Apr 2008.
- ! This code won't work unless rtm >= 0 !!!
- ! We do not clip rcm_in_layer because rcm_in_layer only influences
- ! radiation, and we do not want to bother recomputing it. 6 Aug 2009
- call clip_rcm( rtm, 'rtm < rcm in advance_xm_wpxp', & ! intent(in)
- rcm ) ! intent(inout)
-
-#ifdef GFDL
- call advance_sclrm_Nd_diffusion_OG( dt, sclrm, & ! h1g, 2010-06-16
- sclrm_trsport_only, Kh_zm, cloud_frac, err_code )
-#endif
-
- !----------------------------------------------------------------
- ! Compute some of the variances and covariances. These include the variance of
- ! total water (rtp2), liquid potential termperature (thlp2), their
- ! covariance (rtpthlp), and the variance of horizontal wind (up2 and vp2).
- ! The variance of vertical velocity is computed later.
- !----------------------------------------------------------------
-
- ! We found that certain cases require a time tendency to run
- ! at shorter timesteps so these are prognosed now.
-
- ! We found that if we call advance_xp2_xpyp first, we can use a longer timestep.
- if ( l_use_ice_latent) then
- ! calculate turbulence with terms including ice latent heating
- call advance_xp2_xpyp( tau_zm, wm_zm, rtm, wprtp, thlm, & ! intent(in)
- wpthlp, wpthvp_frz, um, vm, wp2, wp2_zt, & ! intent(in)
- wp3, upwp, vpwp, sigma_sqd_w, Skw_zm, & ! intent(in)
- Kh_zt, rtp2_forcing, thlp2_forcing, & ! intent(in)
- rtpthlp_forcing, rho_ds_zm, rho_ds_zt, & ! intent(in)
- invrs_rho_ds_zm, thv_ds_zm, & ! intent(in)
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, & ! intent(in)
- l_iter_xp2_xpyp, dt, & ! intent(in)
- sclrm, wpsclrp, & ! intent(in)
- rtp2, thlp2, rtpthlp, up2, vp2, & ! intent(inout)
- err_code, & ! intent(inout)
- sclrp2, sclrprtp, sclrpthlp ) ! intent(inout)
- else
- call advance_xp2_xpyp( tau_zm, wm_zm, rtm, wprtp, thlm, & ! intent(in)
- wpthlp, wpthvp, um, vm, wp2, wp2_zt, & ! intent(in)
- wp3, upwp, vpwp, sigma_sqd_w, Skw_zm, & ! intent(in)
- Kh_zt, rtp2_forcing, thlp2_forcing, & ! intent(in)
- rtpthlp_forcing, rho_ds_zm, rho_ds_zt, & ! intent(in)
- invrs_rho_ds_zm, thv_ds_zm, & ! intent(in)
- Lscale, wp3_on_wp2, wp3_on_wp2_zt, & ! intent(in)
- l_iter_xp2_xpyp, dt, & ! intent(in)
- sclrm, wpsclrp, & ! intent(in)
- rtp2, thlp2, rtpthlp, up2, vp2, & ! intent(inout)
- err_code, & ! intent(inout)
- sclrp2, sclrprtp, sclrpthlp ) ! intent(inout)
- end if
-
- !----------------------------------------------------------------
- ! Covariance clipping for wprtp, wpthlp, wpsclrp, upwp, and vpwp
- ! after subroutine advance_xp2_xpyp updated xp2.
- !----------------------------------------------------------------
-
- wprtp_cl_num = 2 ! Second instance of w'r_t' clipping.
- wpthlp_cl_num = 2 ! Second instance of w'th_l' clipping.
- wpsclrp_cl_num = 2 ! Second instance of w'sclr' clipping.
- upwp_cl_num = 1 ! First instance of u'w' clipping.
- vpwp_cl_num = 1 ! First instance of v'w' clipping.
-
- call clip_covars_denom( dt, rtp2, thlp2, up2, vp2, wp2, & ! intent(in)
- sclrp2, wprtp_cl_num, wpthlp_cl_num, & ! intent(in)
- wpsclrp_cl_num, upwp_cl_num, vpwp_cl_num, & ! intent(in)
- wprtp, wpthlp, upwp, vpwp, wpsclrp ) ! intent(inout)
-
-
- !----------------------------------------------------------------
- ! Advance 2nd and 3rd order moment of vertical velocity (wp2 / wp3)
- ! by one timestep
- !----------------------------------------------------------------
-
- if ( l_use_ice_latent) then
- call advance_wp2_wp3 &
- ( dt, sfc_elevation, sigma_sqd_w, wm_zm, wm_zt, & ! intent(in)
- a3_coef, a3_coef_zt, wp3_on_wp2, & ! intent(in)
- wpthvp_frz, wp2thvp_frz, um, vm, upwp, vpwp, & ! intent(in)
- up2, vp2, Kh_zm, Kh_zt, tau_zm, tau_zt, & ! intent(in)
- Skw_zm, Skw_zt, rho_ds_zm, rho_ds_zt, & ! intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, radf, & ! intent(in)
- thv_ds_zm, thv_ds_zt, pdf_params%mixt_frac, & ! intent(in)
- wp2, wp3, wp3_zm, wp2_zt, err_code ) ! intent(inout)
- else
- call advance_wp2_wp3 &
- ( dt, sfc_elevation, sigma_sqd_w, wm_zm, wm_zt, & ! intent(in)
- a3_coef, a3_coef_zt, wp3_on_wp2, & ! intent(in)
- wpthvp, wp2thvp, um, vm, upwp, vpwp, & ! intent(in)
- up2, vp2, Kh_zm, Kh_zt, tau_zm, tau_zt, & ! intent(in)
- Skw_zm, Skw_zt, rho_ds_zm, rho_ds_zt, & ! intent(in)
- invrs_rho_ds_zm, invrs_rho_ds_zt, radf, & ! intent(in)
- thv_ds_zm, thv_ds_zt, pdf_params%mixt_frac, & ! intent(in)
- wp2, wp3, wp3_zm, wp2_zt, err_code ) ! intent(inout)
- end if
-
- !----------------------------------------------------------------
- ! Covariance clipping for wprtp, wpthlp, wpsclrp, upwp, and vpwp
- ! after subroutine advance_wp2_wp3 updated wp2.
- !----------------------------------------------------------------
-
- wprtp_cl_num = 3 ! Third instance of w'r_t' clipping.
- wpthlp_cl_num = 3 ! Third instance of w'th_l' clipping.
- wpsclrp_cl_num = 3 ! Third instance of w'sclr' clipping.
- upwp_cl_num = 2 ! Second instance of u'w' clipping.
- vpwp_cl_num = 2 ! Second instance of v'w' clipping.
-
- call clip_covars_denom( dt, rtp2, thlp2, up2, vp2, wp2, & ! intent(in)
- sclrp2, wprtp_cl_num, wpthlp_cl_num, & ! intent(in)
- wpsclrp_cl_num, upwp_cl_num, vpwp_cl_num, & ! intent(in)
- wprtp, wpthlp, upwp, vpwp, wpsclrp ) ! intent(inout)
-
- !----------------------------------------------------------------
- ! Advance the horizontal mean of the wind in the x-y directions
- ! (i.e. um, vm) and the mean of the eddy-diffusivity scalars
- ! (i.e. edsclrm) by one time step
- !----------------------------------------------------------------
-
- call advance_windm_edsclrm( dt, wm_zt, Kh_zm, ug, vg, um_ref, vm_ref, & ! Intent(in)
- wp2, up2, vp2, um_forcing, vm_forcing, & ! Intent(in)
- edsclrm_forcing, & ! Intent(in)
- rho_ds_zm, invrs_rho_ds_zt, & ! Intent(in)
- fcor, l_implemented, & ! Intent(in)
- um, vm, edsclrm, & ! Intent(inout)
- upwp, vpwp, wpedsclrp, & ! Intent(inout)
- err_code ) ! Intent(inout)
-
- !#######################################################################
- !############# ACCUMULATE STATISTICS #############
- !#######################################################################
-
- if ( l_stats_samp ) then
-
- call stat_end_update( iwp2_bt, wp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( ivp2_bt, vp2 / real( dt , kind = core_rknd ),& ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( iup2_bt, up2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( iwprtp_bt, wprtp / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( iwpthlp_bt, wpthlp / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( irtp2_bt, rtp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( ithlp2_bt, thlp2 / real( dt , kind = core_rknd ), & ! Intent(in)
- zm ) ! Intent(inout)
- call stat_end_update( irtpthlp_bt, rtpthlp / real( dt , kind = core_rknd ), &! Intent(in)
- zm ) ! Intent(inout)
-
- call stat_end_update( irtm_bt, rtm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_end_update( ithlm_bt, thlm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_end_update( ium_bt, um / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_end_update( ivm_bt, vm / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
- call stat_end_update( iwp3_bt, wp3 / real( dt , kind = core_rknd ), & ! Intent(in)
- zt ) ! Intent(inout)
-
- end if ! l_stats_samp
-
-
- if ( iwpthlp_zt > 0 ) then
- wpthlp_zt = zm2zt( wpthlp )
- end if
-
- if ( iwprtp_zt > 0 ) then
- wprtp_zt = zm2zt( wprtp )
- end if
-
- if ( iup2_zt > 0 ) then
- up2_zt = max( zm2zt( up2 ), w_tol_sqd )
- end if
-
- if (ivp2_zt > 0 ) then
- vp2_zt = max( zm2zt( vp2 ), w_tol_sqd )
- end if
-
- if ( iupwp_zt > 0 ) then
- upwp_zt = zm2zt( upwp )
- end if
-
- if ( ivpwp_zt > 0 ) then
- vpwp_zt = zm2zt( vpwp )
- end if
-
- call stats_accumulate &
- ( um, vm, upwp, vpwp, up2, vp2, & ! intent(in)
- thlm, rtm, wprtp, wpthlp, & ! intent(in)
- wp2, wp3, rtp2, thlp2, rtpthlp, & ! intent(in)
- p_in_Pa, exner, rho, rho_zm, & ! intent(in)
- rho_ds_zm, rho_ds_zt, thv_ds_zm, & ! intent(in)
- thv_ds_zt, wm_zt, wm_zm, rcm, wprcp, rc_coef, & ! intent(in)
- rcm_zm, rtm_zm, thlm_zm, cloud_frac, ice_supersat_frac,& ! intent(in)
- cloud_frac_zm, ice_supersat_frac_zm, rcm_in_layer, & ! intent(in)
- cloud_cover, sigma_sqd_w, pdf_params, & ! intent(in)
- sclrm, sclrp2, sclrprtp, sclrpthlp, sclrm_forcing, & ! intent(in)
- wpsclrp, edsclrm, edsclrm_forcing ) ! intent(in)
-
-
- if ( clubb_at_least_debug_level( 2 ) ) then
- call parameterization_check &
- ( thlm_forcing, rtm_forcing, um_forcing, vm_forcing, & ! intent(in)
- wm_zm, wm_zt, p_in_Pa, rho_zm, rho, exner, & ! intent(in)
- rho_ds_zm, rho_ds_zt, invrs_rho_ds_zm, & ! intent(in)
- invrs_rho_ds_zt, thv_ds_zm, thv_ds_zt, & ! intent(in)
- wpthlp_sfc, wprtp_sfc, upwp_sfc, vpwp_sfc, & ! intent(in)
- um, upwp, vm, vpwp, up2, vp2, & ! intent(in)
- rtm, wprtp, thlm, wpthlp, & ! intent(in)
- wp2, wp3, rtp2, thlp2, rtpthlp, & ! intent(in)
- "end of ", & ! intent(in)
- wpsclrp_sfc, wpedsclrp_sfc, & ! intent(in)
- sclrm, wpsclrp, sclrp2, sclrprtp, sclrpthlp, & ! intent(in)
- sclrm_forcing, edsclrm, edsclrm_forcing, & ! intent(in)
- err_code ) ! intent(inout)
- end if
-
- if ( l_stats .and. l_stats_samp ) then
- ! Spurious source will only be calculated if rtm_ma and thlm_ma are zero.
- ! Therefore, wm must be zero or l_implemented must be true.
- if ( l_implemented .or. ( all( wm_zt == 0._core_rknd ) .and. &
- all( wm_zm == 0._core_rknd ) ) ) then
- ! Calculate the spurious source for rtm
- rtm_flux_top = rho_ds_zm(gr%nz) * wprtp(gr%nz)
-
- if ( .not. l_host_applies_sfc_fluxes ) then
- rtm_flux_sfc = rho_ds_zm(1) * wprtp_sfc
- else
- rtm_flux_sfc = 0.0_core_rknd
- end if
-
- rtm_integral_after &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- rtm(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
-
- rtm_integral_forcing &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- rtm_forcing(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
-
- rtm_spur_src &
- = calculate_spurious_source( rtm_integral_after, &
- rtm_integral_before, &
- rtm_flux_top, rtm_flux_sfc, &
- rtm_integral_forcing, &
- real( dt , kind = core_rknd ) )
-
- ! Calculate the spurious source for thlm
- thlm_flux_top = rho_ds_zm(gr%nz) * wpthlp(gr%nz)
-
- if ( .not. l_host_applies_sfc_fluxes ) then
- thlm_flux_sfc = rho_ds_zm(1) * wpthlp_sfc
- else
- thlm_flux_sfc = 0.0_core_rknd
- end if
-
- thlm_integral_after &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- thlm(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
-
- thlm_integral_forcing &
- = vertical_integral( (gr%nz - 2 + 1), rho_ds_zt(2:gr%nz), &
- thlm_forcing(2:gr%nz), gr%invrs_dzt(2:gr%nz) )
-
- thlm_spur_src &
- = calculate_spurious_source( thlm_integral_after, &
- thlm_integral_before, &
- thlm_flux_top, thlm_flux_sfc, &
- thlm_integral_forcing, &
- real( dt , kind = core_rknd ) )
- else ! If l_implemented is false, we don't want spurious source output
- rtm_spur_src = -9999.0_core_rknd
- thlm_spur_src = -9999.0_core_rknd
- end if
-
- ! Write the var to stats
- call stat_update_var_pt( irtm_spur_src, 1, &
- rtm_spur_src, sfc )
- call stat_update_var_pt( ithlm_spur_src, 1, &
- thlm_spur_src, sfc )
- end if
-
- return
- end subroutine advance_clubb_core
-
- !-----------------------------------------------------------------------
- subroutine setup_clubb_core &
- ( nzmax, T0_in, ts_nudge_in, & ! In
- hydromet_dim_in, sclr_dim_in, & ! In
- sclr_tol_in, edsclr_dim_in, params, & ! In
- l_host_applies_sfc_fluxes, & ! In
- l_uv_nudge, saturation_formula, & ! In
-#ifdef GFDL
- I_sat_sphum, & ! intent(in) h1g, 2010-06-16
-#endif
- l_implemented, grid_type, deltaz, zm_init, zm_top, & ! In
- momentum_heights, thermodynamic_heights, & ! In
- host_dx, host_dy, sfc_elevation, & ! In
-#ifdef GFDL
- cloud_frac_min , & ! intent(in) h1g, 2010-06-16
-#endif
- err_code ) ! Out
- !
- ! Description:
- ! Subroutine to set up the model for execution.
- !
- ! References:
- ! None
- !-------------------------------------------------------------------------
- use crmx_grid_class, only: &
- setup_grid, & ! Procedure
- gr ! Variable(s)
-
- use crmx_parameter_indices, only: &
- nparams ! Variable(s)
-
- use crmx_parameters_tunable, only: &
- setup_parameters ! Procedure
-
- use crmx_parameters_model, only: &
- setup_parameters_model ! Procedure
-
- use crmx_variables_diagnostic_module, only: &
- setup_diagnostic_variables ! Procedure
-
- use crmx_variables_prognostic_module, only: &
- setup_prognostic_variables ! Procedure
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_error_code, only: &
- clubb_no_error ! Constant(s)
-
- use crmx_model_flags, only: &
- setup_model_flags, & ! Subroutine
- l_gmres ! Variable
-
-#ifdef MKL
- use crmx_csr_matrix_class, only: &
- initialize_csr_class, & ! Subroutine
- intlc_5d_5d_ja_size ! Variable
-
- use crmx_gmres_wrap, only: &
- gmres_init ! Subroutine
-
- use crmx_gmres_cache, only: &
- gmres_cache_temp_init, & ! Subroutine
- gmres_idx_wp2wp3 ! Variable
-#endif /* MKL */
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
-
- ! Grid definition
- integer, intent(in) :: nzmax ! Vertical grid levels [#]
- ! Only true when used in a host model
- ! CLUBB determines what nzmax should be
- ! given zm_init and zm_top when
- ! running in standalone mode.
-
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- ! Flag to see if CLUBB is running on it's own,
- ! or if it's implemented as part of a host model.
- logical, intent(in) :: l_implemented ! (T/F)
-
- ! If CLUBB is running on it's own, this option determines
- ! if it is using:
- ! 1) an evenly-spaced grid,
- ! 2) a stretched (unevenly-spaced) grid entered on the
- ! thermodynamic grid levels (with momentum levels set
- ! halfway between thermodynamic levels), or
- ! 3) a stretched (unevenly-spaced) grid entered on the
- ! momentum grid levels (with thermodynamic levels set
- ! halfway between momentum levels).
- integer, intent(in) :: grid_type
-
- ! If the CLUBB model is running by itself, and is using an
- ! evenly-spaced grid (grid_type = 1), it needs the vertical
- ! grid spacing, momentum-level starting altitude, and maximum
- ! altitude as input.
- real( kind = core_rknd ), intent(in) :: &
- deltaz, & ! Change in altitude per level [m]
- zm_init, & ! Initial grid altitude (momentum level) [m]
- zm_top ! Maximum grid altitude (momentum level) [m]
-
- ! If the CLUBB parameterization is implemented in a host model,
- ! it needs to use the host model's momentum level altitudes
- ! and thermodynamic level altitudes.
- ! If the CLUBB model is running by itself, but is using a
- ! stretched grid entered on thermodynamic levels (grid_type = 2),
- ! it needs to use the thermodynamic level altitudes as input.
- ! If the CLUBB model is running by itself, but is using a
- ! stretched grid entered on momentum levels (grid_type = 3),
- ! it needs to use the momentum level altitudes as input.
- real( kind = core_rknd ), intent(in), dimension(nzmax) :: &
- momentum_heights, & ! Momentum level altitudes (input) [m]
- thermodynamic_heights ! Thermodynamic level altitudes (input) [m]
-
- ! Host model horizontal grid spacing, if part of host model.
- real( kind = core_rknd ), intent(in) :: &
- host_dx, & ! East-West horizontal grid spacing [m]
- host_dy ! North-South horizontal grid spacing [m]
-
- ! Model parameters
- real( kind = core_rknd ), intent(in) :: &
- T0_in, ts_nudge_in
-
- integer, intent(in) :: &
- hydromet_dim_in, & ! Number of hydrometeor species
- sclr_dim_in, & ! Number of passive scalars
- edsclr_dim_in ! Number of eddy-diff. passive scalars
-
- real( kind = core_rknd ), intent(in), dimension(sclr_dim_in) :: &
- sclr_tol_in ! Thresholds for passive scalars
-
- real( kind = core_rknd ), intent(in), dimension(nparams) :: &
- params ! Including C1, nu1, nu2, etc.
-
- ! Flags
- logical, intent(in) :: &
- l_uv_nudge, & ! Wind nudging
- l_host_applies_sfc_fluxes ! Whether to apply for the surface flux
-
- character(len=*), intent(in) :: &
- saturation_formula ! Approximation for saturation vapor pressure
-
-#ifdef GFDL
- logical, intent(in) :: & ! h1g, 2010-06-16 begin mod
- I_sat_sphum
-
- real( kind = core_rknd ), intent(in) :: &
- cloud_frac_min ! h1g, 2010-06-16 end mod
-#endif
-
- ! Output variables
- integer, intent(out) :: &
- err_code ! Diagnostic for a problem with the setup
-
- ! Local variables
- real( kind = core_rknd ) :: Lscale_max
- integer :: begin_height, end_height
-
- !----- Begin Code -----
-
- ! Sanity check for the saturation formula
- select case ( trim( saturation_formula ) )
- case ( "bolton", "Bolton" )
- ! Using the Bolton 1980 approximations for SVP over vapor/ice
-
- case ( "flatau", "Flatau" )
- ! Using the Flatau, et al. polynomial approximation for SVP over vapor/ice
-
- case ( "gfdl", "GFDL" ) ! h1g, 2010-06-16
- ! Using the GFDL SVP formula (Goff-Gratch)
-
- ! Add new saturation formulas after this
-
- case default
- write(fstderr,*) "Error in setup_clubb_core."
- write(fstderr,*) "Unknown approx. of saturation vapor pressure: "// &
- trim( saturation_formula )
- stop
- end select
-
- ! Setup grid
- call setup_grid( nzmax, sfc_elevation, l_implemented, & ! intent(in)
- grid_type, deltaz, zm_init, zm_top, & ! intent(in)
- momentum_heights, thermodynamic_heights, & ! intent(in)
- begin_height, end_height ) ! intent(out)
-
- ! Setup flags
-#ifdef GFDL
- call setup_model_flags &
- ( l_host_applies_sfc_fluxes, & ! intent(in)
- l_uv_nudge, saturation_formula, & ! intent(in)
- I_sat_sphum ) ! intent(in) h1g, 2010-06-16
-
-#else
- call setup_model_flags &
- ( l_host_applies_sfc_fluxes, & ! intent(in)
- l_uv_nudge, saturation_formula ) ! intent(in)
-#endif
-
- ! Determine the maximum allowable value for Lscale (in meters).
- call set_Lscale_max( l_implemented, host_dx, host_dy, & ! Intent(in)
- Lscale_max ) ! Intent(out)
-
- ! Define model constant parameters
-#ifdef GFDL
- call setup_parameters_model( T0_in, ts_nudge_in, & ! In
- hydromet_dim_in, & ! in
- sclr_dim_in, sclr_tol_in, edsclr_dim_in, &! In
- Lscale_max, cloud_frac_min ) ! In h1g, 2010-06-16
-#else
- call setup_parameters_model( T0_in, ts_nudge_in, & ! In
- hydromet_dim_in, & ! in
- sclr_dim_in, sclr_tol_in, edsclr_dim_in, &! In
- Lscale_max ) ! In
-#endif
-
- ! Define tunable constant parameters
- call setup_parameters &
- ( deltaz, params, gr%nz, & ! intent(in)
- grid_type, momentum_heights(begin_height:end_height), & ! intent(in)
- thermodynamic_heights(begin_height:end_height), & ! intent(in)
- err_code ) ! intent(out)
-
- ! Error Report
- ! Joshua Fasching February 2008
- if ( err_code /= clubb_no_error ) then
-
- write(fstderr,*) "Error in setup_clubb_core"
-
- write(fstderr,*) "Intent(in)"
-
- write(fstderr,*) "deltaz = ", deltaz
- write(fstderr,*) "zm_init = ", zm_init
- write(fstderr,*) "zm_top = ", zm_top
- write(fstderr,*) "momentum_heights = ", momentum_heights
- write(fstderr,*) "thermodynamic_heights = ", &
- thermodynamic_heights
- write(fstderr,*) "T0_in = ", T0_in
- write(fstderr,*) "ts_nudge_in = ", ts_nudge_in
- write(fstderr,*) "params = ", params
-
- return
-
- end if
-
-#ifdef GFDL
-! setup prognostic_variables
- call setup_prognostic_variables( gr%nz ) ! intent(in) h1g, 2010-06-16
-#else
- if ( .not. l_implemented ) then
- call setup_prognostic_variables( gr%nz ) ! intent(in)
- end if
-#endif
-
- ! The diagnostic variables need to be
- ! declared, allocated, initialized, and deallocated whether CLUBB
- ! is part of a larger model or not.
- call setup_diagnostic_variables( gr%nz )
-
-#ifdef MKL
- ! Initialize the CSR matrix class.
- if ( l_gmres ) then
- call initialize_csr_class
- end if
-
- if ( l_gmres ) then
- call gmres_cache_temp_init( gr%nz )
- call gmres_init( (2 * gr%nz), intlc_5d_5d_ja_size )
- end if
-#endif /* MKL */
-
- return
- end subroutine setup_clubb_core
-
- !----------------------------------------------------------------------------
- subroutine cleanup_clubb_core( l_implemented )
- !
- ! Description:
- ! Frees memory used by the model itself.
- !
- ! References:
- ! None
- !---------------------------------------------------------------------------
- use crmx_parameters_model, only: sclr_tol ! Variable
-
- use crmx_variables_diagnostic_module, only: &
- cleanup_diagnostic_variables ! Procedure
-
- use crmx_variables_prognostic_module, only: &
- cleanup_prognostic_variables ! Procedure
-
- use crmx_grid_class, only: &
- cleanup_grid ! Procedure
-
- use crmx_parameters_tunable, only: &
- cleanup_nu ! Procedure
-
- implicit none
-
- ! Flag to see if CLUBB is running on it's own,
- ! or if it's implemented as part of a host model.
- logical, intent(in) :: l_implemented ! (T/F)
-
- !----- Begin Code -----
-#ifdef GFDL
- ! cleanup prognostic_variables
- call cleanup_prognostic_variables( ) ! h1g, 2010-06-16
-#else
- if ( .not. l_implemented ) then
- call cleanup_prognostic_variables( )
- end if
-#endif
-
- ! The diagnostic variables need to be
- ! declared, allocated, initialized, and deallocated whether CLUBB
- ! is part of a larger model or not.
- call cleanup_diagnostic_variables( )
-
- ! De-allocate the array for the passive scalar tolerances
- deallocate( sclr_tol )
-
- ! De-allocate the arrays for the grid
- call cleanup_grid( )
-
- ! De-allocate the arrays for nu
- call cleanup_nu( )
-
- return
- end subroutine cleanup_clubb_core
-
- !-----------------------------------------------------------------------
- subroutine trapezoidal_rule_zt &
- ( l_call_pdf_closure_twice, & ! intent(in)
- wprtp2, wpthlp2, & ! intent(inout)
- wprtpthlp, cloud_frac, ice_supersat_frac, & ! intent(inout)
- rcm, wp2thvp, wpsclrprtp, wpsclrp2, & ! intent(inout)
- wpsclrpthlp, pdf_params, & ! intent(inout)
- wprtp2_zm, wpthlp2_zm, & ! intent(inout)
- wprtpthlp_zm, cloud_frac_zm, & ! intent(inout)
- ice_supersat_frac_zm, rcm_zm, wp2thvp_zm, & ! intent(inout)
- wpsclrprtp_zm, wpsclrp2_zm, wpsclrpthlp_zm, & ! intent(inout)
- pdf_params_zm ) ! intent(inout)
- !
- ! Description:
- ! This subroutine takes the output variables on the thermo.
- ! grid and either: interpolates them to the momentum grid, or uses the
- ! values output from the second call to pdf_closure on momentum levels if
- ! l_call_pdf_closure_twice is true. It then calls the function
- ! trapezoid_zt to recompute the variables on the thermo. grid.
- !
- ! ldgrant June 2009
- !
- ! Note:
- ! The argument variables in the last 5 lines of the subroutine
- ! (wprtp2_zm through pdf_params_zm) are declared intent(inout) because
- ! if l_call_pdf_closure_twice is true, these variables will already have
- ! values from pdf_closure on momentum levels and will not be altered in
- ! this subroutine. However, if l_call_pdf_closure_twice is false, these
- ! variables will not have values yet and will be interpolated to
- ! momentum levels in this subroutine.
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_stats_variables, only: &
- iwprtp2, & ! Varibles
- iwprtpthlp, &
- iwpthlp2, &
- iwprtp2, &
- iwpsclrp2, &
- iwpsclrprtp, &
- iwpsclrpthlp, &
- l_stats
-
- use crmx_grid_class, only: &
- gr, & ! Variable
- zt2zm ! Procedure
-
- use crmx_parameters_model, only: &
- sclr_dim ! Number of passive scalar variables
-
- use crmx_pdf_parameter_module, only: &
- pdf_parameter ! Derived data type
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- logical, parameter :: &
- l_apply_rule_to_pdf_params = .false. ! Apply the trapezoidal rule to pdf_params
-
- ! Input variables
- logical, intent(in) :: l_call_pdf_closure_twice
-
- ! Input/Output variables
- ! Thermodynamic level variables output from the first call to pdf_closure
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wprtp2, & ! w'rt'^2 [m kg^2/kg^2]
- wpthlp2, & ! w'thl'^2 [m K^2/s]
- wprtpthlp, & ! w'rt'thl' [m kg K/kg s]
- cloud_frac, & ! Cloud Fraction [-]
- ice_supersat_frac, & ! Ice Cloud Fraction [-]
- rcm, & ! Liquid water mixing ratio [kg/kg]
- wp2thvp ! w'^2 th_v' [m^2 K/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim), intent(inout) :: &
- wpsclrprtp, & ! w'sclr'rt'
- wpsclrp2, & ! w'sclr'^2
- wpsclrpthlp ! w'sclr'thl'
-
- type (pdf_parameter), dimension(gr%nz), intent(inout) :: &
- pdf_params ! PDF parameters [units vary]
-
- ! Thermo. level variables brought to momentum levels either by
- ! interpolation (in subroutine trapezoidal_rule_zt) or by
- ! the second call to pdf_closure (in subroutine advance_clubb_core)
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wprtp2_zm, & ! w'rt'^2 on momentum grid [m kg^2/kg^2]
- wpthlp2_zm, & ! w'thl'^2 on momentum grid [m K^2/s]
- wprtpthlp_zm, & ! w'rt'thl' on momentum grid [m kg K/kg s]
- cloud_frac_zm, & ! Cloud Fraction on momentum grid [-]
- ice_supersat_frac_zm, & ! Ice Cloud Fraction on momentum grid [-]
- rcm_zm, & ! Liquid water mixing ratio on momentum grid [kg/kg]
- wp2thvp_zm ! w'^2 th_v' on momentum grid [m^2 K/s^2]
-
- real( kind = core_rknd ), dimension(gr%nz,sclr_dim), intent(inout) :: &
- wpsclrprtp_zm, & ! w'sclr'rt' on momentum grid
- wpsclrp2_zm, & ! w'sclr'^2 on momentum grid
- wpsclrpthlp_zm ! w'sclr'thl' on momentum grid
-
- type (pdf_parameter), dimension(gr%nz), intent(inout) :: &
- pdf_params_zm ! PDF parameters on momentum grid [units vary]
-
- ! Local variables
-
- ! Components of PDF_parameters on the momentum grid (_zm) and on the thermo. grid (_zt)
- real( kind = core_rknd ), dimension(gr%nz) :: &
- w1_zt, & ! Mean of w for 1st normal distribution [m/s]
- w1_zm, & ! Mean of w for 1st normal distribution [m/s]
- w2_zm, & ! Mean of w for 2nd normal distribution [m/s]
- w2_zt, & ! Mean of w for 2nd normal distribution [m/s]
- varnce_w1_zm, & ! Variance of w for 1st normal distribution [m^2/s^2]
- varnce_w1_zt, & ! Variance of w for 1st normal distribution [m^2/s^2]
- varnce_w2_zm, & ! Variance of w for 2nd normal distribution [m^2/s^2]
- varnce_w2_zt, & ! Variance of w for 2nd normal distribution [m^2/s^2]
- rt1_zm, & ! Mean of r_t for 1st normal distribution [kg/kg]
- rt1_zt, & ! Mean of r_t for 1st normal distribution [kg/kg]
- rt2_zm, & ! Mean of r_t for 2nd normal distribution [kg/kg]
- rt2_zt, & ! Mean of r_t for 2nd normal distribution [kg/kg]
- varnce_rt1_zm, & ! Variance of r_t for 1st normal distribution [kg^2/kg^2]
- varnce_rt1_zt, & ! Variance of r_t for 1st normal distribution [kg^2/kg^2]
- varnce_rt2_zm, & ! Variance of r_t for 2nd normal distribution [kg^2/kg^2]
- varnce_rt2_zt, & ! Variance of r_t for 2nd normal distribution [kg^2/kg^2]
- crt1_zm, & ! Coefficient for s' [-]
- crt1_zt, & ! Coefficient for s' [-]
- crt2_zm ! Coefficient for s' [-]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- crt2_zt, & ! Coefficient for s' [-]
- cthl1_zm, & ! Coefficient for s' [1/K]
- cthl1_zt, & ! Coefficient for s' [1/K]
- cthl2_zm, & ! Coefficient for s' [1/K]
- cthl2_zt, & ! Coefficient for s' [1/K]
- thl1_zm, & ! Mean of th_l for 1st normal distribution [K]
- thl1_zt, & ! Mean of th_l for 1st normal distribution [K]
- thl2_zm, & ! Mean of th_l for 2nd normal distribution [K]
- thl2_zt, & ! Mean of th_l for 2nd normal distribution
- varnce_thl1_zm, & ! Variance of th_l for 1st normal distribution [K^2]
- varnce_thl1_zt, & ! Variance of th_l for 1st normal distribution [K^2]
- varnce_thl2_zm, & ! Variance of th_l for 2nd normal distribution [K^2]
- varnce_thl2_zt ! Variance of th_l for 2nd normal distribution [K^2]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- mixt_frac_zm, & ! Weight of 1st normal distribution (Sk_w dependent) [-]
- mixt_frac_zt, & ! Weight of 1st normal distribution (Sk_w dependent) [-]
- rc1_zm, & ! Mean of r_c for 1st normal distribution [kg/kg]
- rc1_zt, & ! Mean of r_c for 1st normal distribution [kg/kg]
- rc2_zm, & ! Mean of r_c for 2nd normal distribution [kg/kg]
- rc2_zt, & ! Mean of r_c for 2nd normal distribution [kg/kg]
- rsl1_zm, & ! Mean of r_sl for 1st normal distribution [kg/kg]
- rsl1_zt, & ! Mean of r_sl for 1st normal distribution [kg/kg]
- rsl2_zm, & ! Mean of r_sl for 2nd normal distribution [kg/kg]
- rsl2_zt, & ! Mean of r_sl for 2nd normal distribution [kg/kg]
- cloud_frac1_zm, & ! Cloud fraction for 1st normal distribution [-]
- cloud_frac1_zt, & ! Cloud fraction for 1st normal distribution [-]
- cloud_frac2_zm, & ! Cloud fraction for 2nd normal distribution [-]
- cloud_frac2_zt, & ! Cloud fraction for 2nd normal distribution [-]
- s1_zm, & ! Mean of s for 1st normal distribution [kg/kg]
- s1_zt, & ! Mean of s for 1st normal distribution [kg/kg]
- s2_zm, & ! Mean of s for 2nd normal distribution [kg/kg]
- s2_zt, & ! Mean of s for 2nd normal distribution [kg/kg]
- stdev_s1_zm ! Standard deviation of s for 1st normal distribution [kg/kg]
-
- real( kind = core_rknd ), dimension(gr%nz) :: &
- stdev_s1_zt, & ! Standard deviation of s for 1st normal distribution [kg/kg]
- stdev_s2_zm, & ! Standard deviation of s for 2nd normal distribution [kg/kg]
- stdev_s2_zt, & ! Standard deviation of s for 2nd normal distribution [kg/kg]
- stdev_t1_zm, & ! Standard deviation of t for 1st normal distribution [kg/kg]
- stdev_t1_zt, & ! Standard deviation of t for 1st normal distribution [kg/kg]
- stdev_t2_zm, & ! Standard deviation of t for 2nd normal distribution [kg/kg]
- stdev_t2_zt, & ! Standard deviation of t for 2nd normal distribution [kg/kg]
- rrtthl_zm, & ! Within-a-normal correlation of r_t and th_l [-]
- rrtthl_zt, & ! Within-a-normal correlation of r_t and th_l [-]
- alpha_thl_zm, & ! Factor relating to normalized variance for th_l [-]
- alpha_thl_zt, & ! Factor relating to normalized variance for th_l [-]
- alpha_rt_zm, & ! Factor relating to normalized variance for r_t [-]
- alpha_rt_zt ! Factor relating to normalized variance for r_t [-]
-
- integer :: i
-
- !----------------------- Begin Code -----------------------------
-
- ! Store components of pdf_params in the locally declared variables
- ! We only apply the trapezoidal rule to these when
- ! l_apply_rule_to_pdf_params is true. This is because when we apply the
- ! rule to the final result of pdf_closure rather than the intermediate
- ! results it can lead to an inconsistency in how we determine which
- ! PDF component a point is in and whether the point is in or out of cloud,
- ! which is turn will break the latin hypercube code that samples
- ! preferentially in cloud. -dschanen 13 Feb 2012
-
- if ( l_apply_rule_to_pdf_params ) then
- w1_zt = pdf_params%w1
- w2_zt = pdf_params%w2
- varnce_w1_zt = pdf_params%varnce_w1
- varnce_w2_zt = pdf_params%varnce_w2
- rt1_zt = pdf_params%rt1
- rt2_zt = pdf_params%rt2
- varnce_rt1_zt = pdf_params%varnce_rt1
- varnce_rt2_zt = pdf_params%varnce_rt2
- crt1_zt = pdf_params%crt1
- crt2_zt = pdf_params%crt2
- cthl1_zt = pdf_params%cthl1
- cthl2_zt = pdf_params%cthl2
- thl1_zt = pdf_params%thl1
- thl2_zt = pdf_params%thl2
- varnce_thl1_zt = pdf_params%varnce_thl1
- varnce_thl2_zt = pdf_params%varnce_thl2
- mixt_frac_zt = pdf_params%mixt_frac
- rc1_zt = pdf_params%rc1
- rc2_zt = pdf_params%rc2
- rsl1_zt = pdf_params%rsl1
- rsl2_zt = pdf_params%rsl2
- cloud_frac1_zt = pdf_params%cloud_frac1
- cloud_frac2_zt = pdf_params%cloud_frac2
- s1_zt = pdf_params%s1
- s2_zt = pdf_params%s2
- stdev_s1_zt = pdf_params%stdev_s1
- stdev_s2_zt = pdf_params%stdev_s2
- stdev_t1_zt = pdf_params%stdev_t1
- stdev_t2_zt = pdf_params%stdev_t2
- rrtthl_zt = pdf_params%rrtthl
- alpha_thl_zt = pdf_params%alpha_thl
- alpha_rt_zt = pdf_params%alpha_rt
- end if
-
- ! If l_call_pdf_closure_twice is true, the _zm variables already have
- ! values from the second call to pdf_closure in advance_clubb_core.
- ! If it is false, the variables are interpolated to the _zm levels.
- if ( l_call_pdf_closure_twice ) then
-
- ! Store, in locally declared variables, the pdf_params output
- ! from the second call to pdf_closure
- if ( l_apply_rule_to_pdf_params ) then
- w1_zm = pdf_params_zm%w1
- w2_zm = pdf_params_zm%w2
- varnce_w1_zm = pdf_params_zm%varnce_w1
- varnce_w2_zm = pdf_params_zm%varnce_w2
- rt1_zm = pdf_params_zm%rt1
- rt2_zm = pdf_params_zm%rt2
- varnce_rt1_zm = pdf_params_zm%varnce_rt1
- varnce_rt2_zm = pdf_params_zm%varnce_rt2
- crt1_zm = pdf_params_zm%crt1
- crt2_zm = pdf_params_zm%crt2
- cthl1_zm = pdf_params_zm%cthl1
- cthl2_zm = pdf_params_zm%cthl2
- thl1_zm = pdf_params_zm%thl1
- thl2_zm = pdf_params_zm%thl2
- varnce_thl1_zm = pdf_params_zm%varnce_thl1
- varnce_thl2_zm = pdf_params_zm%varnce_thl2
- mixt_frac_zm = pdf_params_zm%mixt_frac
- rc1_zm = pdf_params_zm%rc1
- rc2_zm = pdf_params_zm%rc2
- rsl1_zm = pdf_params_zm%rsl1
- rsl2_zm = pdf_params_zm%rsl2
- cloud_frac1_zm = pdf_params_zm%cloud_frac1
- cloud_frac2_zm = pdf_params_zm%cloud_frac2
- s1_zm = pdf_params_zm%s1
- s2_zm = pdf_params_zm%s2
- stdev_s1_zm = pdf_params_zm%stdev_s1
- stdev_s2_zm = pdf_params_zm%stdev_s2
- stdev_t1_zm = pdf_params_zm%stdev_t1
- stdev_t2_zm = pdf_params_zm%stdev_t2
- rrtthl_zm = pdf_params_zm%rrtthl
- alpha_thl_zm = pdf_params_zm%alpha_thl
- alpha_rt_zm = pdf_params_zm%alpha_rt
- end if
-
- else
-
- ! Interpolate thermodynamic variables to the momentum grid.
- ! Since top momentum level is higher than top thermo. level,
- ! set variables at top momentum level to 0.
- wprtp2_zm = zt2zm( wprtp2 )
- wprtp2_zm(gr%nz) = 0.0_core_rknd
- wpthlp2_zm = zt2zm( wpthlp2 )
- wpthlp2_zm(gr%nz) = 0.0_core_rknd
- wprtpthlp_zm = zt2zm( wprtpthlp )
- wprtpthlp_zm(gr%nz) = 0.0_core_rknd
- cloud_frac_zm = zt2zm( cloud_frac )
- cloud_frac_zm(gr%nz) = 0.0_core_rknd
- ice_supersat_frac_zm = zt2zm( ice_supersat_frac )
- ice_supersat_frac_zm(gr%nz) = 0.0_core_rknd
- rcm_zm = zt2zm( rcm )
- rcm_zm(gr%nz) = 0.0_core_rknd
- wp2thvp_zm = zt2zm( wp2thvp )
- wp2thvp_zm(gr%nz) = 0.0_core_rknd
-
- do i = 1, sclr_dim
- wpsclrprtp_zm(:,i) = zt2zm( wpsclrprtp(:,i) )
- wpsclrprtp_zm(gr%nz,i) = 0.0_core_rknd
- wpsclrp2_zm(:,i) = zt2zm( wpsclrp2(:,i) )
- wpsclrp2_zm(gr%nz,i) = 0.0_core_rknd
- wpsclrpthlp_zm(:,i) = zt2zm( wpsclrpthlp(:,i) )
- wpsclrpthlp_zm(gr%nz,i) = 0.0_core_rknd
- end do ! i = 1, sclr_dim
-
- if ( l_apply_rule_to_pdf_params ) then
- w1_zm = zt2zm( pdf_params%w1 )
- w1_zm(gr%nz) = 0.0_core_rknd
- w2_zm = zt2zm( pdf_params%w2 )
- w2_zm(gr%nz) = 0.0_core_rknd
- varnce_w1_zm = zt2zm( pdf_params%varnce_w1 )
- varnce_w1_zm(gr%nz) = 0.0_core_rknd
- varnce_w2_zm = zt2zm( pdf_params%varnce_w2 )
- varnce_w2_zm(gr%nz) = 0.0_core_rknd
- rt1_zm = zt2zm( pdf_params%rt1 )
- rt1_zm(gr%nz) = 0.0_core_rknd
- rt2_zm = zt2zm( pdf_params%rt2 )
- rt2_zm(gr%nz) = 0.0_core_rknd
- varnce_rt1_zm = zt2zm( pdf_params%varnce_rt1 )
- varnce_rt1_zm(gr%nz) = 0.0_core_rknd
- varnce_rt2_zm = zt2zm( pdf_params%varnce_rt2 )
- varnce_rt2_zm(gr%nz) = 0.0_core_rknd
- crt1_zm = zt2zm( pdf_params%crt1 )
- crt1_zm(gr%nz) = 0.0_core_rknd
- crt2_zm = zt2zm( pdf_params%crt2 )
- crt2_zm(gr%nz) = 0.0_core_rknd
- cthl1_zm = zt2zm( pdf_params%cthl1 )
- cthl1_zm(gr%nz) = 0.0_core_rknd
- cthl2_zm = zt2zm( pdf_params%cthl2 )
- cthl2_zm(gr%nz) = 0.0_core_rknd
- thl1_zm = zt2zm( pdf_params%thl1 )
- thl1_zm(gr%nz) = 0.0_core_rknd
- thl2_zm = zt2zm( pdf_params%thl2 )
- thl2_zm(gr%nz) = 0.0_core_rknd
- varnce_thl1_zm = zt2zm( pdf_params%varnce_thl1 )
- varnce_thl1_zm(gr%nz) = 0.0_core_rknd
- varnce_thl2_zm = zt2zm( pdf_params%varnce_thl2 )
- varnce_thl2_zm(gr%nz) = 0.0_core_rknd
- mixt_frac_zm = zt2zm( pdf_params%mixt_frac )
- mixt_frac_zm(gr%nz) = 0.0_core_rknd
- rc1_zm = zt2zm( pdf_params%rc1 )
- rc1_zm(gr%nz) = 0.0_core_rknd
- rc2_zm = zt2zm( pdf_params%rc2 )
- rc2_zm(gr%nz) = 0.0_core_rknd
- rsl1_zm = zt2zm( pdf_params%rsl1 )
- rsl1_zm(gr%nz) = 0.0_core_rknd
- rsl2_zm = zt2zm( pdf_params%rsl2 )
- rsl2_zm(gr%nz) = 0.0_core_rknd
- cloud_frac1_zm = zt2zm( pdf_params%cloud_frac1 )
- cloud_frac1_zm(gr%nz) = 0.0_core_rknd
- cloud_frac2_zm = zt2zm( pdf_params%cloud_frac2 )
- cloud_frac2_zm(gr%nz) = 0.0_core_rknd
- s1_zm = zt2zm( pdf_params%s1 )
- s1_zm(gr%nz) = 0.0_core_rknd
- s2_zm = zt2zm( pdf_params%s2 )
- s2_zm(gr%nz) = 0.0_core_rknd
- stdev_s1_zm = zt2zm( pdf_params%stdev_s1 )
- stdev_s1_zm(gr%nz) = 0.0_core_rknd
- stdev_s2_zm = zt2zm( pdf_params%stdev_s2 )
- stdev_s2_zm(gr%nz) = 0.0_core_rknd
- stdev_t1_zm = zt2zm( pdf_params%stdev_t1 )
- stdev_t1_zm(gr%nz) = 0.0_core_rknd
- stdev_t2_zm = zt2zm( pdf_params%stdev_t2 )
- stdev_t2_zm(gr%nz) = 0.0_core_rknd
- rrtthl_zm = zt2zm( pdf_params%rrtthl )
- rrtthl_zm(gr%nz) = 0.0_core_rknd
- alpha_thl_zm = zt2zm( pdf_params%alpha_thl )
- alpha_thl_zm(gr%nz) = 0.0_core_rknd
- alpha_rt_zm = zt2zm( pdf_params%alpha_rt )
- alpha_rt_zm(gr%nz) = 0.0_core_rknd
- end if
- end if ! l_call_pdf_closure_twice
-
- if ( l_stats ) then
- ! Use the trapezoidal rule to recompute the variables on the zt level
- if ( iwprtp2 > 0 ) then
- wprtp2 = trapezoid_zt( wprtp2, wprtp2_zm )
- end if
- if ( iwpthlp2 > 0 ) then
- wpthlp2 = trapezoid_zt( wpthlp2, wpthlp2_zm )
- end if
- if ( iwprtpthlp > 0 ) then
- wprtpthlp = trapezoid_zt( wprtpthlp, wprtpthlp_zm )
- end if
-
- do i = 1, sclr_dim
- if ( iwpsclrprtp(i) > 0 ) then
- wpsclrprtp(:,i) = trapezoid_zt( wpsclrprtp(:,i), wpsclrprtp_zm(:,i) )
- end if
- if ( iwpsclrpthlp(i) > 0 ) then
- wpsclrpthlp(:,i) = trapezoid_zt( wpsclrpthlp(:,i), wpsclrpthlp_zm(:,i) )
- end if
- if ( iwpsclrp2(i) > 0 ) then
- wpsclrp2(:,i) = trapezoid_zt( wpsclrp2(:,i), wpsclrp2_zm(:,i) )
- end if
- end do ! i = 1, sclr_dim
- end if ! l_stats
-
- cloud_frac = trapezoid_zt( cloud_frac, cloud_frac_zm )
- ice_supersat_frac = trapezoid_zt( ice_supersat_frac, ice_supersat_frac_zm )
- rcm = trapezoid_zt( rcm, rcm_zm )
-
- wp2thvp = trapezoid_zt( wp2thvp, wp2thvp_zm )
-
- if ( l_apply_rule_to_pdf_params ) then
- pdf_params%w1 = trapezoid_zt( w1_zt, w1_zm )
- pdf_params%w2 = trapezoid_zt( w2_zt, w2_zm )
- pdf_params%varnce_w1 = trapezoid_zt( varnce_w1_zt, varnce_w1_zm )
- pdf_params%varnce_w2 = trapezoid_zt( varnce_w2_zt, varnce_w2_zm )
- pdf_params%rt1 = trapezoid_zt( rt1_zt, rt1_zm )
- pdf_params%rt2 = trapezoid_zt( rt2_zt, rt2_zm )
- pdf_params%varnce_rt1 = trapezoid_zt( varnce_rt1_zt, varnce_rt1_zm )
- pdf_params%varnce_rt2 = trapezoid_zt( varnce_rt2_zt, varnce_rt2_zm )
- pdf_params%crt1 = trapezoid_zt( crt1_zt, crt1_zm )
- pdf_params%crt2 = trapezoid_zt( crt2_zt, crt2_zm )
- pdf_params%cthl1 = trapezoid_zt( cthl1_zt, cthl1_zm )
- pdf_params%cthl2 = trapezoid_zt( cthl2_zt, cthl2_zm )
- pdf_params%thl1 = trapezoid_zt( thl1_zt, thl1_zm )
- pdf_params%thl2 = trapezoid_zt( thl2_zt, thl2_zm )
- pdf_params%varnce_thl1 = trapezoid_zt( varnce_thl1_zt, varnce_thl1_zm )
- pdf_params%varnce_thl2 = trapezoid_zt( varnce_thl2_zt, varnce_thl2_zm )
- pdf_params%mixt_frac = trapezoid_zt( mixt_frac_zt, mixt_frac_zm )
- pdf_params%rc1 = trapezoid_zt( rc1_zt, rc1_zm )
- pdf_params%rc2 = trapezoid_zt( rc2_zt, rc2_zm )
- pdf_params%rsl1 = trapezoid_zt( rsl1_zt, rsl1_zm )
- pdf_params%rsl2 = trapezoid_zt( rsl2_zt, rsl2_zm )
- pdf_params%cloud_frac1 = trapezoid_zt( cloud_frac1_zt, cloud_frac1_zm )
- pdf_params%cloud_frac2 = trapezoid_zt( cloud_frac2_zt, cloud_frac2_zm )
- pdf_params%s1 = trapezoid_zt( s1_zt, s1_zm )
- pdf_params%s2 = trapezoid_zt( s2_zt, s2_zm )
- pdf_params%rrtthl = trapezoid_zt( rrtthl_zt, rrtthl_zm )
- pdf_params%alpha_thl = trapezoid_zt( alpha_thl_zt, alpha_thl_zm )
- pdf_params%alpha_rt = trapezoid_zt( alpha_rt_zt, alpha_rt_zm )
- pdf_params%stdev_s1 = trapezoid_zt( stdev_s1_zt, stdev_s1_zm )
- pdf_params%stdev_s2 = trapezoid_zt( stdev_s2_zt, stdev_s2_zm )
- pdf_params%stdev_t1 = trapezoid_zt( stdev_t1_zt, stdev_t1_zm )
- pdf_params%stdev_t2 = trapezoid_zt( stdev_t2_zt, stdev_t2_zm )
- end if
-
- ! End of trapezoidal rule
-
- return
- end subroutine trapezoidal_rule_zt
-
- !-----------------------------------------------------------------------
- subroutine trapezoidal_rule_zm &
- ( wpthvp_zt, thlpthvp_zt, rtpthvp_zt, & ! intent(in)
- wpthvp, thlpthvp, rtpthvp ) ! intent(inout)
- !
- ! Description:
- ! This subroutine recomputes three variables on the
- ! momentum grid from pdf_closure -- wpthvp, thlpthvp, and
- ! rtpthvp -- by calling the function trapezoid_zm. Only these three
- ! variables are used in this subroutine because they are the only
- ! pdf_closure momentum variables used elsewhere in CLUBB.
- !
- ! The _zt variables are output from the first call to pdf_closure.
- ! The _zm variables are output from the second call to pdf_closure
- ! on the momentum levels.
- ! This is done before the call to this subroutine.
- !
- ! ldgrant Feb. 2010
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: gr ! Variable
-
- use crmx_clubb_precision, only: &
- core_rknd ! variable(s)
-
- implicit none
-
- ! Input variables
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- wpthvp_zt, & ! Buoyancy flux (on thermo. grid) [(K m)/s]
- thlpthvp_zt, & ! th_l' th_v' (on thermo. grid) [K^2]
- rtpthvp_zt ! r_t' th_v' (on thermo. grid) [(kg K)/kg]
-
- ! Input/Output variables
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- wpthvp, & ! Buoyancy flux [(K m)/s]
- thlpthvp, & ! th_l' th_v' [K^2]
- rtpthvp ! r_t' th_v' [(kg K)/kg]
-
- !----------------------- Begin Code -----------------------------
-
- ! Use the trapezoidal rule to recompute the variables on the zm level
- wpthvp = trapezoid_zm( wpthvp, wpthvp_zt )
- thlpthvp = trapezoid_zm( thlpthvp, thlpthvp_zt )
- rtpthvp = trapezoid_zm( rtpthvp, rtpthvp_zt )
-
- return
- end subroutine trapezoidal_rule_zm
-
- !-----------------------------------------------------------------------
- pure function trapezoid_zt( variable_zt, variable_zm )
- !
- ! Description:
- ! Function which uses the trapezoidal rule from calculus
- ! to recompute the values for the variables on the thermo. grid which
- ! are output from the first call to pdf_closure in module clubb_core.
- !
- ! ldgrant June 2009
- !--------------------------------------------------------------------
-
- use crmx_grid_class, only: gr ! Variable
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- variable_zt, & ! Variable on the zt grid
- variable_zm ! Variable on the zm grid
-
- ! Result
- real( kind = core_rknd ), dimension(gr%nz) :: trapezoid_zt
-
- ! Local Variable
- integer :: k ! Loop index
-
- !------------ Begin Code --------------
-
- ! Boundary condition: trapezoidal rule not valid at zt level 1
- trapezoid_zt(1) = variable_zt(1)
-
- do k = 2, gr%nz
- ! Trapezoidal rule from calculus
- trapezoid_zt(k) = 0.5_core_rknd * ( variable_zm(k) + variable_zt(k) ) &
- * ( gr%zm(k) - gr%zt(k) ) * gr%invrs_dzt(k) &
- + 0.5_core_rknd * ( variable_zt(k) + variable_zm(k-1) ) &
- * ( gr%zt(k) - gr%zm(k-1) ) * gr%invrs_dzt(k)
- end do ! k = 2, gr%nz
-
- return
- end function trapezoid_zt
-
- !-----------------------------------------------------------------------
- pure function trapezoid_zm( variable_zm, variable_zt )
- !
- ! Description:
- ! Function which uses the trapezoidal rule from calculus
- ! to recompute the values for the important variables on the momentum
- ! grid which are output from pdf_closure in module clubb_core.
- ! These momentum variables only include wpthvp, thlpthvp, and rtpthvp.
- !
- ! ldgrant Feb. 2010
- !--------------------------------------------------------------------
-
- use crmx_grid_class, only: gr ! Variable
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- variable_zm, & ! Variable on the zm grid
- variable_zt ! Variable on the zt grid
-
- ! Result
- real( kind = core_rknd ), dimension(gr%nz) :: trapezoid_zm
-
- ! Local Variable
- integer :: k ! Loop index
-
- !------------ Begin Code --------------
-
- ! Boundary conditions: trapezoidal rule not valid at top zm level, nzmax.
- ! Trapezoidal rule also not used at zm level 1.
- trapezoid_zm(1) = variable_zm(1)
- trapezoid_zm(gr%nz) = variable_zm(gr%nz)
-
- do k = 2, gr%nz-1
- ! Trapezoidal rule from calculus
- trapezoid_zm(k) = 0.5_core_rknd * ( variable_zt(k+1) + variable_zm(k) ) &
- * ( gr%zt(k+1) - gr%zm(k) ) * gr%invrs_dzm(k) &
- + 0.5_core_rknd * ( variable_zm(k) + variable_zt(k) ) &
- * ( gr%zm(k) - gr%zt(k) ) * gr%invrs_dzm(k)
- end do ! k = 2, gr%nz-1
-
- return
- end function trapezoid_zm
-
- !-----------------------------------------------------------------------
- subroutine compute_cloud_cover &
- ( pdf_params, cloud_frac, rcm, & ! intent(in)
- cloud_cover, rcm_in_layer ) ! intent(out)
- !
- ! Description:
- ! Subroutine to compute cloud cover (the amount of sky
- ! covered by cloud) and rcm in layer (liquid water mixing ratio in
- ! the portion of the grid box filled by cloud).
- !
- ! References:
- ! Definition of 's' comes from:
- ! ``The Gaussian Cloud Model Relations'' G. L. Mellor (1977)
- ! JAS, Vol. 34, pp. 356--358.
- !
- ! Notes:
- ! Added July 2009
- !---------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- rc_tol, & ! Variable(s)
- fstderr
-
- use crmx_grid_class, only: gr ! Variable
-
- use crmx_pdf_parameter_module, only: &
- pdf_parameter ! Derived data type
-
- use crmx_error_code, only: &
- clubb_at_least_debug_level ! Procedure
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External functions
- intrinsic :: abs, min, max
-
- ! Input variables
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- cloud_frac, & ! Cloud fraction [-]
- rcm ! Liquid water mixing ratio [kg/kg]
-
- type (pdf_parameter), dimension(gr%nz), intent(in) :: &
- pdf_params ! PDF Parameters [units vary]
-
- ! Output variables
- real( kind = core_rknd ), dimension(gr%nz), intent(out) :: &
- cloud_cover, & ! Cloud cover [-]
- rcm_in_layer ! Liquid water mixing ratio in cloud layer [kg/kg]
-
- ! Local variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- s_mean, & ! Mean extended cloud water mixing ratio of the
- ! two Gaussian distributions
- vert_cloud_frac_upper, & ! Fraction of cloud in top half of grid box
- vert_cloud_frac_lower, & ! Fraction of cloud in bottom half of grid box
- vert_cloud_frac ! Fraction of cloud filling the grid box in the vertical
-
- integer :: k
-
- ! ------------ Begin code ---------------
-
- do k = 1, gr%nz
-
- s_mean(k) = pdf_params(k)%mixt_frac * pdf_params(k)%s1 + &
- (1.0_core_rknd-pdf_params(k)%mixt_frac) * pdf_params(k)%s2
-
- end do
-
- do k = 2, gr%nz-1, 1
-
- if ( rcm(k) < rc_tol ) then ! No cloud at this level
-
- cloud_cover(k) = cloud_frac(k)
- rcm_in_layer(k) = rcm(k)
-
- else if ( ( rcm(k+1) >= rc_tol ) .and. ( rcm(k-1) >= rc_tol ) ) then
- ! There is cloud above and below,
- ! so assume cloud fills grid box from top to bottom
-
- cloud_cover(k) = cloud_frac(k)
- rcm_in_layer(k) = rcm(k)
-
- else if ( ( rcm(k+1) < rc_tol ) .or. ( rcm(k-1) < rc_tol) ) then
- ! Cloud may fail to reach gridbox top or base or both
-
- ! First let the cloud fill the entire grid box, then overwrite
- ! vert_cloud_frac_upper(k) and/or vert_cloud_frac_lower(k)
- ! for a cloud top, cloud base, or one-point cloud.
- vert_cloud_frac_upper(k) = 0.5_core_rknd
- vert_cloud_frac_lower(k) = 0.5_core_rknd
-
- if ( rcm(k+1) < rc_tol ) then ! Cloud top
-
- vert_cloud_frac_upper(k) = &
- ( ( 0.5_core_rknd / gr%invrs_dzm(k) ) / ( gr%zm(k) - gr%zt(k) ) ) &
- * ( rcm(k) / ( rcm(k) + abs( s_mean(k+1) ) ) )
-
- vert_cloud_frac_upper(k) = min( 0.5_core_rknd, vert_cloud_frac_upper(k) )
-
- ! Make the transition in cloudiness more gradual than using
- ! the above min statement alone.
- vert_cloud_frac_upper(k) = vert_cloud_frac_upper(k) + &
- ( ( rcm(k+1)/rc_tol )*( 0.5_core_rknd -vert_cloud_frac_upper(k) ) )
-
- else
-
- vert_cloud_frac_upper(k) = 0.5_core_rknd
-
- end if
-
- if ( rcm(k-1) < rc_tol ) then ! Cloud base
-
- vert_cloud_frac_lower(k) = &
- ( ( 0.5_core_rknd / gr%invrs_dzm(k-1) ) / ( gr%zt(k) - gr%zm(k-1) ) ) &
- * ( rcm(k) / ( rcm(k) + abs( s_mean(k-1) ) ) )
-
- vert_cloud_frac_lower(k) = min( 0.5_core_rknd, vert_cloud_frac_lower(k) )
-
- ! Make the transition in cloudiness more gradual than using
- ! the above min statement alone.
- vert_cloud_frac_lower(k) = vert_cloud_frac_lower(k) + &
- ( ( rcm(k-1)/rc_tol )*( 0.5_core_rknd -vert_cloud_frac_lower(k) ) )
-
- else
-
- vert_cloud_frac_lower(k) = 0.5_core_rknd
-
- end if
-
- vert_cloud_frac(k) = &
- vert_cloud_frac_upper(k) + vert_cloud_frac_lower(k)
-
- vert_cloud_frac(k) = &
- max( cloud_frac(k), min( 1.0_core_rknd, vert_cloud_frac(k) ) )
-
- cloud_cover(k) = cloud_frac(k) / vert_cloud_frac(k)
- rcm_in_layer(k) = rcm(k) / vert_cloud_frac(k)
-
- else
-
- if ( clubb_at_least_debug_level( 1 ) ) then
-
- write(fstderr,*) &
- "Error: Should not arrive here in computation of cloud_cover"
-
- write(fstderr,*) "At grid level k = ", k
- write(fstderr,*) "pdf_params(k)%mixt_frac = ", pdf_params(k)%mixt_frac
- write(fstderr,*) "pdf_params(k)%s1 = ", pdf_params(k)%s1
- write(fstderr,*) "pdf_params(k)%s2 = ", pdf_params(k)%s2
- write(fstderr,*) "cloud_frac(k) = ", cloud_frac(k)
- write(fstderr,*) "rcm(k) = ", rcm(k)
- write(fstderr,*) "rcm(k+1) = ", rcm(k+1)
- write(fstderr,*) "rcm(k-1) = ", rcm(k-1)
-
- end if
-
- return
-
- end if ! rcm(k) < rc_tol
-
- end do ! k = 2, gr%nz-1, 1
-
- cloud_cover(1) = cloud_frac(1)
- cloud_cover(gr%nz) = cloud_frac(gr%nz)
-
- rcm_in_layer(1) = rcm(1)
- rcm_in_layer(gr%nz) = rcm(gr%nz)
-
- return
- end subroutine compute_cloud_cover
- !-----------------------------------------------------------------------
- subroutine clip_rcm &
- ( rtm, message, & ! intent(in)
- rcm ) ! intent(inout)
- !
- ! Description:
- ! Subroutine that reduces cloud water (rcm) whenever
- ! it exceeds total water (rtm = vapor + liquid).
- ! This avoids negative values of rvm = water vapor mixing ratio.
- ! However, it will not ensure that rcm <= rtm if rtm <= 0.
- !
- ! References:
- ! None
- !---------------------------------------------------------------------
-
-
- use crmx_grid_class, only: gr ! Variable
-
- use crmx_error_code, only : &
- clubb_at_least_debug_level ! Procedure(s)
-
- use crmx_constants_clubb, only: &
- fstderr, & ! Variable(s)
- zero_threshold
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External functions
- intrinsic :: max, epsilon
-
- ! Input variables
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- rtm ! Total water mixing ratio [kg/kg]
-
- character(len= * ), intent(in) :: message
-
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- rcm ! Cloud water mixing ratio [kg/kg]
-
- integer :: k
-
- ! ------------ Begin code ---------------
-
- ! Vince Larson clipped rcm in order to prevent rvm < 0. 5 Apr 2008.
- ! This code won't work unless rtm >= 0 !!!
- ! We do not clip rcm_in_layer because rcm_in_layer only influences
- ! radiation, and we do not want to bother recomputing it. 6 Aug 2009
- do k = 1, gr%nz
- if ( rtm(k) < rcm(k) ) then
-
- if ( clubb_at_least_debug_level(1) ) then
- write(fstderr,*) message, ' at k=', k, 'rcm(k) = ', rcm(k), &
- 'rtm(k) = ', rtm(k), '.', ' Clipping rcm.'
-
- end if ! clubb_at_least_debug_level(1)
-
- rcm(k) = max( zero_threshold, rtm(k) - epsilon( rtm(k) ) )
-
- end if ! rtm(k) < rcm(k)
-
- end do ! k=1..gr%nz
-
- return
- end subroutine clip_rcm
-
- !-----------------------------------------------------------------------------
- subroutine set_Lscale_max( l_implemented, host_dx, host_dy, &
- Lscale_max )
-
- ! Description:
- ! This subroutine sets the value of Lscale_max, which is the maximum
- ! allowable value of Lscale. For standard CLUBB, it is set to a very large
- ! value so that Lscale will not be limited. However, when CLUBB is running
- ! as part of a host model, the value of Lscale_max is dependent on the size
- ! of the host model's horizontal grid spacing. The smaller the host model's
- ! horizontal grid spacing, the smaller the value of Lscale_max. When Lscale
- ! is limited to a small value, the value of time-scale Tau is reduced, which
- ! in turn produces greater damping on CLUBB's turbulent parameters. This
- ! is the desired effect on turbulent parameters for a host model with small
- ! horizontal grid spacing, for small areas usually contain much less
- ! variation in meteorological quantities than large areas.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- logical, intent(in) :: &
- l_implemented ! Flag to see if CLUBB is running on it's own,
- ! or if it's implemented as part of a host model.
-
- real( kind = core_rknd ), intent(in) :: &
- host_dx, & ! Host model's east-west horizontal grid spacing [m]
- host_dy ! Host model's north-south horizontal grid spacing [m]
-
- ! Output Variable
- real( kind = core_rknd ), intent(out) :: &
- Lscale_max ! Maximum allowable value for Lscale [m]
-
- ! ---- Begin Code ----
-
- ! Determine the maximum allowable value for Lscale (in meters).
- if ( l_implemented ) then
- Lscale_max = 0.25_core_rknd * min( host_dx, host_dy )
- else
- Lscale_max = 1.0e5_core_rknd
- end if
-
- return
- end subroutine set_Lscale_max
-
-!===============================================================================
-
- end module crmx_clubb_core
-! vim: set expandtab tabstop=2 shiftwidth=2 textwidth=100 autoindent:
diff --git a/src/physics/spcam/crm/CLUBB/crmx_clubb_precision.F90 b/src/physics/spcam/crm/CLUBB/crmx_clubb_precision.F90
deleted file mode 100644
index b594d17061..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_clubb_precision.F90
+++ /dev/null
@@ -1,24 +0,0 @@
-!-------------------------------------------------------------------------------
-! $Id: clubb_precision.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-module crmx_clubb_precision
-
- implicit none
-
- public :: stat_nknd, stat_rknd, time_precision, dp, sp, core_rknd
-
- private ! Default scope
-
- ! The precisions below are arbitrary, and could be adjusted as
- ! needed for long simulations or time averaging. Note that on
- ! most machines 12 digits of precision will use a data type
- ! which is 8 bytes long.
- integer, parameter :: &
- stat_nknd = selected_int_kind( 8 ), &
- stat_rknd = selected_real_kind( p=12 ), &
- time_precision = selected_real_kind( p=12 ), &
- dp = selected_real_kind( p=12 ), & ! double precision
- sp = selected_real_kind( p=5 ), & ! single precision
- core_rknd = CLUBB_REAL_TYPE ! Value from the preprocessor directive
-
-end module crmx_clubb_precision
-!-------------------------------------------------------------------------------
diff --git a/src/physics/spcam/crm/CLUBB/crmx_constants_clubb.F90 b/src/physics/spcam/crm/CLUBB/crmx_constants_clubb.F90
deleted file mode 100644
index a6108f6419..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_constants_clubb.F90
+++ /dev/null
@@ -1,375 +0,0 @@
-!-----------------------------------------------------------------------------
-! $Id: constants_clubb.F90 6132 2013-03-28 13:09:40Z vlarson@uwm.edu $
-!=============================================================================
-module crmx_constants_clubb
-
- ! Description:
- ! Contains frequently occuring model constants
-
- ! References:
- ! None
- !---------------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- time_precision, & ! Variable(s)
- dp, &
- core_rknd
-
-!#ifdef CLUBB_CAM /* Set constants as they're set in CAM */
-#if defined(CLUBB_CAM) || defined(CRM) /* set constants as they' are set in CAM for CLUBB_CAM or MMF */
- use shr_const_mod, only: shr_const_rdair, shr_const_cpdair, shr_const_latvap, &
- shr_const_latice, shr_const_latsub, shr_const_rgas, &
- shr_const_mwwv, shr_const_stebol, shr_const_tkfrz, &
- shr_const_mwdair, shr_const_g, shr_const_karman, &
- shr_const_rhofw
-#elif GFDL
- ! use GFDL constants, and then rename them to avoid confusion in case
- ! that the constants share the same names between GFDL and CLUBB
- use constants_mod, only: pi_gfdl => PI, &
- radians_per_deg_dp_gfdl => DEG_TO_RAD, &
- Cp_gfdl => CP_AIR, &
- Lv_gfdl => HLV, &
- Ls_gfdl => HLS, &
- Lf_gfdl => HLF, &
- Rd_gfdl => RDGAS, &
- Rv_gfdl => RVGAS, &
- stefan_boltzmann_gfdl => STEFAN, &
- T_freeze_K_gfdl => TFREEZE, &
- grav_gfdl => GRAV, &
- vonk_gfdl => VONKARM, &
- rho_lw_gfdl => DENS_H2O
-#endif
-
- implicit none
-
- private ! Default scope
-
- !-----------------------------------------------------------------------------
- ! Numerical/Arbitrary Constants
- !-----------------------------------------------------------------------------
-
- ! Fortran file unit I/O constants
- integer, parameter, public :: &
- fstderr = 0, fstdin = 5, fstdout = 6
-
- ! Maximum variable name length in CLUBB GrADS or netCDF output
- integer, parameter, public :: &
- var_length = 30
- ! The parameter parab_cyl_max_input is the largest magnitude that the input to
- ! the parabolic cylinder function is allowed to have. When the value of the
- ! input to the parabolic cylinder function is too large in magnitude
- ! (depending on the order of the parabolic cylinder function), overflow
- ! occurs, and the output of the parabolic cylinder function is +/-Inf. The
- ! parameter parab_cyl_max_input places a limit on the absolute value of the
- ! input to the parabolic cylinder function. When the value of the potential
- ! input exceeds this parameter (usually due to a very large ratio of ith PDF
- ! component mean of x to ith PDF component standard deviation of x), the
- ! variable x is considered to be constant and a different version of the
- ! equation called.
- !
- ! The largest allowable magnitude of the input to the parabolic cylinder
- ! function (before overflow occurs) is dependent on the order of parabolic
- ! cylinder function. However, after a lot of testing, it was determined that
- ! an absolute value of 49 works well for an order of 12 or less.
- real( kind = core_rknd ), parameter, public :: &
- parab_cyl_max_input = 49.0_core_rknd ! Largest allowable input to parab. cyl. fnct.
-
- ! "Over-implicit" weighted time step.
- !
- ! The weight of the implicit portion of a term is controlled by the factor
- ! gamma_over_implicit_ts (abbreviated "gamma" in the expression below). A
- ! factor is added to the right-hand side of the equation in order to balance a
- ! weight that is not equal to 1, such that:
- !
- ! -y(t) * [ gamma * X(t+1) + ( 1 - gamma ) * X(t) ] + RHS;
- !
- ! where X is the variable that is being solved for in a predictive equation
- ! (such as w'^3, w'th_l', r_t'^2, etc), y(t) is the linearized portion of the
- ! term that gets treated implicitly, and RHS is the portion of the term that
- ! is always treated explicitly. A weight of greater than 1 can be applied to
- ! make the term more numerically stable.
- !
- ! gamma_over_implicit_ts Effect on term
- !
- ! 0.0 Term becomes completely explicit
- !
- ! 1.0 Standard implicit portion of the term;
- ! as it was without the weighting factor.
- !
- ! 1.5 Strongly weighted implicit portion of the term;
- ! increased numerical stability.
- !
- ! 2.0 More strongly weighted implicit portion of the
- ! term; increased numerical stability.
- !
- ! Note: The "over-implicit" weighted time step is only applied to terms that
- ! tend to significantly decrease the amount of numerical stability for
- ! variable X.
- ! The "over-implicit" weighted time step is applied to the turbulent
- ! advection term for the following variables:
- ! w'^3 (also applied to the turbulent production term), found in
- ! module advance_wp2_wp3_module;
- ! w'r_t', w'th_l', and w'sclr', found in
- ! module advance_xm_wpxp_module; and
- ! r_t'^2, th_l'^2, r_t'th_l', u'^2, v'^2, sclr'^2, sclr'r_t',
- ! and sclr'th_l', found in module advance_xp2_xpyp_module.
- real( kind = core_rknd ), parameter, public :: &
- gamma_over_implicit_ts = 1.50_core_rknd
-
- !-----------------------------------------------------------------------------
- ! Mathematical Constants
- !-----------------------------------------------------------------------------
- real( kind = dp ), parameter, public :: &
- pi_dp = 3.14159265358979323846_dp
-
-#ifdef GFDL
- real( kind = core_rknd ), parameter, public :: &
- pi = pi_gfdl ! The ratio of radii to their circumference
-
- real( kind = dp ), parameter, public :: &
- radians_per_deg_dp = radians_per_deg_dp_gfdl
-#else
-
- real( kind = core_rknd ), parameter, public :: &
- pi = 3.141592654_core_rknd ! The ratio of radii to their circumference
-
- real( kind = dp ), parameter, public :: &
- radians_per_deg_dp = pi_dp / 180._dp
-#endif
-
- real( kind = core_rknd ), parameter, public :: &
- sqrt_2pi = 2.5066282746310005024_core_rknd, & ! sqrt(2*pi)
- sqrt_2 = 1.4142135623730950488_core_rknd ! sqrt(2)
-
- real( kind = dp ), parameter, public:: &
- two_dp = 2.0_dp, & ! 2
- one_dp = 1.0_dp, & ! 1
- one_half_dp = 0.5_dp, & ! 1/2
- one_fourth_dp = 0.25_dp, & ! 1/4
- zero_dp = 0.0_dp ! 0
-
- real( kind = core_rknd ), parameter, public :: &
- one_hundred = 100.0_core_rknd, & ! 100
- fifty = 50.0_core_rknd, & ! 50
- twenty = 20.0_core_rknd, & ! 20
- ten = 10.0_core_rknd, & ! 10
- five = 5.0_core_rknd, & ! 5
- four = 4.0_core_rknd, & ! 4
- three = 3.0_core_rknd, & ! 3
- two = 2.0_core_rknd, & ! 2
- three_halves = 3.0_core_rknd/2.0_core_rknd, & ! 3/2
- four_thirds = 4.0_core_rknd/3.0_core_rknd, & ! 4/3
- one = 1.0_core_rknd, & ! 1
- three_fourths = 0.75_core_rknd, & ! 3/4
- two_thirds = 2.0_core_rknd/3.0_core_rknd, & ! 2/3
- one_half = 0.5_core_rknd, & ! 1/2
- one_third = 1.0_core_rknd/3.0_core_rknd, & ! 1/3
- one_fourth = 0.25_core_rknd, & ! 1/4
- zero = 0.0_core_rknd ! 0
-
- !-----------------------------------------------------------------------------
- ! Physical constants
- !-----------------------------------------------------------------------------
-
-!#ifdef CLUBB_CAM
-#if defined(CLUBB_CAM) || defined(CRM) /* set constants as they' are set in CAM for CLUBB_CAM or MMF */
-
- real( kind = core_rknd ), parameter, public :: &
- Cp = shr_const_cpdair, & ! Dry air specific heat at constant p [J/kg/K]
- Lv = shr_const_latvap, & ! Latent heat of vaporization [J/kg]
- Lf = shr_const_latice, & ! Latent heat of fusion [J/kg]
- Ls = shr_const_latsub, & ! Latent heat of sublimation [J/kg]
- Rd = shr_const_rdair, & ! Dry air gas constant [J/kg/K]
- Rv = shr_const_rgas/shr_const_mwwv ! Water vapor gas constant [J/kg/K]
-
- real( kind = core_rknd ), parameter, public :: &
- stefan_boltzmann = shr_const_stebol ! Stefan-Boltzmann constant [W/(m^2 K^4)]
-
- real( kind = core_rknd ), parameter, public :: &
- T_freeze_K = shr_const_tkfrz ! Freezing point of water [K]
-
- ! Useful combinations of Rd and Rv
- real( kind = core_rknd ), parameter, public :: &
- ep = shr_const_mwwv/shr_const_mwdair, & ! ep = 0.622 [-]
- ep1 = (1.0-ep)/ep,& ! ep1 = 0.61 [-]
- ep2 = 1.0/ep ! ep2 = 1.61 [-]
-
- real( kind = core_rknd ), parameter, public :: &
- kappa = (shr_const_rgas/shr_const_mwdair)/shr_const_cpdair ! kappa [-]
-
- real( kind = core_rknd ), parameter, public :: &
- grav = shr_const_g, & ! Gravitational acceleration [m/s^2]
- p0 = 1.0e5 ! Reference pressure [Pa]
-
- ! Von Karman's constant
- ! Constant of the logarithmic wind profile in the surface layer
- real( kind = core_rknd ), parameter, public :: &
- vonk = shr_const_karman, & ! Accepted value is 0.40 (+/-) 0.01 [-]
- rho_lw = shr_const_rhofw ! Density of liquid water [kg/m^3]
-
-
-#elif GFDL
- real( kind = core_rknd ), parameter, public :: &
- Cp = Cp_gfdl, & ! Dry air specific heat at constant p [J/kg/K]
- Lv = Lv_gfdl, & ! Latent heat of vaporization [J/kg]
- Ls = Ls_gfdl, & ! Latent heat of sublimation [J/kg]
- Lf = Lf_gfdl, & ! Latent heat of fusion [J/kg]
- Rd = Rd_gfdl, & ! Dry air gas constant [J/kg/K]
- Rv = Rv_gfdl ! Water vapor gas constant [J/kg/K]
-
-
- real( kind = core_rknd ), parameter, public :: &
- stefan_boltzmann = stefan_boltzmann_gfdl ! Stefan-Boltzmann constant [W/(m^2 K^4)]
-
- real( kind = core_rknd ), parameter, public :: &
- T_freeze_K = T_freeze_K_gfdl ! Freezing point of water [K]
-
- ! Useful combinations of Rd and Rv
- real( kind = core_rknd ), parameter, public :: &
- ep = Rd / Rv, & ! ep = 0.622 [-]
- ep1 = (1.0-ep)/ep,& ! ep1 = 0.61 [-]
- ep2 = 1.0/ep ! ep2 = 1.61 [-]
-
- real( kind = core_rknd ), parameter, public :: &
- kappa = Rd / Cp ! kappa [-]
-
- ! Changed g to grav to make it easier to find in the code 5/25/05
- ! real, parameter :: grav = 9.80665 ! Gravitational acceleration [m/s^2]
- real( kind = core_rknd ), parameter, public :: &
- grav = grav_gfdl, & ! Gravitational acceleration [m/s^2]
- p0 = 1.0e5 ! Reference pressure [Pa]
-
- ! Von Karman's constant
- ! Constant of the logarithmic wind profile in the surface layer
- real( kind = core_rknd ), parameter, public :: &
- vonk = vonk_gfdl, & ! Accepted value is 0.40 (+/-) 0.01 [-]
- rho_lw = rho_lw_gfdl ! Density of liquid water [kg/m^3]
-
-
-#else
-
- real( kind = core_rknd ), parameter, public :: &
- Cp = 1004.67_core_rknd, & ! Dry air specific heat at constant p [J/kg/K]
- Lv = 2.5e6_core_rknd, & ! Latent heat of vaporization [J/kg]
- Ls = 2.834e6_core_rknd, & ! Latent heat of sublimation [J/kg]
- Lf = 3.33e5_core_rknd, & ! Latent heat of fusion [J/kg]
- Rd = 287.04_core_rknd, & ! Dry air gas constant [J/kg/K]
- Rv = 461.5_core_rknd ! Water vapor gas constant [J/kg/K]
-
-
- real( kind = core_rknd ), parameter, public :: &
- stefan_boltzmann = 5.6704e-8_core_rknd ! Stefan-Boltzmann constant [W/(m^2 K^4)]
-
- real( kind = core_rknd ), parameter, public :: &
- T_freeze_K = 273.15_core_rknd ! Freezing point of water [K]
-
- ! Useful combinations of Rd and Rv
- real( kind = core_rknd ), parameter, public :: &
- ep = Rd / Rv, & ! ep = 0.622_core_rknd [-]
- ep1 = (1.0_core_rknd-ep)/ep,& ! ep1 = 0.61_core_rknd [-]
- ep2 = 1.0_core_rknd/ep ! ep2 = 1.61_core_rknd [-]
-
- real( kind = core_rknd ), parameter, public :: &
- kappa = Rd / Cp ! kappa [-]
-
- ! Changed g to grav to make it easier to find in the code 5/25/05
- ! real, parameter, public :: grav = 9.80665_core_rknd ! Gravitational acceleration [m/s^2]
- real( kind = core_rknd ), parameter, public :: &
- grav = 9.81_core_rknd, & ! Gravitational acceleration [m/s^2]
- p0 = 1.0e5_core_rknd ! Reference pressure [Pa]
-
- ! Von Karman's constant
- ! Constant of the logarithmic wind profile in the surface layer
- real( kind = core_rknd ), parameter, public :: &
- vonk = 0.4_core_rknd, & ! Accepted value is 0.40 (+/-) 0.01 [-]
- rho_lw = 1000.0_core_rknd ! Density of liquid water [kg/m^3]
-
-#endif
-
- ! Tolerances below which we consider moments to be zero
- real( kind = core_rknd ), parameter, public :: &
- w_tol = 2.e-2_core_rknd, & ! [m/s]
- thl_tol = 1.e-2_core_rknd, & ! [K]
- rt_tol = 1.e-8_core_rknd, & ! [kg/kg]
- s_mellor_tol = 1.e-8_core_rknd, & ! [kg/kg]
- t_mellor_tol = s_mellor_tol ! [kg/kg]
-
- ! Tolerances for use by the monatonic flux limiter.
- ! rt_tol_mfl is larger than rt_tol. rt_tol is extremely small
- ! (1e-8) to prevent spurious cloud formation aloft in LBA.
- ! rt_tol_mfl is larger (1e-4) to prevent the mfl from
- ! depositing moisture at the top of the domain.
- real( kind = core_rknd ), parameter, public :: &
- thl_tol_mfl = 1.e-2_core_rknd, & ! [K]
- rt_tol_mfl = 1.e-4_core_rknd ! [kg/kg]
-
- ! The tolerance for w'^2 is the square of the tolerance for w.
- real( kind = core_rknd ), parameter, public :: &
- w_tol_sqd = w_tol**2 ! [m^2/s^2]
-
- real( kind = core_rknd ), parameter, public :: &
- Skw_max_mag = 4.5_core_rknd ! Max magnitude of skewness [-]
-
- real( kind = core_rknd ), parameter, public :: &
- Skw_max_mag_sqd = Skw_max_mag**2 ! Max mag. of Skw squared [-]
-
- ! Set tolerances for Khairoutdinov and Kogan rain microphysics to insure
- ! against numerical errors. The tolerance values for Nc, rr, and Nr insure
- ! against underflow errors in computing the PDF for l_kk_rain. Basically,
- ! they insure that those values squared won't be less then 10^-38, which is
- ! the lowest number that can be numerically represented. However, the
- ! tolerance value for rc doubles as the lowest mixing ratio there can be to
- ! still officially have a cloud at that level. This is figured to be about
- ! 1.0_core_rknd x 10^-7 kg/kg. Brian; February 10, 2007.
- real( kind = core_rknd ), parameter, public :: &
- rc_tol = 1.0E-6_core_rknd, & ! [kg/kg]
- Nc_tol = 1.0E-10_core_rknd, & ! [#/kg]
- rr_tol = 1.0E-10_core_rknd, & ! [kg/kg]
- Nr_tol = 1.0E-10_core_rknd ! [#/kg]
-
- ! Minimum value for em (turbulence kinetic energy)
- ! If anisotropic TKE is enabled, em = (1/2) * ( up2 + vp2 + wp2 );
- ! otherwise, em = (3/2) * wp2. Since up2, vp2, and wp2 all have
- ! the same minimum threshold value of w_tol_sqd, em cannot be less
- ! than (3/2) * w_tol_sqd. Thus, em_min = (3/2) * w_tol_sqd.
- real( kind = core_rknd ), parameter, public :: em_min = 1.5_core_rknd * w_tol_sqd ! [m^2/s^2]
-
- real( kind = core_rknd ), parameter, public :: &
- eps = 1.0e-10_core_rknd ! Small value to prevent a divide by zero
-
- real( kind = core_rknd ), parameter, public :: &
- zero_threshold = 0.0_core_rknd ! Defining a threshold on a physical quantity to be 0.
-
- ! The maximum absolute value (or magnitude) that a correlation is allowed to
- ! have. Statistically, a correlation is not allowed to be less than -1 or
- ! greater than 1, so the maximum magnitude would be 1.
- real( kind = core_rknd ), parameter, public :: &
- max_mag_correlation = 0.99_core_rknd
-
- real( kind = core_rknd ), parameter, public :: &
- cloud_frac_min = 0.005_core_rknd ! Threshold for cloud fractions
-
- !-----------------------------------------------------------------------------
- ! Useful conversion factors.
- !-----------------------------------------------------------------------------
- real(kind=time_precision), parameter, public :: &
- sec_per_day = 86400.0_time_precision, & ! Seconds in a day.
- sec_per_hr = 3600.0_time_precision, & ! Seconds in an hour.
- sec_per_min = 60.0_time_precision, & ! Seconds in a minute.
- min_per_hr = 60.0_time_precision ! Minutes in an hour.
-
- real( kind = core_rknd ), parameter, public :: &
- g_per_kg = 1000.0_core_rknd ! Grams in a kilogram.
-
- real( kind = core_rknd ), parameter, public :: &
- pascal_per_mb = 100.0_core_rknd ! Pascals per Millibar
-
- real( kind = core_rknd ), parameter, public :: &
- cm3_per_m3 = 1.e6_core_rknd, & ! Cubic centimeters per cubic meter
- micron_per_m = 1.e6_core_rknd, & ! Micrometers per meter
- cm_per_m = 100._core_rknd, & ! Centimeters per meter
- mm_per_m = 1000._core_rknd ! Millimeters per meter
-
-!=============================================================================
-
-end module crmx_constants_clubb
diff --git a/src/physics/spcam/crm/CLUBB/crmx_corr_matrix_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_corr_matrix_module.F90
deleted file mode 100644
index 1a9eaafd0b..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_corr_matrix_module.F90
+++ /dev/null
@@ -1,181 +0,0 @@
-!$Id: corr_matrix_module.F90 5997 2012-12-18 20:47:09Z raut@uwm.edu $
-!---------------------------------------------------------------------------------------------------
-module crmx_corr_matrix_module
-
- implicit none
-
- ! Latin hypercube indices / Correlation array indices
- integer, public :: &
- iiLH_s_mellor = -1, &
- iiLH_t_mellor = -1, &
- iiLH_w = -1
-!$omp threadprivate(iiLH_s_mellor, iiLH_t_mellor, iiLH_w)
-
- integer, public :: &
- iiLH_rrain = -1, &
- iiLH_rsnow = -1, &
- iiLH_rice = -1, &
- iiLH_rgraupel = -1
-!$omp threadprivate(iiLH_rrain, iiLH_rsnow, iiLH_rice, iiLH_rgraupel)
-
- integer, public :: &
- iiLH_Nr = -1, &
- iiLH_Nsnow = -1, &
- iiLH_Ni = -1, &
- iiLH_Ngraupel = -1, &
- iiLH_Nc = -1
-!$omp threadprivate(iiLH_Nr, iiLH_Nsnow, iiLH_Ni, iiLH_Ngraupel, iiLH_Nc)
-
- public :: read_correlation_matrix
-
- private :: get_corr_var_index
-
- private
-
- contains
-
- !-----------------------------------------------------------------------------
- subroutine read_correlation_matrix( iunit, input_file, d_variables, &
- corr_array )
-
- ! Description:
- ! Reads a correlation variance array from a file and stores it in an array.
- !-----------------------------------------------------------------------------
-
- use crmx_input_reader, only: &
- one_dim_read_var, & ! Variable(s)
- read_one_dim_file, deallocate_one_dim_vars, count_columns ! Procedure(s)
-
- use crmx_matrix_operations, only: set_lower_triangular_matrix ! Procedure(s)
-
- use crmx_constants_clubb, only: fstderr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variable(s)
- integer, intent(in) :: &
- iunit, & ! File I/O unit
- d_variables ! number of variables in the array
-
- character(len=*), intent(in) :: input_file ! Path to the file
-
- ! Input/Output Variable(s)
- real( kind = core_rknd ), dimension(d_variables,d_variables), intent(inout) :: &
- corr_array ! Correlation variance array
-
- ! Local Variable(s)
-
- type(one_dim_read_var), allocatable, dimension(:) :: &
- retVars ! stores the variables read in from the corr_varnce.in file
-
- integer :: &
- var_index1, & ! variable index
- var_index2, & ! variable index
- nCols, & ! the number of columns in the file
- i, j ! Loop index
-
-
- !--------------------------- BEGIN CODE -------------------------
-
- nCols = count_columns( iunit, input_file )
-
- ! Allocate all arrays based on d_variables
- allocate( retVars(1:nCols) )
-
- ! Initializing to zero means that correlations we don't have
- ! (e.g. Nc and any variable other than s_mellor ) are assumed to be 0.
- corr_array(:,:) = 0.0_core_rknd
-
- ! Set main diagonal to 1
- do i=1, d_variables
- corr_array(i,i) = 1.0_core_rknd
- end do
-
- ! Read the values from the specified file
- call read_one_dim_file( iunit, nCols, input_file, retVars )
-
- if( size( retVars(1)%values ) /= nCols ) then
- write(fstderr, *) "Correlation matrix must have an equal number of rows and cols in file ", &
- input_file
- stop "Bad data in correlation file."
- end if
-
- ! Start at 2 because the first index is always just 1.0 in the first row
- ! and the rest of the rows are ignored
- do i=2, nCols
- var_index1 = get_corr_var_index( retVars(i)%name )
- if( var_index1 > -1 ) then
- do j=1, (i-1)
- var_index2 = get_corr_var_index( retVars(j)%name )
- if( var_index2 > -1 ) then
- call set_lower_triangular_matrix &
- ( d_variables, var_index1, var_index2, retVars(i)%values(j), &
- corr_array )
- end if
- end do
- end if
- end do
-
- call deallocate_one_dim_vars( nCols, retVars )
-
- return
- end subroutine read_correlation_matrix
-
- !--------------------------------------------------------------------------
- function get_corr_var_index( var_name ) result( i )
-
- ! Definition:
- ! Returns the index for a variable based on its name.
- !--------------------------------------------------------------------------
-
- implicit none
-
- character(len=*), intent(in) :: var_name ! The name of the variable
-
- ! Output variable
- integer :: i
-
- !------------------ BEGIN CODE -----------------------------
- i = -1
-
- select case( trim(var_name) )
-
- case( "s" )
- i = iiLH_s_mellor
-
- case( "t" )
- i = iiLH_t_mellor
-
- case( "w" )
- i = iiLH_w
-
- case( "Nc" )
- i = iiLH_Nc
-
- case( "rrain" )
- i = iiLH_rrain
-
- case( "Nr" )
- i = iiLH_Nr
-
- case( "rice" )
- i = iiLH_rice
-
- case( "Ni" )
- i = iiLH_Ni
-
- case( "rsnow" )
- i = iiLH_rsnow
-
- case( "Nsnow" )
- i = iiLH_Nsnow
-
- end select
-
- return
-
- end function get_corr_var_index
-end module crmx_corr_matrix_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_csr_matrix_class_3array.F90 b/src/physics/spcam/crm/CLUBB/crmx_csr_matrix_class_3array.F90
deleted file mode 100644
index 1891bd6945..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_csr_matrix_class_3array.F90
+++ /dev/null
@@ -1,522 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: csr_matrix_class_3array.F90 5529 2011-11-29 19:49:15Z connork@uwm.edu $
-!===============================================================================
-module crmx_csr_matrix_class
-
- ! Description:
- ! This module contains some of the matrix description arrays required by
- ! PARDISO, GMRES, and other sparse matrix solvers. The format is called CSR
- ! (compressed sparse row) format, and is currently leveraged through PARDISO
- ! and GMRES.
- ! These are all 1 dimensional arrays that describe a matrix that
- ! will be passed to the solver. The _ja arrays describe which
- ! columns in the matrix have nonzero values--for our purposes, all the
- ! elements on the appropriate diagonals have values. The _ia arrays describe
- ! which _ja array elements correspond to new rows.
- ! Further description of this format can be found in the PARDISO manual, or
- ! alternately, in Intel MKL's documentation.
- ! For our purposes, the _ia and _ja arrays will be fixed for the types
- ! of matrices we have, so we calculate these initially using
- ! initialize_csr_class and simply use the pointers, similar to how
- ! the grid pointers are initialized. This should save a fair amount of time,
- ! as we do not have to recalculate the arrays.
- !
- ! A description of the CSR matrix format:
- ! The CSR matrix format requires three arrays--an a array,
- ! a ja array, and an ia array.
- !
- ! The a array stores, in sequential order, the actual values in the matrix.
- ! Essentially, just copy the matrix into a 1-dimensional array as you move
- ! from left to right, top down through the matrix. The a array changes
- ! frequently for our purposes in CLUBB, and is not useful to be initialized
- ! here.
- !
- ! The ja array stores, in sequential order, the columns of each element in
- ! the matrix that is nonzero. Essentially, you take the column of each
- ! element that is nonzero as you move from left to right, top down through
- ! the matrix.
- !
- ! An example follows to illustrate the point:
- ! [3.0 2.0 0.0 0.0 0.0 0.0
- ! 2.5 1.7 3.6 0.0 0.0 0.0
- ! 0.0 5.2 1.7 3.6 0.0 0.0
- ! 0.0 0.0 4.7 2.9 0.6 0.0
- ! 0.0 0.0 0.0 8.9 4.6 1.2
- ! 0.0 0.0 0.0 0.0 5.8 3.7]
- !
- ! Our ja array would look like the following--a pipe denotes a new row:
- ! [1 2 | 1 2 3 | 2 3 4 | 3 4 5 | 4 5 6 | 5 6]
- !
- ! The ia array stores the indices of the ja array that correspond to new rows
- ! in the matrix, with a final entry just beyond the end of the ja matrix
- ! that signifies the end of the matrix.
- ! In our example, the ia array would look like this:
- !
- ! [1 3 6 9 12 15 17]
- !
- ! Similar principles can be applied to find the ia and ja matrices for all
- ! of the general cases CLUBB uses. In addition, because CLUBB typically
- ! uses similar matrices for its calculations, we can simply initialize
- ! the ia and ja matrices in this module rather than repeatedly initialize
- ! them. This should save on compute time and provide a centralized location
- ! to acquire ia and ja arrays.
-
- implicit none
-
- public :: csr_tridiag_ia, csr_tridiag_ja, &
- csr_banddiag5_135_ia, csr_banddiag5_135_ja, &
- csr_banddiag5_12345_ia, csr_banddiag5_12345_ja, &
- initialize_csr_class, &
- ia_size, tridiag_ja_size, band12345_ja_size, band135_ja_size, &
- csr_intlc_s3b_f5b_ia, csr_intlc_s3b_f5b_ja, &
- csr_intlc_trid_5b_ia, csr_intlc_trid_5b_ja, &
- csr_intlc_5b_5b_ia, csr_intlc_5b_5b_ja, &
- intlc_ia_size, intlc_s3d_5d_ja_size, intlc_5d_5d_ja_size, &
- intlc_td_5d_ja_size
-
- private ! Default scope
-
- integer, pointer, dimension(:) :: &
- csr_tridiag_ia, & !_ia array description for a tridiagonal matrix
- csr_tridiag_ja, & !_ja array description for a tridiagonal matrix
- csr_banddiag5_135_ia, & !_ia array description for a 5-band matrix
- ! with the first upper and lower bands as 0.
- csr_banddiag5_135_ja, & !_ja array description for a 5-band matrix
- ! with the first upper and lower bands as 0.
- csr_banddiag5_12345_ia, & !_ia array description for a 5-band matrix
- csr_banddiag5_12345_ja, & !_ja array description for a 5-band matrix
- csr_intlc_s3b_f5b_ia, & !_ia array description for interlaced 5-band
- ! matrix ("spaced 3-band, full 5-band")
- csr_intlc_s3b_f5b_ja, & !_ja array description for interlaced 5-band
- ! matrix ("spaced 3-band, full 5-band")
- csr_intlc_trid_5b_ia, & !_ia array description for interlaced tridiag
- ! and 5-band matrix (tridiag, 5-band)
- csr_intlc_trid_5b_ja, & !_ja array description for interlaced tridiag
- ! and 5-band matrix (tridiag, 5-band)
- csr_intlc_5b_5b_ia, & !_ia array description for "interlaced"
- ! 5-band and 5-band matrix (double-size 5-band)
- csr_intlc_5b_5b_ja !_ja array description for "interlaced"
- ! 5-band and 5-band matrix (double-size 5-band)
-
- integer :: &
- ia_size, & ! Size of the _ia arrays.
- tridiag_ja_size, & ! Size of the tridiagonal ja array.
- band12345_ja_size, & ! Size of the 5-band-with-first-bands-0 ja array.
- band135_ja_size, & ! Size of the 5-band ja array.
- intlc_ia_size, & ! Size of the interlaced _ia arrays.
- intlc_s3d_5d_ja_size, & ! Size of the interlaced spaced
- ! 3-diag+5-diag ja arrays.
- intlc_5d_5d_ja_size, & ! Size of the interlaced 5-diag+5-diag ja arrays.
- intlc_td_5d_ja_size ! Size of the interlaced tridiag+5-diag ja arrays.
-
- contains
-
- !============================================================================
- subroutine initialize_csr_class
-
- ! Description:
- ! PARDISO matrix array initialization
- !
- ! This subroutine creates the _ia and _ja arrays, and calculates their
- ! required values for the current gr%nz.
- !
- ! References:
- ! None
- !------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- implicit none
-
- ! Local variables
- integer :: &
- i, j, & ! Loop indices
- error, & ! Status for allocation
- num_bands, & ! Number of diagonals for allocation
- num_diags, & ! Number of non-empty diagonals for allocation
- cur_row, & ! Current row--used in initialization
- cur_diag, & ! Current diagonal--num_diags/2 + 1 is the main diagonal
- ! Note: At the boundaries, less diagonals are in scope.
- ! At the lower boundaries, the subdiagonals aren't in scope.
- ! At the upper boundaries, the superdiagonals aren't in scope.
- counter ! Counter used to initialize the interlaced matrices
-
- logical :: l_print_ia_ja ! Debug flag to print the ia and ja arrays after
- ! initialization is complete.
-
- ! ---- Begin Code ----
-
- ! Define the array sizes
- ia_size = gr%nz + 1
- intlc_ia_size = (2 * gr%nz) + 1
-
- ! Tridiagonal case and 5-band with 2 empty diagonals have 3 full diagonals
- num_diags = 3
- tridiag_ja_size = (gr%nz * num_diags) - 2
- band135_ja_size = (gr%nz * num_diags) - 4
-
- ! 5-band with all diagonals has 5 full diagonals
- num_diags = 5
- band12345_ja_size = (gr%nz * num_diags) - 6
-
- ! Interlaced arrays are tricky--there is an average of 4 diagonals for
- ! the 3/5band, but we need to take into account the fact that the
- ! tridiagonal and spaced 3-band will have different boundary indices.
- num_diags = 4
- intlc_td_5d_ja_size = (gr%nz * 2 * num_diags) - 4
- intlc_s3d_5d_ja_size = (gr%nz * 2 * num_diags) - 5
-
- ! The double-sized "interlaced" 5-band is similar to the standard 5-band
- num_diags = 5
- intlc_5d_5d_ja_size = (gr%nz * 2 * num_diags) - 6
-
- ! Allocate the correct amount of space for the actual _ia and _ja arrays
- allocate( csr_tridiag_ia(1:ia_size), &
- csr_tridiag_ja(1:tridiag_ja_size), &
- csr_banddiag5_12345_ia(1:ia_size), &
- csr_banddiag5_12345_ja(1:band12345_ja_size), &
- csr_banddiag5_135_ia(1:ia_size), &
- csr_banddiag5_135_ja(1:band135_ja_size), &
- csr_intlc_s3b_f5b_ia(1:intlc_ia_size), &
- csr_intlc_s3b_f5b_ja(1:intlc_s3d_5d_ja_size), &
- csr_intlc_trid_5b_ia(1:intlc_ia_size), &
- csr_intlc_trid_5b_ja(1:intlc_td_5d_ja_size), &
- csr_intlc_5b_5b_ia(1:intlc_ia_size), &
- csr_intlc_5b_5b_ja(1:intlc_5d_5d_ja_size), &
- stat=error )
-
- if ( error /= 0 ) then
- write(fstderr,*) "Allocation of CSR matrix arrays failed."
- stop "Fatal error--allocation of CSR matrix arrays failed."
- end if
-
- ! Initialize the tridiagonal matrix arrays
- num_bands = 3
- do i = 2, (gr%nz - 1), 1
- cur_row = (i - 1) * num_bands
- do j = 1, num_bands, 1
- cur_diag = j - 1
- csr_tridiag_ja(cur_row + cur_diag) = i + j - 2
- end do
- csr_tridiag_ia(i) = cur_row
- end do ! i = 2...gr%nz-1
-
- ! Handle boundary conditions for the tridiagonal matrix arrays
- ! These conditions have been hand-calculated bearing in mind that the
- ! matrix in question is tridiagonal.
-
- ! Make sure we don't crash if someone sets up gr%nz as 1.
- if ( gr%nz > 1 ) then
- ! Lower boundaries
- csr_tridiag_ja(1) = 1
- csr_tridiag_ja(2) = 2
- csr_tridiag_ia(1) = 1
-
- ! Upper boundaries
- csr_tridiag_ja(tridiag_ja_size - 1) = gr%nz - 1
- csr_tridiag_ja(tridiag_ja_size) = gr%nz
- csr_tridiag_ia(ia_size - 1) = tridiag_ja_size - 1
-
- ! This final boundary is to signify the end of the matrix, and is
- ! intended to be beyond the bound of the ja array.
- csr_tridiag_ia(ia_size) = tridiag_ja_size + 1
- end if ! gr%nz > 1
-
- ! Initialize the 5-band matrix arrays
- num_bands = 5
- do i = 3, (gr%nz - 2), 1
-
- ! Full 5-band matrix has 5 diagonals to initialize
- num_diags = 5
- cur_row = num_diags * (i - 1)
- do j = 1, num_diags, 1
- cur_diag = j - 3
- csr_banddiag5_12345_ja(cur_row + cur_diag) = i + cur_diag
- end do
-
- csr_banddiag5_12345_ia(i) = cur_row - 2
-
- ! 5-band matrix with 2 zero bands has 3 diagonals to initialize
- num_diags = 3
- cur_row = num_diags * (i - 1)
- do j = 1, num_diags, 1
- cur_diag = j - 2
- ! The first upper and first lower bands are zero, so there needs to be
- ! special handling to account for this. The j * 2 takes into account
- ! the spaces between diagonals.
- csr_banddiag5_135_ja(cur_row + cur_diag) = i + ((j * 2) - 1) - num_diags
- end do
-
- csr_banddiag5_135_ia(i) = cur_row - 1
-
- end do ! i = 3...gr%nz-2
-
- ! Handle boundary conditions for the 5-band matrix arrays
- ! These values have been hand-calculated bearing in mind the two different
- ! types of 5-band matrices.
-
- ! Make sure we don't crash if someone sets up gr%nz as less than 3.
- if ( gr%nz > 2 ) then
-
- ! -------------- (full) 5-band matrix boundaries ---------------
-
- ! Lower boundaries for the (full) 5-band matrix.
- do i = 1, 3, 1
- csr_banddiag5_12345_ja(i) = i
- end do
- do i = 1, 4, 1
- csr_banddiag5_12345_ja(i + 3) = i
- end do
- csr_banddiag5_12345_ia(1) = 1
- csr_banddiag5_12345_ia(2) = 4
-
- ! Upper boundaries for the (full) 5-band matrix.
- ! 7 and 3 are the number of elements from the "end" of the matrix if we
- ! travel right to left, bottom up. Because the ja matrices correspond to
- ! the column the element is in, we go 3 or 4 elements from the end for the
- ! second to last row (both superdiagonals absent on last row),
- ! and 3 for the last row (both superdiagonals absent). The indices are
- ! similarly calculated, except that in the case of the second to last
- ! row, it is necessary to offset for the last row as well (hence,
- ! 7 = 4+3).
- do i = 1, 4, 1
- csr_banddiag5_12345_ja(band12345_ja_size - 7 + i) = gr%nz + i - 4
- end do
- do i = 1, 3, 1
- csr_banddiag5_12345_ja(band12345_ja_size - 3 + i) = gr%nz + i - 3
- end do
- csr_banddiag5_12345_ia(ia_size - 2) = band12345_ja_size - 6
- csr_banddiag5_12345_ia(ia_size - 1) = band12345_ja_size - 2
-
- ! This final boundary is to signify the end of the matrix, and is
- ! intended to be beyond the bound of the ja array.
- csr_banddiag5_12345_ia(ia_size) = band12345_ja_size + 1
-
- ! ------------ end (full) 5-band matrix boundaries ---------------
-
- ! --------- 5-band matrix w/ empty first bands boundaries ----------
-
- ! Lower boundaries for the 5-band w/ empty first bands matrix
- ! The 2 * i is present because of the space between the main diagonal
- ! and the superdiagonal that actually have nonzero values.
- do i = 1, 2, 1
- csr_banddiag5_135_ja(i) = (2 * i) - 1
- csr_banddiag5_135_ja(i + 2) = (2 * i)
- csr_banddiag5_135_ia(i) = (2 * i) - 1
- end do
-
- ! Upper boundaries for the 5-band w/ empty first bands matrix
- ! The values for the boundaries are tricky, as the indices and values
- ! are not equal. The indices are 2 and 4 away from the end, as there are
- ! only two nonzero values at the two final rows.
- ! The values, on the other hand, are different, because of the
- ! aforementioned space, this time between the main and subdiagonal.
- do i = 1, 2, 1
- csr_banddiag5_135_ja(band135_ja_size - 4 + i) = gr%nz + (i * 2) - 5
- csr_banddiag5_135_ja(band135_ja_size - 2 + i) = gr%nz + (i * 2) - 4
- end do
- csr_banddiag5_135_ia(ia_size - 2) = band135_ja_size - 3
- csr_banddiag5_135_ia(ia_size - 1) = band135_ja_size + 1
-
- ! This final boundary is to signify the end of the matrix, and is
- ! intended to be beyond the bound of the ja array.
- csr_banddiag5_135_ia(ia_size) = band135_ja_size + 1
-
- ! ------- end 5-band matrix w/ empty first bands boundaries --------
-
- end if ! gr%nz > 2
-
- ! Initialize the interlaced arrays--all of them are 5-band right now.
- num_bands = 5
-
- ! Our counter starts at 2--this is used for the 3/5 interlaced matrices.
- ! We start at 2 so when we enter the odd row and increment by 5,
- ! it becomes 7.
- counter = 2
-
- do i = 3, ((gr%nz * 2) - 2), 1
- if (mod( i,2 ) == 1) then
- ! Odd row, this is the potentially non 5-band row.
- ! Increment counter. Last row was an even row, so we'll need to add 5.
- counter = counter + 5
-
- ! For our tridiag and spaced 3-band arrays, this will be a
- ! 3-diagonal row.
- num_diags = 3
- cur_row = counter + 1
- do j = 1, num_diags, 1
- cur_diag = j - 2
- csr_intlc_s3b_f5b_ja(cur_row + cur_diag) &
- = i + ((j * 2) - 1) - num_diags
- csr_intlc_trid_5b_ja(cur_row + cur_diag) = i + cur_diag
- end do
- csr_intlc_s3b_f5b_ia(i) = counter
- csr_intlc_trid_5b_ia(i) = counter
-
- ! For our 5-band interlaced-size array, this will be a
- ! 5-diagonal row (obviously!).
- num_diags = 5
- cur_row = num_diags * (i - 1)
- do j = 1, num_diags, 1
- cur_diag = j - 3
- csr_intlc_5b_5b_ja(cur_row + cur_diag) = i + cur_diag
- end do
-
- csr_intlc_5b_5b_ia(i) = cur_row - 2
-
- else
- ! Even row, this is the "guaranteed" 5-band row.
- ! Increment counter. Last row was an odd row, so we'll need to add 3.
- counter = counter + 3
-
- ! For our tridiag and spaced 3-band arrays, this will be a
- ! 5-diagonal row.
- num_diags = 5
- cur_row = counter + 2
- do j = 1, num_diags, 1
- cur_diag = j - 3
- csr_intlc_s3b_f5b_ja(cur_row + cur_diag) = i + cur_diag
- csr_intlc_trid_5b_ja(cur_row + cur_diag) = i + cur_diag
- end do
-
- csr_intlc_s3b_f5b_ia(i) = counter
- csr_intlc_trid_5b_ia(i) = counter
-
- ! For our 5-band "interlaced" array, this will also be a
- ! 5-diagonal row. However, we need to change the cur_row to match
- ! what we're expecting for the 5-band.
- num_diags = 5
- cur_row = num_diags * (i - 1)
- do j = 1, num_diags, 1
- cur_diag = j - 3
- csr_intlc_5b_5b_ja(cur_row + cur_diag) = i + cur_diag
- end do
-
- csr_intlc_5b_5b_ia(i) = cur_row - 2
-
- end if ! mod(i,2) == 1
- end do ! i = 3...(gr%nz*2)-2
-
- ! Handle boundary conditions for the interlaced matrix arrays
- ! These conditions have been hand-calculated bearing in mind
- ! the structure of the interlaced matrices.
-
- ! Make sure we don't crash if someone sets up gr%nz as less than 3.
- if (gr%nz > 2) then
- ! Lower boundaries
-
- ! First row
- do i = 1, 2, 1
- csr_intlc_s3b_f5b_ja(i) = (i * 2) - 1
- csr_intlc_trid_5b_ja(i) = i
- end do
- do i = 1, 3, 1
- csr_intlc_5b_5b_ja(i) = i
- end do
- csr_intlc_s3b_f5b_ia(1) = 1
- csr_intlc_trid_5b_ia(1) = 1
- csr_intlc_5b_5b_ia(1) = 1
-
- ! Second row
- do i = 1, 4, 1
- csr_intlc_s3b_f5b_ja(i + 2) = i
- csr_intlc_trid_5b_ja(i + 2) = i
- csr_intlc_5b_5b_ja(i + 3) = i
- end do
- csr_intlc_s3b_f5b_ia(2) = 3
- csr_intlc_trid_5b_ia(2) = 3
- csr_intlc_5b_5b_ia(2) = 4
-
- ! Upper boundaries
-
- ! Last two rows
- ! Note that in comparison to the other upper boundaries, we have to use
- ! intlc_ia_size - 1 for our upper index limit as the matrix is
- ! double-sized.
-
- ! Second-to-last row
- do i = 1, 2, 1
- csr_intlc_s3b_f5b_ja(intlc_s3d_5d_ja_size - 5 + i) &
- = intlc_ia_size - 1 + (i * 2) - 5
- end do
- do i = 1, 3, 1
- csr_intlc_trid_5b_ja(intlc_td_5d_ja_size - 6 + i) &
- = intlc_ia_size - 1 + i - 3
- end do
- do i = 1, 4, 1
- csr_intlc_5b_5b_ja(intlc_5d_5d_ja_size - 7 + i) &
- = intlc_ia_size-1 + i - 4
- end do
-
- ! Last row
- do i = 1, 3, 1
- csr_intlc_s3b_f5b_ja(intlc_s3d_5d_ja_size - 3 + i) &
- = intlc_ia_size-1 + i - 3
- csr_intlc_trid_5b_ja(intlc_td_5d_ja_size - 3 + i) &
- = intlc_ia_size-1 + i - 3
- csr_intlc_5b_5b_ja(intlc_5d_5d_ja_size - 3 + i) &
- = intlc_ia_size-1 + i - 3
- end do
-
- ! Lastly, take care of the ia arrays.
- csr_intlc_s3b_f5b_ia(intlc_ia_size - 2) = intlc_s3d_5d_ja_size - 4
- csr_intlc_s3b_f5b_ia(intlc_ia_size - 1) = intlc_s3d_5d_ja_size - 2
- csr_intlc_s3b_f5b_ia(intlc_ia_size) = intlc_s3d_5d_ja_size + 1
-
- csr_intlc_trid_5b_ia(intlc_ia_size - 2) = intlc_td_5d_ja_size - 5
- csr_intlc_trid_5b_ia(intlc_ia_size - 1) = intlc_td_5d_ja_size - 2
- csr_intlc_trid_5b_ia(intlc_ia_size) = intlc_td_5d_ja_size + 1
-
- csr_intlc_5b_5b_ia(intlc_ia_size - 2) = intlc_5d_5d_ja_size - 6
- csr_intlc_5b_5b_ia(intlc_ia_size - 1) = intlc_5d_5d_ja_size - 2
- csr_intlc_5b_5b_ia(intlc_ia_size) = intlc_5d_5d_ja_size + 1
-
-
- end if ! gr%nz > 2
-
- ! Enable printing the ia/ja arrays for debug purposes
- l_print_ia_ja = .false.
- if (l_print_ia_ja) then
- do i = 1, ia_size, 1
- print *, "tridiag ia idx", i, "=", csr_tridiag_ia(i)
- print *, "banddiag12345 ia idx", i, "=", csr_banddiag5_12345_ia(i)
- print *, "banddiag135 ia idx", i, "=", csr_banddiag5_135_ia(i)
- end do
- do i = 1, intlc_ia_size, 1
- print *, "interlaced tridiag w/ 5-band ia idx", i, &
- "=", csr_intlc_trid_5b_ia(i)
- print *, "interlaced spaced-3-band+5-band ia idx", i, &
- "=", csr_intlc_s3b_f5b_ia(i)
- print *, "interlaced 5-band w/ 5-band ia idx", i, "=", &
- csr_intlc_5b_5b_ia(i)
- end do
- do i = 1, tridiag_ja_size, 1
- print *, "tridiag ja idx", i, "=", csr_tridiag_ja(i)
- end do
- do i = 1, band12345_ja_size, 1
- print *, "band12345 ja idx", i, "=", csr_banddiag5_12345_ja(i)
- end do
- do i = 1, band135_ja_size, 1
- print *, "band135 ja idx", i, "=", csr_banddiag5_135_ja(i)
- end do
- do i = 1, intlc_td_5d_ja_size, 1
- print *, "interlaced tridiag w/ 5-band ja idx", i, &
- "=", csr_intlc_trid_5b_ja(i)
- end do
- do i = 1, intlc_s3d_5d_ja_size, 1
- print *, "interlaced spaced-3-band+5-band ja idx", i, &
- "=", csr_intlc_s3b_f5b_ja(i)
- end do
- do i = 1, intlc_5d_5d_ja_size, 1
- print *, "interlaced 5-band w/ 5-band ja idx", i, "=", &
- csr_intlc_5b_5b_ja(i)
- end do
- end if ! l_print_ia_ja
-
- end subroutine initialize_csr_class
-
-end module crmx_csr_matrix_class
diff --git a/src/physics/spcam/crm/CLUBB/crmx_diagnose_correlation_module.f90 b/src/physics/spcam/crm/CLUBB/crmx_diagnose_correlation_module.f90
deleted file mode 100644
index 1160134ab3..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_diagnose_correlation_module.f90
+++ /dev/null
@@ -1,489 +0,0 @@
-! $Id$
-module crmx_diagnose_correlations_module
-
- use crmx_clubb_precision, only: &
- core_rknd
-
- implicit none
-
- public :: diagnose_KK_corr, diagnose_LH_corr, &
- calc_mean, calc_varnce, calc_w_corr
-
-
- private :: diagnose_corr
-
-
- contains
-
-!-----------------------------------------------------------------------
- subroutine diagnose_KK_corr( Ncm, rrainm, Nrm, & ! intent(in)
- Ncp2_on_Ncm2, rrp2_on_rrm2, Nrp2_on_Nrm2, &
- corr_ws, corr_wrr, corr_wNr, corr_wNc, &
- pdf_params, &
- corr_rrNr_p, corr_srr_p, corr_sNr_p, corr_sNc_p, &
- corr_rrNr, corr_srr, corr_sNr, corr_sNc ) ! intent(inout)
-
- ! Description:
- ! This subroutine diagnoses the correlation matrix in order to feed it
- ! into KK microphysics.
-
- ! References:
- ! Larson et al. (2011), J. of Geophysical Research, Vol. 116, D00T02
- ! (see CLUBB-Trac:ticket:514)
- !-----------------------------------------------------------------------
-
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_pdf_parameter_module, only: &
- pdf_parameter ! Type
-
- use crmx_constants_clubb, only: &
- w_tol, & ! [m/s]
- s_mellor_tol, & ! [kg/kg]
- Nc_tol, & ! [num/kg]
- rr_tol, & ! [kg/kg]
- Nr_tol ! [num/kg]
-
- use crmx_stats_type, only: &
- stat_update_var_pt ! Procedure(s)
-
- implicit none
-
- intrinsic :: sqrt
-
- ! Local Constants
- integer, parameter :: &
- n_variables = 5
-
- ! Input Variables
-
- real( kind = core_rknd ), intent(in) :: &
- Ncm, & ! Cloud droplet number conc. [num/kg]
- rrainm, & ! rain water mixing ratio [kg/kg]
- Nrm, & ! Mean rain drop concentration [num/kg]
- Ncp2_on_Ncm2, & ! Variance of Nc divided by Ncm^2 [-]
- rrp2_on_rrm2, & ! Variance of rrain divided by rrainm^2 [-]
- Nrp2_on_Nrm2, & ! Variance of Nr divided by Nrm^2 [-]
- corr_ws, & ! Correlation between s_mellor and w [-]
- corr_wrr, & ! Correlation between rrain and w [-]
- corr_wNr, & ! Correlation between Nr and w [-]
- corr_wNc, & ! Correlation between Nc and w [-]
- corr_rrNr_p, & ! Prescribed correlation between rrain and Nr [-]
- corr_srr_p, & ! Prescribed correlation between s and rrain [-]
- corr_sNr_p, & ! Prescribed correlation between s and Nr [-]
- corr_sNc_p ! Prescribed correlation between s and Nc [-]
-
- type(pdf_parameter), intent(in) :: &
- pdf_params ! PDF parameters [units vary]
-
- ! Input/Output Variables
- real( kind = core_rknd ), intent(inout) :: &
- corr_rrNr, & ! Correlation between rrain and Nr [-]
- corr_srr, & ! Correlation between s and rrain [-]
- corr_sNr, & ! Correlation between s and Nr [-]
- corr_sNc ! Correlation between s and Nc [-]
-
-
-
- ! Local Variables
- real( kind = core_rknd ), dimension(n_variables, n_variables) :: &
- corr_matrix_approx, & ! [-]
- corr_matrix_prescribed ! [-]
-
- real( kind = core_rknd ), dimension(n_variables) :: &
- sqrt_xp2_on_xm2, & ! sqrt of x_variance / x_mean^2 [units vary]
- xm ! means of the hydrometeors [units vary]
-
- ! Indices of the hydrometeors
- integer :: &
- ii_w = 1, &
- ii_s = 2, &
- ii_rrain = 3, &
- ii_Nr = 4, &
- ii_Nc = 5
-
- integer :: i, j ! Loop Iterators
-
-
- !-------------------- Begin code --------------------
-
- ! set up xp2_on_xm2
-
- ! TODO Why is wp2_on_wm2=1
- ! S_i is set to 1 for s_mellor and w, because s_mellorm could be 0
- sqrt_xp2_on_xm2(ii_w) = 1._core_rknd
- sqrt_xp2_on_xm2(ii_s) = 1._core_rknd
-
- sqrt_xp2_on_xm2(ii_rrain) = sqrt(rrp2_on_rrm2)
- sqrt_xp2_on_xm2(ii_Nr) = sqrt(Nrp2_on_Nrm2)
- sqrt_xp2_on_xm2(ii_Nc) = sqrt(Ncp2_on_Ncm2)
-
- ! initialize the correlation matrix with 0
- do i=1, n_variables
- do j=1, n_variables
- corr_matrix_approx(i,j) = 0._core_rknd
- corr_matrix_prescribed(i,j) = 0._core_rknd
- end do
- end do
-
- ! set diagonal of the correlation matrix to 1
- do i = 1, n_variables
- corr_matrix_approx(i,i) = 1._core_rknd
- corr_matrix_prescribed(i,i) = 1._core_rknd
- end do
-
-
- ! set the first row to the corresponding prescribed correlations
- corr_matrix_approx(ii_s,1) = corr_ws
- corr_matrix_approx(ii_rrain,1) = corr_wrr
- corr_matrix_approx(ii_Nr,1) = corr_wNr
- corr_matrix_approx(ii_Nc,1) = corr_wNc
-
- !corr_matrix_prescribed = corr_matrix_approx
-
- ! set up the prescribed correlation matrix
- if( ii_rrain > ii_Nr ) then
- corr_matrix_prescribed(ii_rrain, ii_Nr) = corr_rrNr_p
- else
- corr_matrix_prescribed(ii_Nr, ii_rrain) = corr_rrNr_p
- end if
-
- if ( ii_s > ii_rrain ) then
- corr_matrix_prescribed(ii_s, ii_rrain) = corr_srr_p
- else
- corr_matrix_prescribed(ii_rrain, ii_s) = corr_srr_p
- end if
-
- if ( ii_s > ii_Nr ) then
- corr_matrix_prescribed(ii_s, ii_Nr) = corr_sNr_p
- else
- corr_matrix_prescribed(ii_Nr, ii_s) = corr_sNr_p
- end if
-
- if ( ii_s > ii_Nc ) then
- corr_matrix_prescribed(ii_s, ii_Nc) = corr_sNc_p
- else
- corr_matrix_prescribed(ii_Nc, ii_s) = corr_sNc_p
- end if
-
- call diagnose_corr( n_variables, sqrt_xp2_on_xm2, corr_matrix_prescribed, & !intent(in)
- corr_matrix_approx ) ! intent(inout)
-
- if( ii_rrain > ii_Nr ) then
- corr_rrNr = corr_matrix_approx(ii_rrain, ii_Nr)
- else
- corr_rrNr = corr_matrix_approx(ii_Nr, ii_rrain)
- end if
-
- if ( ii_s > ii_rrain ) then
- corr_srr = corr_matrix_approx(ii_s, ii_rrain)
- else
- corr_srr = corr_matrix_approx(ii_rrain, ii_s)
- end if
-
- if ( ii_s > ii_Nr ) then
- corr_sNr = corr_matrix_approx(ii_s, ii_Nr)
- else
- corr_sNr = corr_matrix_approx(ii_Nr, ii_s)
- end if
-
- if ( ii_s > ii_Nc ) then
- corr_sNc = corr_matrix_approx(ii_s, ii_Nc)
- else
- corr_sNc = corr_matrix_approx(ii_Nc, ii_s)
- end if
-
- end subroutine diagnose_KK_corr
-
-!-----------------------------------------------------------------------
- subroutine diagnose_LH_corr( xp2_on_xm2, d_variables, corr_matrix_prescribed, & !intent(in)
- corr_array ) ! intent(inout)
-
- ! Description:
- ! This subroutine diagnoses the correlation matrix in order to feed it
- ! into SILHS microphysics.
-
- ! References:
- ! Larson et al. (2011), J. of Geophysical Research, Vol. 116, D00T02
- ! (see CLUBB Trac ticket#514)
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_corr_matrix_module, only: &
- iiLH_w ! Variable(s)
-
- implicit none
-
- intrinsic :: max, sqrt, transpose
-
- ! Input Variables
- integer, intent(in) :: d_variables
-
- real( kind = core_rknd ), dimension(d_variables, d_variables), intent(in) :: &
- corr_matrix_prescribed
-
- real( kind = core_rknd ), dimension(d_variables), intent(in) :: &
- xp2_on_xm2 ! ratios of x_variance over x_mean^2
-
- ! Input/Output variables
- real( kind = core_rknd ), dimension(d_variables, d_variables), intent(inout) :: &
- corr_array
-
- ! Local Variables
- real( kind = core_rknd ), dimension(d_variables, d_variables) :: &
- corr_matrix_pre_swapped
-
- real( kind = core_rknd ), dimension(d_variables) :: &
- swap_array
-
- !-------------------- Begin code --------------------
-
- ! Swap the w-correlations to the first row
- swap_array = corr_array(:, 1)
- corr_array(1:iiLH_w, 1) = corr_array(iiLH_w, iiLH_w:1:-1)
- corr_array((iiLH_w+1):d_variables, 1) = corr_array((iiLH_w+1):d_variables, iiLH_w)
- corr_array(iiLH_w, 1:iiLH_w) = swap_array(iiLH_w:1:-1)
- corr_array((iiLH_w+1):d_variables, iiLH_w) = swap_array((iiLH_w+1):d_variables)
-
- corr_matrix_pre_swapped = corr_matrix_prescribed
- swap_array = corr_matrix_pre_swapped (:,1)
- corr_matrix_pre_swapped(1:iiLH_w, 1) = corr_matrix_pre_swapped(iiLH_w, iiLH_w:1:-1)
- corr_matrix_pre_swapped((iiLH_w+1):d_variables, 1) = corr_matrix_pre_swapped( &
- (iiLH_w+1):d_variables, iiLH_w)
- corr_matrix_pre_swapped(iiLH_w, 1:iiLH_w) = swap_array(iiLH_w:1:-1)
- corr_matrix_pre_swapped((iiLH_w+1):d_variables, iiLH_w) = swap_array((iiLH_w+1):d_variables)
-
- ! diagnose correlations
- call diagnose_corr( d_variables, sqrt(xp2_on_xm2), corr_matrix_pre_swapped, &
- corr_array)
-
- ! Swap rows back
- swap_array = corr_array(:, 1)
- corr_array(1:iiLH_w, 1) = corr_array(iiLH_w, iiLH_w:1:-1)
- corr_array((iiLH_w+1):d_variables, 1) = corr_array((iiLH_w+1):d_variables, iiLH_w)
- corr_array(iiLH_w, 1:iiLH_w) = swap_array(iiLH_w:1:-1)
- corr_array((iiLH_w+1):d_variables, iiLH_w) = swap_array((iiLH_w+1):d_variables)
-
- end subroutine diagnose_LH_corr
-
-!-----------------------------------------------------------------------
- subroutine diagnose_corr( n_variables, sqrt_xp2_on_xm2, corr_matrix_prescribed, & !intent(in)
- corr_matrix_approx ) ! intent(inout)
-
- ! Description:
- ! This subroutine diagnoses the correlation matrix for each timestep.
-
- ! References:
- ! Larson et al. (2011), J. of Geophysical Research, Vol. 116, D00T02
- ! (see CLUBB Trac ticket#514)
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_parameters_tunable, only: &
- alpha_corr ! Constant(s)
-
- use crmx_constants_clubb, only: &
- max_mag_correlation
-
- implicit none
-
- intrinsic :: &
- sqrt, abs, sign
-
- ! Input Variables
- integer, intent(in) :: &
- n_variables ! number of variables in the correlation matrix [-]
-
- real( kind = core_rknd ), dimension(n_variables), intent(in) :: &
- sqrt_xp2_on_xm2 ! sqrt of x_variance / x_mean^2 [units vary]
-
- real( kind = core_rknd ), dimension(n_variables,n_variables), intent(in) :: &
- corr_matrix_prescribed ! correlation matrix [-]
-
- ! Input/Output Variables
- real( kind = core_rknd ), dimension(n_variables,n_variables), intent(inout) :: &
- corr_matrix_approx ! correlation matrix [-]
-
-
- ! Local Variables
- integer :: i, j ! Loop iterator
-
- real( kind = core_rknd ) :: &
- f_ij, &
- f_ij_o
-
- real( kind = core_rknd ), dimension(n_variables) :: &
- s_1j ! s_1j = sqrt(1-c_1j^2)
-
-
- !-------------------- Begin code --------------------
-
- ! calculate all square roots
- do i = 1, n_variables
-
- s_1j(i) = sqrt(1._core_rknd-corr_matrix_approx(i,1)**2)
-
- end do
-
-
- ! Diagnose the missing correlations (upper triangle)
- do j = 2, (n_variables-1)
- do i = (j+1), n_variables
-
- ! formula (16) in the ref. paper (Larson et al. (2011))
- !f_ij = alpha_corr * sqrt_xp2_on_xm2(i) * sqrt_xp2_on_xm2(j) &
- ! * sign(1.0_core_rknd,corr_matrix_approx(1,i)*corr_matrix_approx(1,j))
-
- ! If the predicting c1i's are small then cij will be closer to the prescribed value. If
- ! the c1i's are bigger, then cij will be closer to formular (15) from the ref. paper. See
- ! clubb:ticket:514:comment:61 for details.
- !f_ij = (1-abs(corr_matrix_approx(1,i)*corr_matrix_approx(1,j)))*corr_matrix_prescribed(i,j) &
- ! + abs(corr_matrix_approx(1,i)*corr_matrix_approx(1,j))*f_ij_o
-
- f_ij = corr_matrix_prescribed(i,j)
-
- ! make sure -1 < f_ij < 1
- if ( f_ij < -max_mag_correlation ) then
-
- f_ij = -max_mag_correlation
-
- else if ( f_ij > max_mag_correlation ) then
-
- f_ij = max_mag_correlation
-
- end if
-
-
- ! formula (15) in the ref. paper (Larson et al. (2011))
- corr_matrix_approx(i,j) = corr_matrix_approx(i,1) * corr_matrix_approx(j,1) &
- + f_ij * s_1j(i) * s_1j(j)
-
- end do ! do j
- end do ! do i
-
- end subroutine diagnose_corr
-
- !-----------------------------------------------------------------------
- function calc_w_corr( wpxp, stdev_w, stdev_x, w_tol, x_tol )
- ! Description:
- ! Compute the correlations of w with the hydrometeors.
-
- ! References:
- ! clubb:ticket:514
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: &
- max_mag_correlation
-
- implicit none
-
- intrinsic :: max
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- stdev_w, & ! standard deviation of w [m/s]
- stdev_x, & ! standard deviation of x [units vary]
- wpxp, & ! Covariances of w with the hydrometeors [units vary]
- w_tol, & ! tolerance for w [m/s]
- x_tol ! tolerance for x [units vary]
-
- real( kind = core_rknd ) :: &
- calc_w_corr
-
- ! --- Begin Code ---
-
- calc_w_corr = wpxp / ( max(stdev_x, x_tol) * max(stdev_w, w_tol) )
-
- ! Make sure the correlation is in [-1,1]
- if ( calc_w_corr < -max_mag_correlation ) then
-
- calc_w_corr = -max_mag_correlation
-
- else if ( calc_w_corr > max_mag_correlation ) then
-
- calc_w_corr = max_mag_correlation
-
- end if
-
- end function calc_w_corr
-
-
- !-----------------------------------------------------------------------
- function calc_varnce( mixt_frac, x1, x2, xm, x1p2, x2p2 )
-
- ! Description:
- ! Calculate the variance xp2 from the components x1, x2.
-
- ! References:
- ! Larson et al. (2011), J. of Geophysical Research, Vol. 116, D00T02,
- ! page 3535
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- mixt_frac, & ! mixing ratio [-]
- x1, & ! first component of the double gaussian [units vary]
- x2, & ! second component of the double gaussian [units vary]
- xm, & ! mean of x [units vary]
- x1p2, & ! variance of the first component [units vary]
- x2p2 ! variance of the second component [units vary]
-
- ! Return Variable
- real( kind = core_rknd ) :: &
- calc_varnce ! variance of x (both components) [units vary]
-
- ! --- Begin Code ---
-
- calc_varnce = mixt_frac * ((x1 - xm)**2 + x1p2) + (1.0_core_rknd - mixt_frac) * ((x2 - xm)**2 + x2p2)
-
- return
- end function calc_varnce
-
- !-----------------------------------------------------------------------
- function calc_mean( mixt_frac, x1, x2 )
-
- ! Description:
- ! Calculate the mean xm from the components x1, x2.
-
- ! References:
- ! Larson et al. (2011), J. of Geophysical Research, Vol. 116, D00T02,
- ! page 3535
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- mixt_frac, & ! mixing ratio [-]
- x1, & ! first component of the double gaussian [units vary]
- x2 ! second component of the double gaussian [units vary]
-
- ! Return Variable
- real( kind = core_rknd ) :: &
- calc_mean ! mean of x (both components) [units vary]
-
- ! --- Begin Code ---
-
- calc_mean = mixt_frac * x1 + (1.0_core_rknd - mixt_frac) * x2
-
- return
- end function calc_mean
-
-end module crmx_diagnose_correlations_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_diffusion.F90 b/src/physics/spcam/crm/CLUBB/crmx_diffusion.F90
deleted file mode 100644
index 75956e82d6..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_diffusion.F90
+++ /dev/null
@@ -1,800 +0,0 @@
-! $Id: diffusion.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_diffusion
-
- ! Description:
- ! Module diffusion computes the eddy diffusion terms for all of the
- ! time-tendency (prognostic) equations in the CLUBB parameterization. Most of
- ! the eddy diffusion terms are solved for completely implicitly, and therefore
- ! become part of the left-hand side of their respective equations. However,
- ! wp2 and wp3 have an option to use a Crank-Nicholson eddy diffusion scheme,
- ! which has both implicit and explicit components.
- !
- ! Function diffusion_zt_lhs handles the eddy diffusion terms for the variables
- ! located at thermodynamic grid levels. These variables are: wp3 and all
- ! hydrometeor species. The variables um and vm also use the Crank-Nicholson
- ! eddy-diffusion scheme for their turbulent advection term.
- !
- ! Function diffusion_zm_lhs handles the eddy diffusion terms for the variables
- ! located at momentum grid levels. The variables are: wprtp, wpthlp, wp2,
- ! rtp2, thlp2, rtpthlp, up2, vp2, wpsclrp, sclrprtp, sclrpthlp, and sclrp2.
-
- implicit none
-
- private ! Default Scope
-
- public :: diffusion_zt_lhs, &
- diffusion_cloud_frac_zt_lhs, &
- diffusion_zm_lhs
-
- contains
-
- !=============================================================================
- pure function diffusion_zt_lhs( K_zm, K_zmm1, nu, &
- invrs_dzmm1, invrs_dzm, &
- invrs_dzt, level ) &
- result( lhs )
-
- ! Description:
- ! Vertical eddy diffusion of var_zt: implicit portion of the code.
- !
- ! The variable "var_zt" stands for a variable that is located at
- ! thermodynamic grid levels.
- !
- ! The d(var_zt)/dt equation contains an eddy diffusion term:
- !
- ! + d [ ( K_zm + nu ) * d(var_zt)/dz ] / dz.
- !
- ! This term is usually solved for completely implicitly, such that:
- !
- ! + d [ ( K_zm + nu ) * d( var_zt(t+1) )/dz ] / dz.
- !
- ! However, when a Crank-Nicholson scheme is used, the eddy diffusion term
- ! has both implicit and explicit components, such that:
- !
- ! + (1/2) * d [ ( K_zm + nu ) * d( var_zt(t+1) )/dz ] / dz
- ! + (1/2) * d [ ( K_zm + nu ) * d( var_zt(t) )/dz ] / dz;
- !
- ! for which the implicit component is:
- !
- ! + (1/2) * d [ ( K_zm + nu ) * d( var_zt(t+1) )/dz ] / dz.
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "+" in front of the term
- ! is changed to a "-".
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(var_zt)/dt equation.
- !
- ! The implicit portion of this term is discretized as follows:
- !
- ! The values of var_zt are found on the thermodynamic levels, while the
- ! values of K_zm are found on the momentum levels. The derivatives (d/dz)
- ! of var_zt are taken over the intermediate momentum levels. At the
- ! intermediate momentum levels, d(var_zt)/dz is multiplied by ( K_zm + nu ).
- ! Then, the derivative of the whole mathematical expression is taken over
- ! the central thermodynamic level, which yields the desired result.
- !
- ! --var_ztp1----------------------------------------------- t(k+1)
- !
- ! ==========d(var_zt)/dz==(K_zm+nu)======================== m(k)
- !
- ! --var_zt-------------------d[(K_zm+nu)*d(var_zt)/dz]/dz-- t(k)
- !
- ! ==========d(var_zt)/dz==(K_zmm1+nu)====================== m(k-1)
- !
- ! --var_ztm1----------------------------------------------- t(k-1)
- !
- ! The vertical indices t(k+1), m(k), t(k), m(k-1), and t(k-1) correspond
- ! with altitudes zt(k+1), zm(k), zt(k), zm(k-1), and zt(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
- ! invrs_dzm(k-1) = 1 / ( zt(k) - zt(k-1) )
- !
- ! Note: This function only computes the general implicit form:
- ! + d [ ( K_zm + nu ) * d( var_zt(t+1) )/dz ] / dz.
- ! For a Crank-Nicholson scheme, the left-hand side result of this
- ! function will have to be multiplied by (1/2). For a
- ! Crank-Nicholson scheme, the right-hand side (explicit) component
- ! needs to be computed by multiplying the left-hand side results by
- ! (1/2), reversing the sign on each left-hand side element, and then
- ! multiplying each element by the appropriate var_zt(t) value from
- ! the appropriate vertical level.
- !
- !
- ! Boundary Conditions:
- !
- ! 1) Zero-flux boundary conditions.
- ! This function is set up to use zero-flux boundary conditions at both
- ! the lower boundary level and the upper boundary level. The flux, F,
- ! is the amount of var_zt flowing normal through the boundary per unit
- ! time per unit surface area. The derivative of the flux effects the
- ! time-tendency of var_zt, such that:
- !
- ! d(var_zt)/dt = -dF/dz.
- !
- ! For the 2nd-order eddy-diffusion term, +d[(K_zm+nu)*d(var_zt)/dz]/dz,
- ! the flux is:
- !
- ! F = -(K_zm+nu)*d(var_zt)/dz.
- !
- ! In order to have zero-flux boundary conditions, the derivative of
- ! var_zt, d(var_zt)/dz, needs to equal 0 at both the lower boundary and
- ! the upper boundary.
- !
- ! In order to discretize the lower boundary condition, consider a new
- ! level outside the model (thermodynamic level 0) just below the lower
- ! boundary level (thermodynamic level 1). The value of var_zt at the
- ! level just outside the model is defined to be the same as the value of
- ! var_zt at the lower boundary level. Therefore, the value of
- ! d(var_zt)/dz between the level just outside the model and the lower
- ! boundary level is 0, satisfying the zero-flux boundary condition. The
- ! other value for d(var_zt)/dz (between thermodynamic level 2 and
- ! thermodynamic level 1) is taken over the intermediate momentum level
- ! (momentum level 1), where it is multiplied by the factor
- ! ( K_zm(1) + nu ). Then, the derivative of the whole expression is
- ! taken over the central thermodynamic level.
- !
- ! -var_zt(2)-------------------------------------------- t(2)
- !
- ! ==========d(var_zt)/dz==(K_zm(1)+nu)================== m(1)
- !
- ! -var_zt(1)---------------d[(K_zm+nu)*d(var_zt)/dz]/dz- t(1) Boundary
- !
- ! [d(var_zt)/dz = 0]
- !
- ! -[var_zt(0) = var_zt(1)]-----(level outside model)---- t(0)
- !
- ! The result is dependent only on values of K_zm found at momentum
- ! level 1 and values of var_zt found at thermodynamic levels 1 and 2.
- ! Thus, it only affects 2 diagonals on the left-hand side matrix.
- !
- ! The same method can be used to discretize the upper boundary by
- ! considering a new level outside the model just above the upper boundary
- ! level.
- !
- ! 2) Fixed-point boundary conditions.
- ! Many equations in the model use fixed-point boundary conditions rather
- ! than zero-flux boundary conditions. This means that the value of
- ! var_zt stays the same over the course of the timestep at the lower
- ! boundary, as well as at the upper boundary.
- !
- ! In order to discretize the boundary conditions for equations requiring
- ! fixed-point boundary conditions, either:
- ! a) in the parent subroutine or function (that calls this function),
- ! loop over all vertical levels from the second-lowest to the
- ! second-highest, ignoring the boundary levels. Then set the values
- ! at the boundary levels in the parent subroutine; or
- ! b) in the parent subroutine or function, loop over all vertical levels
- ! and then overwrite the results at the boundary levels.
- !
- ! Either way, at the boundary levels, an array with a value of 1 at the
- ! main diagonal on the left-hand side and with values of 0 at all other
- ! diagonals on the left-hand side will preserve the right-hand side value
- ! at that level, thus satisfying the fixed-point boundary conditions.
- !
- !
- ! Conservation Properties:
- !
- ! When zero-flux boundary conditions are used, this technique of
- ! discretizing the eddy diffusion term leads to conservative differencing.
- ! When conservative differencing is in place, the column totals for each
- ! column in the left-hand side matrix (for the eddy diffusion term) should
- ! be equal to 0. This ensures that the total amount of the quantity var_zt
- ! over the entire vertical domain is being conserved, meaning that nothing
- ! is lost due to diffusional effects.
- !
- ! To see that this conservation law is satisfied, compute the eddy diffusion
- ! of var_zt and integrate vertically. In discretized matrix notation (where
- ! "i" stands for the matrix column and "j" stands for the matrix row):
- !
- ! 0 = Sum_j Sum_i ( 1/invrs_dzt )_i
- ! ( invrs_dzt * ((K_zm+nu)*invrs_dzm) )_ij (var_zt)_j.
- !
- ! The left-hand side matrix, ( invrs_dzt * ((K_zm+nu)*invrs_dzm) )_ij, is
- ! partially written below. The sum over i in the above equation removes
- ! invrs_dzt everywhere from the matrix below. The sum over j leaves the
- ! column totals that are desired.
- !
- ! Left-hand side matrix contributions from eddy diffusion term; first four
- ! vertical levels:
- !
- ! -------------------------------------------------------------------------->
- !k=1 | +invrs_dzt(k) -invrs_dzt(k) 0
- ! | *(K_zm(k)+nu) *(K_zm(k)+nu)
- ! | *invrs_dzm(k) *invrs_dzm(k)
- ! |
- !k=2 | -invrs_dzt(k) +invrs_dzt(k) -invrs_dzt(k)
- ! | *(K_zm(k-1)+nu) *[ (K_zm(k)+nu) *(K_zm(k)+nu)
- ! | *invrs_dzm(k-1) *invrs_dzm(k) *invrs_dzm(k)
- ! | +(K_zm(k-1)+nu)
- ! | *invrs_dzm(k-1) ]
- ! |
- !k=3 | 0 -invrs_dzt(k) +invrs_dzt(k)
- ! | *(K_zm(k-1)+nu) *[ (K_zm(k)+nu)
- ! | *invrs_dzm(k-1) *invrs_dzm(k)
- ! | +(K_zm(k-1)+nu)
- ! | *invrs_dzm(k-1) ]
- ! |
- !k=4 | 0 0 -invrs_dzt(k)
- ! | *(K_zm(k-1)+nu)
- ! | *invrs_dzm(k-1)
- ! \ /
- !
- ! Note: The superdiagonal term from level 3 and both the main diagonal and
- ! superdiagonal terms from level 4 are not shown on this diagram.
- !
- ! Note: The matrix shown is a tridiagonal matrix. For a band diagonal
- ! matrix (with 5 diagonals), there would be an extra row between each
- ! of the rows shown and an extra column between each of the columns
- ! shown. However, for the purposes of the var_zt eddy diffusion
- ! term, those extra row and column values are all 0, and the
- ! conservation properties of the matrix aren't effected.
- !
- ! If fixed-point boundary conditions are used, the matrix entries at
- ! level 1 (k=1) read: 1 0 0; which means that conservative differencing
- ! is not in play. The total amount of var_zt over the entire vertical
- ! domain is not being conserved, as amounts of var_zt may be fluxed out
- ! through the upper boundary or lower boundary through the effects of
- ! diffusion.
- !
- ! Brian Griffin. April 26, 2008.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2, & ! Thermodynamic main diagonal index.
- km1_tdiag = 3 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- K_zm, & ! Coef. of eddy diffusivity at momentum level (k) [m^2/s]
- K_zmm1, & ! Coef. of eddy diffusivity at momentum level (k-1) [m^2/s
- invrs_dzt, & ! Inverse of grid spacing over thermo. level (k) [1/m]
- invrs_dzm, & ! Inverse of grid spacing over momentum level (k) [1/m]
- invrs_dzmm1 ! Inverse of grid spacing over momentum level (k-1) [1/m]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- nu ! Background constant coef. of eddy diffusivity [m^2/s]
-
- integer, intent(in) :: &
- level ! Thermodynamic level where calculation occurs. [-]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
- if ( level == 1 ) then
-
- ! k = 1 (bottom level); lower boundary level.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) = - invrs_dzt * (K_zm+nu(1)) * invrs_dzm
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) = + invrs_dzt * (K_zm+nu(1)) * invrs_dzm
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) = 0.0_core_rknd
-
-
- elseif ( level > 1 .and. level < gr%nz ) then
-
- ! Most of the interior model; normal conditions.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) = - invrs_dzt * (K_zm+nu(level)) * invrs_dzm
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) = + invrs_dzt * ( (K_zm+nu(level))*invrs_dzm &
- + (K_zmm1+nu(level))*invrs_dzmm1 )
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) = - invrs_dzt * (K_zmm1+nu(level)) * invrs_dzmm1
-
- elseif ( level == gr%nz ) then
-
- ! k = gr%nz (top level); upper boundary level.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) = 0.0_core_rknd
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) = + invrs_dzt * (K_zmm1+nu(gr%nz)) * invrs_dzmm1
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) = - invrs_dzt * (K_zmm1+nu(gr%nz)) * invrs_dzmm1
-
-
- endif
-
- end function diffusion_zt_lhs
-
- !=============================================================================
- pure function diffusion_cloud_frac_zt_lhs &
- ( K_zm, K_zmm1, cloud_frac_zt, cloud_frac_ztm1, &
- cloud_frac_ztp1, cloud_frac_zm, &
- cloud_frac_zmm1, &
- nu, invrs_dzmm1, invrs_dzm, invrs_dzt, level ) &
- result( lhs )
-
- ! Description:
- ! This function adds a weight of cloud fraction to the existing diffusion
- ! function for number concentration variables (e.g. cloud droplet number
- ! concentration). This code should be considered experimental and may
- ! contain bugs.
- ! References:
- ! This algorithm uses equations derived from Guo, et al. 2009.
- !-----------------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! External
- intrinsic :: min
-
- ! Constant parameters
- real( kind = core_rknd ), parameter :: &
- cf_ratio = 10._core_rknd ! Maximum cloud-fraction coefficient applied to Kh_zm
-
- integer, parameter :: &
- kp1_tdiag = 1, & ! Thermodynamic superdiagonal index.
- k_tdiag = 2, & ! Thermodynamic main diagonal index.
- km1_tdiag = 3 ! Thermodynamic subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- K_zm, & ! Coef. of eddy diffusivity at mom. level (k) [m^2/s]
- K_zmm1, & ! Coef. of eddy diffusivity at mom. level (k-1) [m^2/s]
- cloud_frac_zt, & ! Cloud fraction at the thermo. level (k) [-]
- cloud_frac_ztm1, & ! Cloud fraction at the thermo. level (k-1) [-]
- cloud_frac_ztp1, & ! Cloud fraction at the thermo. level (k+1) [-]
- cloud_frac_zm, & ! Cloud fraction at the momentum level (k) [-]
- cloud_frac_zmm1, & ! Cloud fraction at the momentum level (k-1) [-]
- invrs_dzt, & ! Inverse of grid spacing over thermo. lev. (k) [1/m]
- invrs_dzm, & ! Inverse of grid spacing over mom. level (k) [1/m]
- invrs_dzmm1 ! Inverse of grid spacing over mom. level (k-1) [1/m]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- nu ! Background constant coef. of eddy diffusivity [m^2/s]
-
- integer, intent(in) :: &
- level ! Thermodynamic level where calculation occurs. [-]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
- ! ---- Begin Code ----
-
- if ( level == 1 ) then
-
- ! k = 1 (bottom level); lower boundary level.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
-! lhs(kp1_tdiag) = - invrs_dzt &
-! * (K_zm+nu) &
-! * ( cloud_frac_zm / cloud_frac_ztp1 ) * invrs_dzm
- lhs(kp1_tdiag) = - invrs_dzt &
- * (K_zm &
- * min( cloud_frac_zm / cloud_frac_ztp1, cf_ratio ) &
- + nu(1)) * invrs_dzm
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
-! lhs(k_tdiag) = + invrs_dzt &
-! * (K_zm+nu) &
-! * ( cloud_frac_zm / cloud_frac_ztp1 ) * invrs_dzm
- lhs(k_tdiag) = + invrs_dzt &
- * (K_zm &
- * min( cloud_frac_zm / cloud_frac_ztp1, cf_ratio ) &
- + nu(1)) * invrs_dzm
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) = 0.0_core_rknd
-
-
- else if ( level > 1 .and. level < gr%nz ) then
-
- ! Most of the interior model; normal conditions.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
-! lhs(kp1_tdiag) = - invrs_dzt &
-! * (K_zm+nu) &
-! * ( cloud_frac_zm / cloud_frac_ztp1 ) * invrs_dzm
-! lhs(kp1_tdiag) = - invrs_dzt &
-! * (K_zm &
-! * ( cloud_frac_zm / cloud_frac_ztp1 ) &
-! + nu ) * invrs_dzm
- lhs(kp1_tdiag) = - invrs_dzt &
- * (K_zm &
- * min( cloud_frac_zm / cloud_frac_ztp1, cf_ratio ) &
- + nu(level) ) * invrs_dzm
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
-! lhs(k_tdiag) = + invrs_dzt &
-! * ( ((K_zm+nu)*cloud_frac_zm)*invrs_dzm &
-! + ((K_zmm1+nu)*cloud_frac_zmm1)*invrs_dzmm1 ) &
-! / cloud_frac_zt
-! lhs(k_tdiag) = + invrs_dzt &
-! * ( nu*(invrs_dzm+invrs_dzmm1) + &
-! ( ((K_zm*cloud_frac_zm)*invrs_dzm +
-! (K_zmm1*cloud_frac_zmm1)*invrs_dzmm1)&
-! / cloud_frac_zt &
-! ) &
-! )
- lhs(k_tdiag) = + invrs_dzt &
- * ( nu(level)*(invrs_dzm+invrs_dzmm1) + &
- ( K_zm*invrs_dzm* &
- min( cloud_frac_zm / cloud_frac_zt, &
- cf_ratio ) &
- + K_zmm1*invrs_dzmm1* &
- min( cloud_frac_zmm1 / cloud_frac_zt, &
- cf_ratio ) &
- ) &
- )
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
-! lhs(km1_tdiag) = - invrs_dzt * (K_zmm1+nu) * &
-! ( cloud_frac_zmm1 / cloud_frac_ztm1 ) * invrs_dzmm1
- lhs(km1_tdiag) = - invrs_dzt &
- * (K_zmm1 &
- * min( cloud_frac_zmm1 / cloud_frac_ztm1, &
- cf_ratio ) &
- + nu(level) ) * invrs_dzmm1
-
- else if ( level == gr%nz ) then
-
- ! k = gr%nz (top level); upper boundary level.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Thermodynamic superdiagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) = 0.0_core_rknd
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
-! lhs(k_tdiag) = + invrs_dzt &
-! *(K_zmm1+nu) &
-! *( cloud_frac_zmm1 / cloud_frac_ztm1 ) * invrs_dzmm1
- lhs(k_tdiag) = + invrs_dzt &
- * (K_zmm1 &
- * min( cloud_frac_zmm1 / cloud_frac_ztm1, &
- cf_ratio ) &
- + nu(gr%nz)) * invrs_dzmm1
-
- ! Thermodynamic subdiagonal: [ x var_zt(k-1,) ]
-! lhs(km1_tdiag) = - invrs_dzt * (K_zmm1+nu) * &
-! ( cloud_frac_zmm1 / cloud_frac_ztm1 ) * invrs_dzmm1
- lhs(km1_tdiag) = - invrs_dzt &
- * (K_zmm1 &
- * min( cloud_frac_zmm1 / cloud_frac_ztm1, &
- cf_ratio ) &
- + nu(gr%nz)) * invrs_dzmm1
-
- end if
-
- return
- end function diffusion_cloud_frac_zt_lhs
-
- !=============================================================================
- pure function diffusion_zm_lhs( K_zt, K_ztp1, nu, &
- invrs_dztp1, invrs_dzt, &
- invrs_dzm, level ) &
- result( lhs )
-
- ! Description:
- ! Vertical eddy diffusion of var_zm: implicit portion of the code.
- !
- ! The variable "var_zm" stands for a variable that is located at momentum
- ! grid levels.
- !
- ! The d(var_zm)/dt equation contains an eddy diffusion term:
- !
- ! + d [ ( K_zt + nu ) * d(var_zm)/dz ] / dz.
- !
- ! This term is usually solved for completely implicitly, such that:
- !
- ! + d [ ( K_zt + nu ) * d( var_zm(t+1) )/dz ] / dz.
- !
- ! However, when a Crank-Nicholson scheme is used, the eddy diffusion term
- ! has both implicit and explicit components, such that:
- !
- ! + (1/2) * d [ ( K_zt + nu ) * d( var_zm(t+1) )/dz ] / dz
- ! + (1/2) * d [ ( K_zt + nu ) * d( var_zm(t) )/dz ] / dz;
- !
- ! for which the implicit component is:
- !
- ! + (1/2) * d [ ( K_zt + nu ) * d( var_zm(t+1) )/dz ] / dz.
- !
- ! Note: When the implicit term is brought over to the left-hand side,
- ! the sign is reversed and the leading "+" in front of the term
- ! is changed to a "-".
- !
- ! Timestep index (t) stands for the index of the current timestep, while
- ! timestep index (t+1) stands for the index of the next timestep, which is
- ! being advanced to in solving the d(var_zm)/dt equation.
- !
- ! The implicit portion of this term is discretized as follows:
- !
- ! The values of var_zm are found on the momentum levels, while the values of
- ! K_zt are found on the thermodynamic levels. The derivatives (d/dz) of
- ! var_zm are taken over the intermediate thermodynamic levels. At the
- ! intermediate thermodynamic levels, d(var_zm)/dz is multiplied by
- ! ( K_zt + nu ). Then, the derivative of the whole mathematical expression
- ! is taken over the central momentum level, which yields the desired result.
- !
- ! ==var_zmp1=============================================== m(k+1)
- !
- ! ----------d(var_zm)/dz--(K_ztp1+nu)---------------------- t(k+1)
- !
- ! ==var_zm===================d[(K_zt+nu)*d(var_zm)/dz]/dz== m(k)
- !
- ! ----------d(var_zm)/dz--(K_zt+nu)------------------------ t(k)
- !
- ! ==var_zmm1=============================================== m(k-1)
- !
- ! The vertical indices m(k+1), t(k+1), m(k), t(k), and m(k-1) correspond
- ! with altitudes zm(k+1), zt(k+1), zm(k), zt(k), and zm(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! invrs_dzm(k) = 1 / ( zt(k+1) - zt(k) )
- ! invrs_dzt(k+1) = 1 / ( zm(k+1) - zm(k) )
- ! invrs_dzt(k) = 1 / ( zm(k) - zm(k-1) )
- !
- ! Note: This function only computes the general implicit form:
- ! + d [ ( K_zt + nu ) * d( var_zm(t+1) )/dz ] / dz.
- ! For a Crank-Nicholson scheme, the left-hand side result of this
- ! function will have to be multiplied by (1/2). For a
- ! Crank-Nicholson scheme, the right-hand side (explicit) component
- ! needs to be computed by multiplying the left-hand side results by
- ! (1/2), reversing the sign on each left-hand side element, and then
- ! multiplying each element by the appropriate var_zm(t) value from
- ! the appropriate vertical level.
- !
- !
- ! Boundary Conditions:
- !
- ! 1) Zero-flux boundary conditions.
- ! This function is set up to use zero-flux boundary conditions at both
- ! the lower boundary level and the upper boundary level. The flux, F,
- ! is the amount of var_zm flowing normal through the boundary per unit
- ! time per unit surface area. The derivative of the flux effects the
- ! time-tendency of var_zm, such that:
- !
- ! d(var_zm)/dt = -dF/dz.
- !
- ! For the 2nd-order eddy-diffusion term, +d[(K_zt+nu)*d(var_zm)/dz]/dz,
- ! the flux is:
- !
- ! F = -(K_zt+nu)*d(var_zm)/dz.
- !
- ! In order to have zero-flux boundary conditions, the derivative of
- ! var_zm, d(var_zm)/dz, needs to equal 0 at both the lower boundary and
- ! the upper boundary.
- !
- ! In order to discretize the lower boundary condition, consider a new
- ! level outside the model (momentum level 0) just below the lower
- ! boundary level (momentum level 1). The value of var_zm at the level
- ! just outside the model is defined to be the same as the value of var_zm
- ! at the lower boundary level. Therefore, the value of d(var_zm)/dz
- ! between the level just outside the model and the lower boundary level
- ! is 0, satisfying the zero-flux boundary condition. The other value for
- ! d(var_zm)/dz (between momentum level 2 and momentum level 1) is taken
- ! over the intermediate thermodynamic level (thermodynamic level 2),
- ! where it is multiplied by the factor ( K_zt(2) + nu ). Then, the
- ! derivative of the whole expression is taken over the central momentum
- ! level.
- !
- ! =var_zm(2)============================================ m(2)
- !
- ! ----------d(var_zm)/dz==(K_zt(2)+nu)------------------ t(2)
- !
- ! =var_zm(1)===============d[(K_zt+nu)*d(var_zm)/dz]/dz= m(1) Boundary
- !
- ! ----------[d(var_zm)/dz = 0]-------------------------- t(1)
- !
- ! =[var_zm(0) = var_zm(1)]=====(level outside model)==== m(0)
- !
- ! The result is dependent only on values of K_zt found at thermodynamic
- ! level 2 and values of var_zm found at momentum levels 1 and 2. Thus,
- ! it only affects 2 diagonals on the left-hand side matrix.
- !
- ! The same method can be used to discretize the upper boundary by
- ! considering a new level outside the model just above the upper boundary
- ! level.
- !
- ! 2) Fixed-point boundary conditions.
- ! Many equations in the model use fixed-point boundary conditions rather
- ! than zero-flux boundary conditions. This means that the value of
- ! var_zm stays the same over the course of the timestep at the lower
- ! boundary, as well as at the upper boundary.
- !
- ! In order to discretize the boundary conditions for equations requiring
- ! fixed-point boundary conditions, either:
- ! a) in the parent subroutine or function (that calls this function),
- ! loop over all vertical levels from the second-lowest to the
- ! second-highest, ignoring the boundary levels. Then set the values
- ! at the boundary levels in the parent subroutine; or
- ! b) in the parent subroutine or function, loop over all vertical levels
- ! and then overwrite the results at the boundary levels.
- !
- ! Either way, at the boundary levels, an array with a value of 1 at the
- ! main diagonal on the left-hand side and with values of 0 at all other
- ! diagonals on the left-hand side will preserve the right-hand side value
- ! at that level, thus satisfying the fixed-point boundary conditions.
- !
- !
- ! Conservation Properties:
- !
- ! When zero-flux boundary conditions are used, this technique of
- ! discretizing the eddy diffusion term leads to conservative differencing.
- ! When conservative differencing is in place, the column totals for each
- ! column in the left-hand side matrix (for the eddy diffusion term) should
- ! be equal to 0. This ensures that the total amount of the quantity var_zm
- ! over the entire vertical domain is being conserved, meaning that nothing
- ! is lost due to diffusional effects.
- !
- ! To see that this conservation law is satisfied, compute the eddy diffusion
- ! of var_zm and integrate vertically. In discretized matrix notation (where
- ! "i" stands for the matrix column and "j" stands for the matrix row):
- !
- ! 0 = Sum_j Sum_i ( 1/invrs_dzm )_i
- ! ( invrs_dzm * ((K_zt+nu)*invrs_dzt) )_ij (var_zm)_j.
- !
- ! The left-hand side matrix, ( invrs_dzm * ((K_zt+nu)*invrs_dzt) )_ij, is
- ! partially written below. The sum over i in the above equation removes
- ! invrs_dzm everywhere from the matrix below. The sum over j leaves the
- ! column totals that are desired.
- !
- ! Left-hand side matrix contributions from eddy diffusion term; first four
- ! vertical levels:
- !
- ! ---------------------------------------------------------------------->
- !k=1 | +invrs_dzm(k) -invrs_dzm(k) 0
- ! | *(K_zt(k+1)+nu) *(K_zt(k+1)+nu)
- ! | *invrs_dzt(k+1) *invrs_dzt(k+1)
- ! |
- !k=2 | -invrs_dzm(k) +invrs_dzm(k) -invrs_dzm(k)
- ! | *(K_zt(k)+nu) *[ (K_zt(k+1)+nu) *(K_zt(k+1)+nu)
- ! | *invrs_dzt(k) *invrs_dzt(k+1) *invrs_dzt(k+1)
- ! | +(K_zt(k)+nu)
- ! | *invrs_dzt(k) ]
- ! |
- !k=3 | 0 -invrs_dzm(k) +invrs_dzm(k)
- ! | *(K_zt(k)+nu) *[ (K_zt(k+1)+nu)
- ! | *invrs_dzt(k) *invrs_dzt(k+1)
- ! | +(K_zt(k)+nu)
- ! | *invrs_dzt(k) ]
- ! |
- !k=4 | 0 0 -invrs_dzm(k)
- ! | *(K_zt(k)+nu)
- ! | *invrs_dzt(k)
- ! \ /
- !
- ! Note: The superdiagonal term from level 3 and both the main diagonal and
- ! superdiagonal terms from level 4 are not shown on this diagram.
- !
- ! Note: The matrix shown is a tridiagonal matrix. For a band diagonal
- ! matrix (with 5 diagonals), there would be an extra row between each
- ! of the rows shown and an extra column between each of the columns
- ! shown. However, for the purposes of the var_zm eddy diffusion
- ! term, those extra row and column values are all 0, and the
- ! conservation properties of the matrix aren't effected.
- !
- ! If fixed-point boundary conditions are used, the matrix entries at
- ! level 1 (k=1) read: 1 0 0; which means that conservative differencing
- ! is not in play. The total amount of var_zm over the entire vertical
- ! domain is not being conserved, as amounts of var_zm may be fluxed out
- ! through the upper boundary or lower boundary through the effects of
- ! diffusion.
- !
- ! Brian Griffin. April 26, 2008.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp1_mdiag = 1, & ! Momentum superdiagonal index.
- k_mdiag = 2, & ! Momentum main diagonal index.
- km1_mdiag = 3 ! Momentum subdiagonal index.
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- K_zt, & ! Coef. of eddy diffusivity at thermo. level (k) [m^2/s]
- K_ztp1, & ! Coef. of eddy diffusivity at thermo. level (k+1) [m^2/s]
- invrs_dzm, & ! Inverse of grid spacing over momentum level (k) [1/m]
- invrs_dzt, & ! Inverse of grid spacing over thermo. level (k) [1/m]
- invrs_dztp1 ! Inverse of grid spacing over thermo. level (k+1) [1/m]
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- nu ! Background constant coef. of eddy diffusivity [m^2/s]
-
- integer, intent(in) :: &
- level ! Momentum level where calculation occurs. [-]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(3) :: lhs
-
- if ( level == 1 ) then
-
- ! k = 1; lower boundary level at surface.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Momentum superdiagonal: [ x var_zm(k+1,) ]
- lhs(kp1_mdiag) = - invrs_dzm * (K_ztp1+nu(2)) * invrs_dztp1
-
- ! Momentum main diagonal: [ x var_zm(k,) ]
- lhs(k_mdiag) = + invrs_dzm * (K_ztp1+nu(2)) * invrs_dztp1
-
- ! Momentum subdiagonal: [ x var_zm(k-1,) ]
- lhs(km1_mdiag) = 0.0_core_rknd
-
-
- elseif ( level > 1 .and. level < gr%nz ) then
-
- ! Most of the interior model; normal conditions.
-
- ! Momentum superdiagonal: [ x var_zm(k+1,) ]
- lhs(kp1_mdiag) = - invrs_dzm * (K_ztp1+nu(level+1)) * invrs_dztp1
-
- ! Momentum main diagonal: [ x var_zm(k,) ]
- lhs(k_mdiag) = + invrs_dzm * ( (K_ztp1+nu(level+1))*invrs_dztp1 &
- + (K_zt+nu(level))*invrs_dzt )
-
- ! Momentum subdiagonal: [ x var_zm(k-1,) ]
- lhs(km1_mdiag) = - invrs_dzm * (K_zt+nu(level)) * invrs_dzt
-
-
- elseif ( level == gr%nz ) then
-
- ! k = gr%nz (top level); upper boundary level.
- ! Only relevant if zero-flux boundary conditions are used.
-
- ! Momentum superdiagonal: [ x var_zm(k+1,) ]
- lhs(kp1_mdiag) = 0.0_core_rknd
-
- ! Momentum main diagonal: [ x var_zm(k,) ]
- lhs(k_mdiag) = + invrs_dzm * (K_zt+nu(gr%nz)) * invrs_dzt
-
- ! Momentum subdiagonal: [ x var_zm(k-1,) ]
- lhs(km1_mdiag) = - invrs_dzm * (K_zt+nu(gr%nz)) * invrs_dzt
-
-
- endif
-
- end function diffusion_zm_lhs
-
-!===============================================================================
-
-end module crmx_diffusion
diff --git a/src/physics/spcam/crm/CLUBB/crmx_endian.F90 b/src/physics/spcam/crm/CLUBB/crmx_endian.F90
deleted file mode 100644
index 6886f158a1..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_endian.F90
+++ /dev/null
@@ -1,173 +0,0 @@
-!----------------------------------------------------------------------
-! $Id: endian.F90 3784 2009-07-14 21:29:16Z dschanen@uwm.edu $
-
-!----------------------------------------------------------------------
-module crmx_endian
-
-! Description:
-! big_endian and little_endian are parameters set at compile time
-! based on whether the architecture is big or little endian.
-
-! native_4byte_real is a portable byte re-ordering subroutine
-! native_8byte_real is a knock off of the other routine for 8 bytes
-! References:
-! big_endian, little_endian from:
-!
-!----------------------------------------------------------------------
-
- implicit none
-
- interface byte_order_swap
- module procedure native_4byte_real, native_8byte_real
- end interface
-
- public :: big_endian, little_endian, byte_order_swap
- private :: native_4byte_real, native_8byte_real
-
- private ! Default scope
- ! External
- intrinsic :: selected_int_kind, ichar, transfer
-
- ! Parameters
- integer, parameter :: &
- i4 = 4, & ! 4 byte long integer
- ich = ichar( transfer( 1_i4, "a" ) )
-
- logical, parameter :: &
- big_endian = ich == 0, &
- little_endian = .not. big_endian
-
- contains
-
-!-------------------------------------------------------------------------------
-! SUBPROGRAM: native_4byte_real
-!
-! AUTHOR: David Stepaniak, NCAR/CGD/CAS
-! DATE INITIATED: 29 April 2003
-! LAST MODIFIED: 19 April 2005
-!
-! SYNOPSIS: Converts a 32 bit, 4 byte, REAL from big Endian to
-! little Endian, or conversely from little Endian to big
-! Endian.
-!
-! DESCRIPTION: This subprogram allows one to convert a 32 bit, 4 byte,
-! REAL data element that was generated with, say, a big
-! Endian processor (e.g. Sun/sparc, SGI/R10000, etc.) to its
-! equivalent little Endian representation for use on little
-! Endian processors (e.g. PC/Pentium running Linux). The
-! converse, little Endian to big Endian, also holds.
-! This conversion is accomplished by writing the 32 bits of
-! the REAL data element into a generic 32 bit INTEGER space
-! with the TRANSFER intrinsic, reordering the 4 bytes with
-! the MVBITS intrinsic, and writing the reordered bytes into
-! a new 32 bit REAL data element, again with the TRANSFER
-! intrinsic. The following schematic illustrates the
-! reordering process
-!
-!
-! -------- -------- -------- --------
-! | D | | C | | B | | A | 4 Bytes
-! -------- -------- -------- --------
-! |
-! -> 1 bit
-! ||
-! MVBITS
-! ||
-! \/
-!
-! -------- -------- -------- --------
-! | A | | B | | C | | D | 4 Bytes
-! -------- -------- -------- --------
-! | | | |
-! 24 16 8 0 <- bit
-! position
-!
-! INPUT: realIn, a single 32 bit, 4 byte REAL data element.
-! OUTPUT: realOut, a single 32 bit, 4 byte REAL data element, with
-! reverse byte order to that of realIn.
-! RESTRICTION: It is assumed that the default REAL data element is
-! 32 bits / 4 bytes.
-!
-!-----------------------------------------------------------------------
- SUBROUTINE native_4byte_real( realInOut )
-
- IMPLICIT NONE
-
- REAL(KIND=4), INTENT(INOUT):: realInOut ! a single 32 bit, 4 byte
- ! REAL data element
-! Modified 8/1/05
-! I found transfer does not work on pgf90 when -r8 is used and the mold
-! is a literal constant real; Changed the mold "0.0" to "readInOut"
-! -dschanen
-!
-! REAL, INTENT(IN):: realInOut
-! REAL, INTENT(OUT) :: realOut
-! ! a single 32 bit, 4 byte
-! ! REAL data element, with
-! ! reverse byte order to
-! ! that of realIn
-!----------------------------------------------------------------------
-! Local variables (generic 32 bit INTEGER spaces):
-
- INTEGER(KIND=4) :: i_element
- INTEGER(KIND=4) :: i_element_br
-!----------------------------------------------------------------------
-! Transfer 32 bits of realIn to generic 32 bit INTEGER space:
- i_element = TRANSFER( realInOut, i_element )
-!----------------------------------------------------------------------
-! Reverse order of 4 bytes in 32 bit INTEGER space:
- CALL MVBITS( i_element, 24, 8, i_element_br, 0 )
- CALL MVBITS( i_element, 16, 8, i_element_br, 8 )
- CALL MVBITS( i_element, 8, 8, i_element_br, 16 )
- CALL MVBITS( i_element, 0, 8, i_element_br, 24 )
-!----------------------------------------------------------------------
-! Transfer reversed order bytes to 32 bit REAL space (realOut):
- realInOut = TRANSFER( i_element_br, realInOut )
-
- RETURN
- END SUBROUTINE native_4byte_real
-
-!-------------------------------------------------------------------------------
- subroutine native_8byte_real( realInOut )
-
-! Description:
-! This is just a modification of the above routine for 64 bit data
-!-------------------------------------------------------------------------------
-
- implicit none
-
- ! External
- intrinsic :: mvbits, transfer
-
- real(kind=8), intent(inout) :: realInOut ! a single 64 bit, 8 byte
- ! REAL data element
- ! Local variables (generic 64 bit INTEGER spaces):
-
- integer(kind=8) :: i_element
- integer(kind=8) :: i_element_br
-
-!-------------------------------------------------------------------------------
-
- ! Transfer 64 bits of realIn to generic 64 bit INTEGER space:
- i_element = transfer( realInOut, i_element )
-
- ! Reverse order of 8 bytes in 64 bit INTEGER space:
- call mvbits( i_element, 56, 8, i_element_br, 0 )
- call mvbits( i_element, 48, 8, i_element_br, 8 )
- call mvbits( i_element, 40, 8, i_element_br, 16 )
- call mvbits( i_element, 32, 8, i_element_br, 24 )
- call mvbits( i_element, 24, 8, i_element_br, 32 )
- call mvbits( i_element, 16, 8, i_element_br, 40 )
- call mvbits( i_element, 8, 8, i_element_br, 48 )
- call mvbits( i_element, 0, 8, i_element_br, 56 )
-
- ! Transfer reversed order bytes to 64 bit REAL space (realOut):
- realInOut = transfer( i_element_br, realInOut )
-
- return
- end subroutine native_8byte_real
-!-------------------------------------------------------------------------------
-
-end module crmx_endian
-
-!-------------------------------------------------------------------------------
diff --git a/src/physics/spcam/crm/CLUBB/crmx_error_code.F90 b/src/physics/spcam/crm/CLUBB/crmx_error_code.F90
deleted file mode 100644
index bddf1c39b2..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_error_code.F90
+++ /dev/null
@@ -1,227 +0,0 @@
-!-------------------------------------------------------------------------------
-! $Id: error_code.F90 5906 2012-08-10 23:20:05Z dschanen@uwm.edu $
-!-------------------------------------------------------------------------------
-
-module crmx_error_code
-
-! Description:
-! Since f90/95 lacks enumeration, we're stuck numbering each
-! error code by hand like this.
-
-! We are "enumerating" error codes to be used with CLUBB. Adding
-! additional codes is as simple adding an additional integer
-! parameter. The error codes are ranked by severity, the higher
-! number being more servere. When two errors occur, assign the
-! most servere to the output.
-
-! This code also handles subroutines related to debug_level. See
-! the 'set_clubb_debug_level' description for more detail.
-
-! References:
-! None
-!-------------------------------------------------------------------------------
-
- implicit none
-
- private ! Default Scope
-
- public :: &
- reportError, &
- fatal_error, &
- lapack_error, &
- clubb_at_least_debug_level, &
- set_clubb_debug_level, &
- clubb_debug
-
- private :: clubb_debug_level
-
- ! Model-Wide Debug Level
- integer, save :: clubb_debug_level = 0
-
-!$omp threadprivate(clubb_debug_level)
-
- ! Error Code Values
- integer, parameter, public :: &
- clubb_no_error = 0, &
- clubb_var_less_than_zero = 1, &
- clubb_var_equals_NaN = 2, &
- clubb_singular_matrix = 3, &
- clubb_bad_lapack_arg = 4, &
- clubb_rtm_level_not_found = 5, &
- clubb_var_out_of_bounds = 6, &
- clubb_var_out_of_range = 7
-
- contains
-
-!-------------------------------------------------------------------------------
- subroutine reportError( err_code )
-!
-! Description:
-! Reports meaning of error code to console.
-!
-!-------------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- implicit none
-
- ! Input Variable
- integer, intent(in) :: err_code ! Error Code being examined
-
- ! ---- Begin Code ----
-
- select case ( err_code )
-
- case ( clubb_no_error )
- write(fstderr,*) "No errors reported."
-
- case ( clubb_var_less_than_zero )
- write(fstderr,*) "Variable in CLUBB is less than zero."
-
- case ( clubb_singular_matrix )
- write(fstderr,*) "Singular Matrix in CLUBB."
-
- case ( clubb_var_equals_NaN )
- write(fstderr,*) "Variable in CLUBB is NaN."
-
- case ( clubb_bad_lapack_arg )
- write(fstderr,*) "Argument passed to a LAPACK procedure is invalid."
-
- case ( clubb_rtm_level_not_found )
- write(fstderr,*) "rtm level not found"
-
- case ( clubb_var_out_of_bounds )
- write(fstderr,*) "Input variable is out of bounds."
-
- case ( clubb_var_out_of_range )
- write(fstderr,*) "A CLUBB variable had a value outside the valid range."
-
- case default
- write(fstderr,*) "Unknown error: ", err_code
-
- end select
-
- return
- end subroutine reportError
-!-------------------------------------------------------------------------------
- elemental function lapack_error( err_code )
-!
-! Description:
-! Checks to see if the err_code is equal to one
-! caused by an error encountered using LAPACK.
-! Reference:
-! None
-!-------------------------------------------------------------------------------
- implicit none
-
- ! Input variable
- integer,intent(in) :: err_code ! Error Code being examined
-
- ! Output variable
- logical :: lapack_error
-
- ! ---- Begin Code ----
-
- lapack_error = (err_code == clubb_singular_matrix .or. &
- err_code == clubb_bad_lapack_arg )
-
- return
- end function lapack_error
-
-!-------------------------------------------------------------------------------
- elemental function fatal_error( err_code )
-!
-! Description: Checks to see if the err_code is one that usually
-! causes an exit in other parts of CLUBB.
-! References:
-! None
-!-------------------------------------------------------------------------------
- implicit none
-
- ! Input Variable
- integer, intent(in) :: err_code ! Error Code being examined
-
- ! Output variable
- logical :: fatal_error
-
- ! ---- Begin Code ----
-
- fatal_error = err_code /= clubb_no_error .and. &
- err_code /= clubb_var_less_than_zero
- return
- end function fatal_error
-
-!------------------------------------------------------------------
- logical function clubb_at_least_debug_level( level )
-!
-! Description:
-! Checks to see if clubb has been set to a specified debug level
-!------------------------------------------------------------------
- implicit none
-
- ! Input variable
- integer, intent(in) :: level ! The debug level being checked against the current setting
-
- ! ---- Begin Code ----
-
- clubb_at_least_debug_level = ( level <= clubb_debug_level )
-
- return
- end function clubb_at_least_debug_level
-
-!-------------------------------------------------------------------------------
- subroutine set_clubb_debug_level( level )
-!
-! Description:
-! Accessor for clubb_debug_level
-!
-! 0 => Print no debug messages to the screen
-! 1 => Print lightweight debug messages, e.g. print statements
-! 2 => Print debug messages that require extra testing,
-! e.g. checks for NaNs and spurious negative values.
-! References:
-! None
-!-------------------------------------------------------------------------------
- implicit none
-
- ! Input variable
- integer, intent(in) :: level ! The debug level being checked against the current setting
-
- ! ---- Begin Code ----
-
- clubb_debug_level = level
-
- return
- end subroutine set_clubb_debug_level
-
-!-------------------------------------------------------------------------------
- subroutine clubb_debug( level, str )
-!
-! Description:
-! Prints a message to file unit fstderr if the level is greater
-! than or equal to the current debug level.
-!-------------------------------------------------------------------------------
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- implicit none
-
- ! Input Variable(s)
-
- character(len=*), intent(in) :: str ! The message being reported
-
- ! The debug level being checked against the current setting
- integer, intent(in) :: level
-
- ! ---- Begin Code ----
-
- if ( level <= clubb_debug_level ) then
- write(fstderr,*) str
- end if
-
- return
- end subroutine clubb_debug
-
-end module crmx_error_code
-!-------------------------------------------------------------------------------
diff --git a/src/physics/spcam/crm/CLUBB/crmx_extrapolation.F90 b/src/physics/spcam/crm/CLUBB/crmx_extrapolation.F90
deleted file mode 100644
index 38c4837bd9..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_extrapolation.F90
+++ /dev/null
@@ -1,90 +0,0 @@
-!$Id: extrapolation.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-module crmx_extrapolation
-
- implicit none
-
- public :: lin_ext_zm_bottom, lin_ext_zt_bottom
-
- private ! Default scope
-
- contains
-!===============================================================================
- pure function lin_ext_zm_bottom( var_zmp2, var_zmp1, &
- zmp2, zmp1, zm ) &
- result( var_zm )
-
- ! Description:
- ! This function computes the value of a momentum-level variable at a bottom
- ! grid level by using a linear extension of the values of the variable at
- ! the two levels immediately above the level where the result value is
- ! needed.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- var_zmp2, & ! Momentum level variable at level (k+2) [units vary]
- var_zmp1, & ! Momentum level variable at level (k+1) [units vary]
- zmp2, & ! Altitude at momentum level (k+2) [m]
- zmp1, & ! Altitude at momentum level (k+1) [m]
- zm ! Altitude at momentum level (k) [m]
-
- ! Return Variable
- real( kind = core_rknd ) :: var_zm ! Momentum level variable at level (k) [units vary]
-
- ! ---- Begin Code -----
-
- var_zm = ( ( var_zmp2 - var_zmp1 ) / ( zmp2 - zmp1 ) ) &
- * ( zm - zmp1 ) + var_zmp1
-
- return
- end function lin_ext_zm_bottom
-
-!===============================================================================
- pure function lin_ext_zt_bottom( var_ztp2, var_ztp1, &
- ztp2, ztp1, zt ) &
- result( var_zt )
-
- ! Description:
- ! This function computes the value of a thermodynamic-level variable at a
- ! bottom grid level by using a linear extension of the values of the
- ! variable at the two levels immediately above the level where the result
- ! value is needed.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- var_ztp2, & ! Thermodynamic level variable at level (k+2) [units vary]
- var_ztp1, & ! Thermodynamic level variable at level (k+1) [units vary]
- ztp2, & ! Altitude at thermodynamic level (k+2) [m]
- ztp1, & ! Altitude at thermodynamic level (k+1) [m]
- zt ! Altitude at thermodynamic level (k) [m]
-
- ! Return Variable
- real( kind = core_rknd ) :: var_zt ! Thermodynamic level variable at level (k) [units vary]
-
- ! ---- Begin Code -----
-
- var_zt = ( ( var_ztp2 - var_ztp1 ) / ( ztp2 - ztp1 ) ) &
- * ( zt - ztp1 ) + var_ztp1
-
- return
- end function lin_ext_zt_bottom
-
-end module crmx_extrapolation
diff --git a/src/physics/spcam/crm/CLUBB/crmx_file_functions.F90 b/src/physics/spcam/crm/CLUBB/crmx_file_functions.F90
deleted file mode 100644
index 82d1eb1d10..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_file_functions.F90
+++ /dev/null
@@ -1,156 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: file_functions.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_file_functions
-
- implicit none
-
- public :: file_read_1d, file_read_2d
-
- private ! Default Scope
-
- contains
-
-!===============================================================================
- subroutine file_read_1d( file_unit, path_and_filename, &
- num_datapts, entries_per_line, variable )
-
-! Description:
-! This subroutine reads in values from a data file with a number of
-! rows and a declared number of columns (entries_per_line) of data.
-! It reads in the data in the form of:
-! 1 ==> (row 1, column 1); 2 ==> (row 1, column 2); etc.
-!
-! Example: a diagram of a data file with 18 total data points
-! (DP1 to DP18), with 4 data points per row.
-!
-! i = 1 i = 2 i = 3 i = 4
-! ---------------------------------------
-! k = 1 | DP1 DP2 DP3 DP4
-! |
-! k = 2 | DP5 DP6 DP7 DP8
-! |
-! k = 3 | DP9 DP10 DP11 DP12
-! |
-! k = 4 | DP13 DP14 DP15 DP16
-! |
-! k = 5 | DP17 DP18
-!
-! See Michael Falk's comments below for more information.
-!-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: fstderr ! Constant(s)
-
- implicit none
-
- integer, intent(in) :: &
- file_unit, & ! Unit number of file being read.
- num_datapts, & ! Total number of data points being read in.
- entries_per_line ! Number of data points
- ! on one line of the file being read.
-
- character(*), intent(in) :: &
- path_and_filename ! Path to file and filename of file being read.
-
- real( kind = core_rknd ), dimension(num_datapts), intent(out) :: &
- variable ! Data values output into variable
-
- integer :: k ! Data file row number.
- integer :: i ! Data file column number.
- integer :: ierr
-
- ! ---- Begin Code ----
-
- ! Open data file.
- open( unit=file_unit, file=path_and_filename, action='read', status='old', &
- iostat=ierr )
- if ( ierr /= 0 ) then
- write(fstderr,*) "CLUBB encountered an error trying to open "//path_and_filename
- stop "Error opening forcings file"
- end if
-
- ! Michael Falk wrote this routine to read data files in a particular format for mpace_a.
- ! Each line has a specific number of values, until the last line in the file, which
- ! has the last few values and then ends. This reads the correct number of values on
- ! each line. 24 September 2007
-
- ! Loop over each full line of the input file.
- do k = 1, (num_datapts/entries_per_line), 1
- read(file_unit,*) ( variable( ((k-1)*entries_per_line) + i ), &
- i=1,entries_per_line )
- enddo
- ! Read any partial line remaining.
- if ( mod(num_datapts,entries_per_line) /= 0 ) then
- k = (num_datapts/entries_per_line)
- read(file_unit,*) ( variable( (k*entries_per_line) + i ), &
- i=1,(mod(num_datapts,entries_per_line)) )
- endif
-
- ! Close data file.
- close( file_unit )
-
- return
-
- end subroutine file_read_1d
-
-!===============================================================================
- subroutine file_read_2d( device, file_path, file_dimension1, &
- file_dimension2, file_per_line, variable )
-
-! Description:
-! Michael Falk wrote this routine to read data files in a particular format for mpace_a.
-! The 2d mpace_a files list the (file_dimension2) values on a given vertical level, then
-! moves to the next level to list its values.
-! Each line has a specific number of values, until the last line on a level, which
-! is short-- it has the last few values and then a line break. The next line, beginning
-! the next level, is full-sized again. 24 September 2007
-!
-! References:
-! None
-!-------------------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- integer, intent(in) :: &
- device, &
- file_dimension1, &
- file_dimension2, &
- file_per_line
-
- character(*), intent(in) :: &
- file_path
-
- real( kind = core_rknd ), dimension(file_dimension1,file_dimension2), intent(out) :: &
- variable
-
- integer i, j, k
-
- ! ---- Begin Code ----
-
- variable = -999._core_rknd ! Initialize to nonsense values
-
- open(device,file=file_path,action='read')
-
- do k=1,(file_dimension1) ! For each level in the data file,
- do j=0,((file_dimension2/file_per_line)-1)
- read(device,*) (variable(k,(j*file_per_line)+i), & ! read file_per_line values in,
- i=1,file_per_line)
- end do
- read (device,*) (variable(k,(j*file_per_line)+i), & ! then read the partial line
- i=1,(mod(file_dimension2,file_per_line)))
- end do ! and then start over at the next level.
-
- close(device)
-
- return
- end subroutine file_read_2d
-
-!===============================================================================
-
-end module crmx_file_functions
diff --git a/src/physics/spcam/crm/CLUBB/crmx_fill_holes.F90 b/src/physics/spcam/crm/CLUBB/crmx_fill_holes.F90
deleted file mode 100644
index 8e17d3bc53..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_fill_holes.F90
+++ /dev/null
@@ -1,487 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: fill_holes.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_fill_holes
-
- implicit none
-
- public :: fill_holes_driver, &
- vertical_avg, &
- vertical_integral
-
- private :: fill_holes_multiplicative
-
- private ! Set Default Scope
-
- contains
-
- !=============================================================================
- subroutine fill_holes_driver( num_pts, threshold, field_grid, &
- rho_ds, rho_ds_zm, &
- field )
-
- ! Description:
- ! This subroutine clips values of 'field' that are below 'threshold' as much
- ! as possible (i.e. "fills holes"), but conserves the total integrated mass
- ! of 'field'. This prevents clipping from acting as a spurious source.
- !
- ! Mass is conserved by reducing the clipped field everywhere by a constant
- ! multiplicative coefficient.
- !
- ! This subroutine does not guarantee that the clipped field will exceed
- ! threshold everywhere; blunt clipping is needed for that.
-
- ! References:
- ! ``Numerical Methods for Wave Equations in Geophysical Fluid
- ! Dynamics'', Durran (1999), p. 292.
- !-----------------------------------------------------------------------
-
- use crmx_grid_class, only: &
- gr ! Variable
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- integer, intent(in) :: &
- num_pts ! The number of points on either side of the hole;
- ! Mass is drawn from these points to fill the hole. []
-
- real( kind = core_rknd ), intent(in) :: &
- threshold ! A threshold (e.g. w_tol*w_tol) below which field must not
- ! fall [Units vary; same as field]
-
- character(len=2), intent(in) :: &
- field_grid ! The grid of the field, either zt or zm
-
- real( kind = core_rknd ), dimension(gr%nz), intent(in) :: &
- rho_ds, & ! Dry, static density on thermodynamic levels [kg/m^3]
- rho_ds_zm ! Dry, static density on momentum levels [kg/m^3]
-
- ! Input/Output variable
- real( kind = core_rknd ), dimension(gr%nz), intent(inout) :: &
- field ! The field (e.g. wp2) that contains holes [Units same as threshold]
-
- ! Local Variables
- integer :: &
- k, & ! Loop index for absolute grid level []
- begin_idx, & ! Lower grid level of local hole-filling range []
- end_idx, & ! Upper grid level of local hole-filling range []
- upper_hf_level ! Upper grid level of global hole-filling range []
-
- !-----------------------------------------------------------------------
-
- ! Check whether any holes exist in the entire profile.
- ! The lowest level (k=1) should not be included, as the hole-filling scheme
- ! should not alter the set value of 'field' at the surface (for momentum
- ! level variables), or consider the value of 'field' at a level below the
- ! surface (for thermodynamic level variables). For momentum level variables
- ! only, the hole-filling scheme should not alter the set value of 'field' at
- ! the upper boundary level (k=gr%nz).
-
- if ( field_grid == "zt" ) then
- ! 'field' is on the zt (thermodynamic level) grid
- upper_hf_level = gr%nz
- elseif ( field_grid == "zm" ) then
- ! 'field' is on the zm (momentum level) grid
- upper_hf_level = gr%nz-1
- endif
-
- if ( any( field( 2:upper_hf_level ) < threshold ) ) then
-
- ! Make one pass up the profile, filling holes as much as we can using
- ! nearby mass.
- ! The lowest level (k=1) should not be included in the loop, as the
- ! hole-filling scheme should not alter the set value of 'field' at the
- ! surface (for momentum level variables), or consider the value of
- ! 'field' at a level below the surface (for thermodynamic level
- ! variables). For momentum level variables only, the hole-filling scheme
- ! should not alter the set value of 'field' at the upper boundary
- ! level (k=gr%nz).
- do k = 2+num_pts, upper_hf_level-num_pts, 1
-
- begin_idx = k - num_pts
- end_idx = k + num_pts
-
- if ( any( field( begin_idx:end_idx ) < threshold ) ) then
-
- ! 'field' is on the zt (thermodynamic level) grid
- if ( field_grid == "zt" ) then
- call fill_holes_multiplicative &
- ( begin_idx, end_idx, threshold, &
- rho_ds(begin_idx:end_idx), gr%invrs_dzt(begin_idx:end_idx), &
- field(begin_idx:end_idx) )
-
- ! 'field' is on the zm (momentum level) grid
- elseif ( field_grid == "zm" ) then
- call fill_holes_multiplicative &
- ( begin_idx, end_idx, threshold, &
- rho_ds_zm(begin_idx:end_idx), gr%invrs_dzm(begin_idx:end_idx), &
- field(begin_idx:end_idx) )
- endif
-
- endif
-
- enddo
-
- ! Fill holes globally, to maximize the chance that all holes are filled.
- ! The lowest level (k=1) should not be included, as the hole-filling
- ! scheme should not alter the set value of 'field' at the surface (for
- ! momentum level variables), or consider the value of 'field' at a level
- ! below the surface (for thermodynamic level variables). For momentum
- ! level variables only, the hole-filling scheme should not alter the set
- ! value of 'field' at the upper boundary level (k=gr%nz).
- if ( any( field( 2:upper_hf_level ) < threshold ) ) then
-
- ! 'field' is on the zt (thermodynamic level) grid
- if ( field_grid == "zt" ) then
- call fill_holes_multiplicative &
- ( 2, upper_hf_level, threshold, &
- rho_ds(2:upper_hf_level), gr%invrs_dzt(2:upper_hf_level), &
- field(2:upper_hf_level) )
-
- ! 'field' is on the zm (momentum level) grid
- elseif ( field_grid == "zm" ) then
- call fill_holes_multiplicative &
- ( 2, upper_hf_level, threshold, &
- rho_ds_zm(2:upper_hf_level), gr%invrs_dzm(2:upper_hf_level), &
- field(2:upper_hf_level) )
- endif
-
- endif
-
- endif ! End overall check for existence of holes
-
- return
-
- end subroutine fill_holes_driver
-
- !=============================================================================
- subroutine fill_holes_multiplicative &
- ( begin_idx, end_idx, threshold, &
- rho, invrs_dz, &
- field )
-
- ! Description:
- ! This subroutine clips values of 'field' that are below 'threshold' as much
- ! as possible (i.e. "fills holes"), but conserves the total integrated mass
- ! of 'field'. This prevents clipping from acting as a spurious source.
- !
- ! Mass is conserved by reducing the clipped field everywhere by a constant
- ! multiplicative coefficient.
- !
- ! This subroutine does not guarantee that the clipped field will exceed
- ! threshold everywhere; blunt clipping is needed for that.
-
- ! References:
- ! ``Numerical Methods for Wave Equations in Geophysical Fluid
- ! Dynamics", Durran (1999), p. 292.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- integer, intent(in) :: &
- begin_idx, & ! The beginning index (e.g. k=2) of the range of hole-filling
- end_idx ! The end index (e.g. k=gr%nz) of the range of hole-filling
-
- real( kind = core_rknd ), intent(in) :: &
- threshold ! A threshold (e.g. w_tol*w_tol) below which field must not fall
- ! [Units vary; same as field]
-
- real( kind = core_rknd ), dimension(end_idx-begin_idx+1), intent(in) :: &
- rho, & ! Dry, static density on either thermodynamic or momentum levels [kg/m^3]
- invrs_dz ! Reciprocal of thermodynamic or momentum level thickness depending on whether
- ! we're on zt or zm grid.
-
- ! Input/Output variable
- real( kind = core_rknd ), dimension(end_idx-begin_idx+1), intent(inout) :: &
- field ! The field (e.g. wp2) that contains holes
- ! [Units same as threshold]
-
- ! Local Variables
- real( kind = core_rknd ), dimension(end_idx-begin_idx+1) :: &
- field_clipped ! The raw field (e.g. wp2) that contains no holes
- ! [Units same as threshold]
-
- real( kind = core_rknd ) :: &
- field_avg, & ! Vertical average of field [Units of field]
- field_clipped_avg, & ! Vertical average of clipped field [Units of field]
- mass_fraction ! Coefficient that multiplies clipped field
- ! in order to conserve mass. []
-
- !-----------------------------------------------------------------------
-
- ! Compute the field's vertical average, which we must conserve.
- field_avg = vertical_avg( (end_idx-begin_idx+1), rho, &
- field, invrs_dz )
-
- ! Clip small or negative values from field.
- if ( field_avg >= threshold ) then
- ! We know we can fill in holes completely
- field_clipped = max( threshold, field )
- else
- ! We can only fill in holes partly;
- ! to do so, we remove all mass above threshold.
- field_clipped = min( threshold, field )
- endif
-
- ! Compute the clipped field's vertical integral.
- ! clipped_total_mass >= original_total_mass
- field_clipped_avg = vertical_avg( (end_idx-begin_idx+1), rho, &
- field_clipped, invrs_dz )
-
- ! If the difference between the field_clipped_avg and the threshold is so
- ! small that it falls within numerical round-off, return to the parent
- ! subroutine without altering the field in order to avoid divide-by-zero
- ! error.
- !if ( abs(field_clipped_avg - threshold) &
- ! < threshold*epsilon(threshold) ) then
- if ( abs(field_clipped_avg - threshold) == 0.0_core_rknd ) then
- return
- endif
-
- ! Compute coefficient that makes the clipped field have the same mass as the
- ! original field. We should always have mass_fraction > 0.
- mass_fraction = ( field_avg - threshold ) / &
- ( field_clipped_avg - threshold )
-
- ! Output normalized, filled field
- field = mass_fraction * ( field_clipped - threshold ) &
- + threshold
-
-
- return
-
- end subroutine fill_holes_multiplicative
-
- !=============================================================================
- function vertical_avg( total_idx, rho_ds, &
- field, invrs_dz )
-
- ! Description:
- ! Computes the density-weighted vertical average of a field.
- !
- ! The average value of a function, f, over a set domain, [a,b], is
- ! calculated by the equation:
- !
- ! f_avg = ( INT(a:b) f*g ) / ( INT(a:b) g );
- !
- ! as long as f is continous and g is nonnegative and integrable. Therefore,
- ! the density-weighted (by dry, static, base-static density) vertical
- ! average value of any model field, x, is calculated by the equation:
- !
- ! x_avg|_z = ( INT(z_bot:z_top) x rho_ds dz )
- ! / ( INT(z_bot:z_top) rho_ds dz );
- !
- ! where z_bot is the bottom of the vertical domain, and z_top is the top of
- ! the vertical domain.
- !
- ! This calculation is done slightly differently depending on whether x is a
- ! thermodynamic-level or a momentum-level variable.
- !
- ! Thermodynamic-level computation:
-
- !
- ! For numerical purposes, INT(z_bot:z_top) x rho_ds dz, which is the
- ! numerator integral, is calculated as:
- !
- ! SUM(k_bot:k_top) x(k) rho_ds(k) delta_z(k);
- !
- ! where k is the index of the given thermodynamic level, x and rho_ds are
- ! both thermodynamic-level variables, and delta_z(k) = zm(k) - zm(k-1). The
- ! indices k_bot and k_top are the indices of the respective lower and upper
- ! thermodynamic levels involved in the integration.
- !
- ! Likewise, INT(z_bot:z_top) rho_ds dz, which is the denominator integral,
- ! is calculated as:
- !
- ! SUM(k_bot:k_top) rho_ds(k) delta_z(k).
- !
- ! The first (k=1) thermodynamic level is below ground (or below the
- ! official lower boundary at the first momentum level), so it should not
- ! count in a vertical average, whether that vertical average is used for
- ! the hole-filling scheme or for statistical purposes. Begin no lower
- ! than level k=2, which is the first thermodynamic level above ground (or
- ! above the model lower boundary).
- !
- ! For cases where hole-filling over the entire (global) vertical domain
- ! is desired, or where statistics over the entire (global) vertical
- ! domain are desired, the lower (thermodynamic-level) index of k = 2 and
- ! the upper (thermodynamic-level) index of k = gr%nz, means that the
- ! overall vertical domain will be gr%zm(gr%nz) - gr%zm(1).
- !
- !
- ! Momentum-level computation:
- !
- ! For numerical purposes, INT(z_bot:z_top) x rho_ds dz, which is the
- ! numerator integral, is calculated as:
- !
- ! SUM(k_bot:k_top) x(k) rho_ds(k) delta_z(k);
- !
- ! where k is the index of the given momentum level, x and rho_ds are both
- ! momentum-level variables, and delta_z(k) = zt(k+1) - zt(k). The indices
- ! k_bot and k_top are the indices of the respective lower and upper momentum
- ! levels involved in the integration.
- !
- ! Likewise, INT(z_bot:z_top) rho_ds dz, which is the denominator integral,
- ! is calculated as:
- !
- ! SUM(k_bot:k_top) rho_ds(k) delta_z(k).
- !
- ! The first (k=1) momentum level is right at ground level (or right at
- ! the official lower boundary). The momentum level variables that call
- ! the hole-filling scheme have set values at the surface (or lower
- ! boundary), and those set values should not be changed. Therefore, the
- ! vertical average (for purposes of hole-filling) should not include the
- ! surface level (or lower boundary level). For hole-filling purposes,
- ! begin no lower than level k=2, which is the second momentum level above
- ! ground (or above the model lower boundary). Likewise, the value at the
- ! model upper boundary (k=gr%nz) is also set for momentum level
- ! variables. That value should also not be changed.
- !
- ! However, this function is also used to keep track (for statistical
- ! purposes) of the vertical average of certain variables. In that case,
- ! the vertical average needs to be taken over the entire vertical domain
- ! (level 1 to level gr%nz).
- !
- !
- ! In both the thermodynamic-level computation and the momentum-level
- ! computation, the numerator integral is divided by the denominator integral
- ! in order to find the average value (over the vertical domain) of x.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- integer, intent(in) :: &
- total_idx ! The total numer of indices within the range of averaging
-
- real( kind = core_rknd ), dimension(total_idx), intent(in) :: &
- rho_ds, & ! Dry, static density on either thermodynamic or momentum levels [kg/m^3]
- field, & ! The field (e.g. wp2) to be vertically averaged [Units vary]
- invrs_dz ! Reciprocal of thermodynamic or momentum level thickness [1/m]
- ! depending on whether we're on zt or zm grid.
- ! Note: The rho_ds and field points need to be arranged from
- ! lowest to highest in altitude, with rho_ds(1) and
- ! field(1) actually their respective values at level k = 1.
-
- ! Output variable
- real( kind = core_rknd ) :: &
- vertical_avg ! Vertical average of field [Units of field]
-
- ! Local variables
- real( kind = core_rknd ) :: &
- numer_integral, & ! Integral in the numerator (see description)
- denom_integral ! Integral in the denominator (see description)
-
- real( kind = core_rknd ), dimension(total_idx) :: &
- denom_field ! When computing the vertical integral in the denominator
- ! there is no field variable, so create a "dummy" variable
- ! with value of 1 to pass as an argument
-
- !-----------------------------------------------------------------------
-
- ! Fill array with 1's (see variable description)
- denom_field = 1.0_core_rknd
-
- ! Initializing vertical_avg to avoid a compiler warning.
- vertical_avg = 0.0_core_rknd
-
-
- ! Compute the numerator integral.
- ! Multiply the variable 'field' at level k by rho_ds at level k and by
- ! the level thickness at level k. Then, sum over all vertical levels.
- ! Note: The level thickness at level k is the distance between either
- ! momentum level k and momentum level k-1, or
- ! thermodynamic level k+1 and thermodynamic level k, depending
- ! on which field grid is being analyzed. Thus, 1.0/invrs_dz(k)
- ! is the level thickness for level k.
- ! Note: The values of 'field' and rho_ds are passed into this function
- ! so that field(1) and rho_ds(1) are actually 'field' and rho_ds
- ! at the level k = 1.
-
- numer_integral = vertical_integral( total_idx, rho_ds(1:total_idx), &
- field(1:total_idx), invrs_dz(1:total_idx) )
-
- ! Compute the denominator integral.
- ! Multiply rho_ds at level k by the level thickness
- ! at level k. Then, sum over all vertical levels.
- denom_integral = vertical_integral( total_idx, rho_ds(1:total_idx), &
- denom_field(1:total_idx), invrs_dz(1:total_idx) )
-
- ! Find the vertical average of 'field'.
- vertical_avg = numer_integral / denom_integral
-
- return
- end function vertical_avg
-
- !=============================================================================
- pure function vertical_integral( total_idx, rho_ds, &
- field, invrs_dz )
-
- ! Description:
- ! Computes the vertical integral. rho_ds, field, and invrs_dz must all be
- ! of size total_idx and should all start at the same index.
- !
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input variables
- integer, intent(in) :: &
- total_idx ! The total numer of indices within the range of averaging
-
- real( kind = core_rknd ), dimension(total_idx), intent(in) :: &
- rho_ds, & ! Dry, static density [kg/m^3]
- field, & ! The field to be vertically averaged [Units vary]
- invrs_dz ! Level thickness [1/m]
- ! Note: The rho_ds and field points need to be arranged from
- ! lowest to highest in altitude, with rho_ds(1) and
- ! field(1) actually their respective values at level k = begin_idx.
-
- ! Local variables
- real( kind = core_rknd ) :: &
- vertical_integral ! Integral in the numerator (see description)
-
- !-----------------------------------------------------------------------
-
- ! Assertion checks: that begin_idx <= gr%nz - 1
- ! that end_idx >= 2
- ! that begin_idx <= end_idx
-
-
- ! Initializing vertical_integral to avoid a compiler warning.
- vertical_integral = 0.0_core_rknd
-
- ! Compute the integral.
- ! Multiply the field at level k by rho_ds at level k and by
- ! the level thickness at level k. Then, sum over all vertical levels.
- ! Note: The values of the field and rho_ds are passed into this function
- ! so that field(1) and rho_ds(1) are actually the field and rho_ds
- ! at level k_start.
- vertical_integral = sum( field * rho_ds / invrs_dz )
-
- return
- end function vertical_integral
-
-!===============================================================================
-
-end module crmx_fill_holes
diff --git a/src/physics/spcam/crm/CLUBB/crmx_gmres_cache.F90 b/src/physics/spcam/crm/CLUBB/crmx_gmres_cache.F90
deleted file mode 100644
index 008ce4925d..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_gmres_cache.F90
+++ /dev/null
@@ -1,171 +0,0 @@
-!----------------------------------------------------------------------------
-! $Id: gmres_cache.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!==============================================================================
-module crmx_gmres_cache
-
-#ifdef MKL
-
- use crmx_clubb_precision, only: &
- dp ! double precision
-
- ! Description:
- ! This module contains cache data structures for the GMRES wrapper class.
- !
- ! This is mostly to allow us to get around some...odd errors when it was
- ! integrated into the gmres_wrap module. The cache variables are public, as
- ! they will need to be passed in whenever gmres_solve is called.
-
- implicit none
-
- public :: gmres_cache_matrix_init, gmres_cache_soln, &
- gmres_cache_temp_init
-
- private ! Default scope
-
- real( kind = dp ), public, pointer, dimension(:,:) :: &
- gmres_prev_soln, & ! Stores the previous solution vectors from earlier
- ! GMRES solve runs. The first dimension is for the
- ! actual vector; the second dimension is to determine
- ! which cache to access--this is done via the GMRES
- ! indices for each of the different matrices.
- gmres_prev_precond_a ! Stores the previous preconditioner matrix from
- ! earlier GMRES solve runs. The first dimension is
- ! for the a-array itself; the second dimension is to
- ! determine which cached array to access--this is
- ! done via the GMRES indices for each of the
- ! different matrices.
-
- real( kind = dp ), public, pointer, dimension(:) :: &
- gmres_temp_intlc, & ! Temporary array that stores GMRES internal values
- ! for the interlaced matrices (2 x gr%nz grid
- ! levels)
- gmres_temp_norm ! Temporary array that stores GMRES internal values
- ! for the non-interlaced matrices (gr%nz grid
- ! levels)
-
- integer, public :: &
- gmres_tempsize_norm, & ! Size of the temporary array for
- ! non-interlaced matrices
- gmres_tempsize_intlc ! Size of the temporary array for
- ! interlaced matrices
-
- integer, public, parameter :: &
- maximum_gmres_idx = 1 ! Maximum number of different types of solves the
- ! wrapper can keep memory for. If new matrices are
- ! added that GMRES is to be used for, increase this
- ! number and add a public parameter corresponding to
- ! the matrix below:
-
- integer, public, parameter :: &
- gmres_idx_wp2wp3 = 1 ! GMRES wrapper index for the wp2_wp3 matrices
-
- logical, public, dimension(maximum_gmres_idx) :: &
- l_gmres_soln_ok ! Stores if the current solution is "okay"--that is, if an
- ! initial solution has been passed in for that particular
- ! cache index. This defaults to false and is set to true
- ! when a solution is updated.
-
- contains
-
- subroutine gmres_cache_temp_init(numeqns) ! Intent(in)
- ! Description:
- ! Initialization subroutine for the temporary arrays for GMRES
- !
- ! This subroutine initializes the temporary arrays that are used to work
- ! the GMRES solver.
- !
- ! These temporary arrays are used for all GMRES solves.
- !
- ! References:
- ! None
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- numeqns ! Number of equations for non-interlaced matrices (gr%nz)
-
- integer :: &
- numeqns_intlc ! Number of equations for interlaced matrices
-
- numeqns_intlc = numeqns * 2
-
- ! Figure out the sizes of the temporary arrays
- ! The equations were lifted from the Intel documentation of dfgmres:
- ! http://www.intel.com/software/products/mkl/docs/webhelp/ssr/functn_rci_dfgmres.html
- ! All of the ipar(15)s have been replaced with "numeqns", as the code
- ! examples seemed to use N (numeqns) in place of ipar(15).
- gmres_tempsize_norm = ((((2*numeqns + 1)*numeqns) &
- + (numeqns*(numeqns+9))/2) + 1) ! Known magic number
-
- gmres_tempsize_intlc = ((((2*numeqns_intlc + 1)*numeqns_intlc) &
- + (numeqns_intlc*(numeqns_intlc+9))/2) + 1) ! Known magic number
-
- ! Allocate the temporary arrays
- allocate( gmres_temp_intlc(1:gmres_tempsize_intlc), &
- gmres_temp_norm(1:gmres_tempsize_norm) )
-
- end subroutine gmres_cache_temp_init
-
- subroutine gmres_cache_matrix_init(max_numeqns, max_elements, & ! Intent(in)
- max_gmres_idx) ! Intent(in)
- ! Description:
- ! Initialization subroutine for the caches for GMRES.
- !
- ! This initializes the cache that stores the previous solution and
- ! previous preconditioner values for all GMRES solves.
- !
- ! References:
- ! None
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- max_numeqns, & ! Maximum number of equations for a matrix that will be
- ! solved with GMRES
- max_elements, & ! Maximum number of non-zero elements for a matrix that
- ! will be solved with GMRES
- max_gmres_idx ! Maximum number of distinct matrices that will be solved
- ! with GMRES
-
- allocate( gmres_prev_soln(1:max_numeqns,1:max_gmres_idx), &
- gmres_prev_precond_a(1:max_elements,1:max_gmres_idx) )
-
- l_gmres_soln_ok = .false.
-
- end subroutine gmres_cache_matrix_init
-
- subroutine gmres_cache_soln(numeqns, gmres_idx, solution) ! Intent(in)
- ! Description:
- ! Subroutine that caches a previous solution for a particular GMRES-solved
- ! matrix.
- !
- ! Stores the current solution in the cache so it can be referenced for
- ! the next GMRES solve. This subroutine will also set the solution_ok
- ! flag for that particular GMRES index.
- !
- ! References:
- ! None
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- integer, intent(in) :: &
- numeqns, & ! The number of equations in the solution vector
- gmres_idx ! The index for the particular matrix solved by GMRES
-
- real( kind = core_rknd ), dimension(numeqns), intent(in) :: &
- solution ! The solution vector to be cached
-
- gmres_prev_soln(1:numeqns,gmres_idx) = solution
-
- l_gmres_soln_ok(gmres_idx) = .true.
-
- end subroutine gmres_cache_soln
-
-#endif /* MKL */
-
-end module crmx_gmres_cache
diff --git a/src/physics/spcam/crm/CLUBB/crmx_gmres_wrap.F90 b/src/physics/spcam/crm/CLUBB/crmx_gmres_wrap.F90
deleted file mode 100644
index bcab38cdb4..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_gmres_wrap.F90
+++ /dev/null
@@ -1,391 +0,0 @@
-!----------------------------------------------------------------------------
-! $Id: gmres_wrap.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!==============================================================================
-
-module crmx_gmres_wrap
-
-#ifdef MKL
-
- ! Description:
- ! This module wraps the MKL version of GMRES, an iterative solver. Note that
- ! this will only work for the MKL-specific version of GMRES--any other GMRES
- ! implementations will require retooling of this code!
- !
- ! The primary subroutine, gmres_solve utilizes GMRES to solve a given matrix.
- !
- ! There is also a gmres_init, which initializes some of the internal data
- ! used for the wrapper.
- !
- ! This wrapper automatically keeps prior solutions to use the previous data
- ! to speed up the solves. For the purposes of allowing this solver to be used
- ! with more than one matrix type, the wrapper has a "solve index" variable.
- ! Pass in the proper solve index variable to associate your solve with
- ! previous solves of the same matrix.
-
- use crmx_gmres_cache, only: &
- maximum_gmres_idx ! Variable
-
- implicit none
-
- public :: gmres_solve, gmres_init
-
- private ! Default scope
-
- contains
-
- subroutine gmres_init(max_numeqns, max_elements) ! Intent(in)
-
- ! Description:
- ! Initialization subroutine for the GMRES iterative matrix equation solver
- !
- ! This subroutine initializes the previous memory handles for the GMRES
- ! routines, for the purpose of speeding up calculations.
- ! These handles are initialized to a size specified by the number of
- ! equations specified in this subroutine.
- !
- ! WARNING: Once initialized, only use the specified gmres_idx for that
- ! particular matrix! Failure to do so could result in greatly decreased
- ! performance, incorrect solutions, or both!
- !
- ! Once this is called, the proper prev_soln_ and prev_lu_
- ! handles in the gmres_cache module can be used, and will need to be passed
- ! in to gmres_solve for that matrix.
- !
- ! References:
- ! None
-
- use crmx_gmres_cache, only: &
- gmres_cache_matrix_init ! Subroutines
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: &
- max_numeqns, & ! Maximum number of equations for a matrix that will be
- ! solved with GMRES
- max_elements ! Maximum number of non-zero elements for a matrix that
- ! will be solved with GMRES
-
- call gmres_cache_matrix_init( max_numeqns, max_elements, maximum_gmres_idx )
-
- end subroutine gmres_init
-
- subroutine gmres_solve(elements, numeqns, & !Intent(in)
- csr_a, csr_ia, csr_ja, tempsize, & !Intent(in)
- prev_soln, prev_lu, rhs, temp, & !Intent(in/out)
- solution, err_code) !Intent(out)
-
- ! Description:
- ! Solves a matrix equation using GMRES. On the first timestep and every
- ! fifth timestep afterward, a preconditioner is computed for the matrix
- ! and stored. In addition, on the first timestep the matrix is solved using
- ! LAPACK, which is used as the estimate for GMRES for the first timestep.
- ! After this, the previous solution found is used as the estimate.
- !
- ! To use the proper cached preconditioner and solution, make sure you pass
- ! the proper gmres_idx corresponding to the matrix you're solving--using a
- ! value different than what has been used in the past will cause, at best,
- ! a slower solve, and at worst, an incorrect one.
- !
- ! References:
- ! None
-
- use crmx_clubb_precision, only: &
- dp, & ! double precision
- core_rknd
-
- implicit none
-
- include "mkl_rci.fi"
-
- ! Input variables
- integer, intent(in) :: &
- elements, & ! Number of elements in the csr_a/csr_ja arrays
- numeqns ! Number of equations in the matrix
-
- real( kind = core_rknd ), dimension(elements), intent(in) :: &
- csr_a ! A-array description of the matrix in CSR format. This
- ! will be converted to double precision for the purposes
- ! of running GMRES.
-
- integer, dimension(numeqns + 1), intent(in) :: &
- csr_ia ! IA-array portion of the matrix description in CSR format.
- ! This describes the indices of the JA-array that start
- ! new rows. For more details, check the documentation in
- ! the csr_matrix_class module.
-
- integer, dimension(elements), intent(in) :: &
- csr_ja ! JA-array portion of the matrix description in CSR format.
- ! This describes which columns of a are nonzero. For more
- ! details, check the documentation in the csr_matrix_class
- ! module.
-
- integer, intent(in) :: &
- tempsize ! Denotes the size of the temporary array used for GMRES
- ! calculations.
-
- ! Input/Output variables
- real( kind = core_rknd ), dimension(numeqns), intent(inout) :: &
- rhs ! Right-hand-side vectors to solve the equation for.
-
- real( kind = dp ), dimension(numeqns), intent(inout) :: &
- prev_soln ! Previous solution cache vector for the matrix to be solved
- ! for--pass the proper handle from the gmres_cache module
-
- real( kind = dp ), dimension(elements), intent(inout) :: &
- prev_lu ! Previous LU-decomposition a-array for the matrix to be
- ! solved for--pass the proper handle from the gmres_cache
- ! module
-
- real( kind = dp ), dimension(tempsize), intent(inout) :: &
- temp ! Temporary array that stores working values while the GMRES
- ! solver iterates
-
- ! Output variables
- real( kind = core_rknd ), dimension(numeqns), intent(out) :: &
- solution ! Solution vector, output of solver routine
-
- integer, intent(out) :: &
- err_code ! Error code, nonzero if errors occurred.
-
- ! Local variables
- logical :: l_gmres_run ! Variable denoting if we need to loop and run
- ! a GMRES iteration again.
-
- integer :: &
- rci_req, & ! RCI_Request for GMRES--allows us to take action based
- ! on what the iterative solver requests to be done.
- iters ! Total number of iterations GMRES has run.
-
- integer, dimension(128) :: &
- ipar ! Parameter array for the GMRES iterative solver
-
- real( kind = dp ), dimension(128) :: &
- dpar ! Parameter array for the GMRES iterative solver
-
- ! The following local variables are double-precision so we can use GMRES
- ! as there is only double-precision support for GMRES.
- ! We will need to convert our single-precision numbers to double precision
- ! for the duration of the calculations.
- real( kind = dp ), dimension(elements) :: &
- csr_dbl_a ! Double-precision version of the CSR-format A array
-
- real( kind = dp ), dimension(numeqns) :: &
- dbl_rhs, & ! Double-precision version of the rhs vector
- dbl_soln, & ! Double-precision version of the solution vector
- tempvec ! Temporary vector for applying inverse LU-decomp matrix
- !tmp_rhs
-
- ! Variables used to solve the preconditioner the first time with PARDISO.
- !integer, parameter :: &
- !pardiso_size_arrays = 64, &
- !real_nonsymm = 11
-
- !integer(kind=8), dimension(pardiso_size_arrays) :: &
- ! pt ! PARDISO internal pointer array
-
- !integer(kind=4), dimension(pardiso_size_arrays) :: &
- ! iparm
-
- !integer(kind=4), dimension(numeqns) :: &
- ! perm
-
- ! integer :: i, j
-
- ! We want to be running, initially.
- l_gmres_run = .true.
-
- ! Set the default error code to 0 (no errors)
- ! This is to make the default explicit; Fortran initializes
- ! values to 0.
- err_code = 0
-
- ! Convert our A array and rhs vector to double precision...
- csr_dbl_a = dble(csr_a)
- dbl_rhs = dble(rhs)
-
- ! DEBUG: Set our a_array so it represents the identity matrix, and
- ! set the RHS so we can get a meaningful answer.
-! csr_dbl_a = 1_dp
-! csr_dbl_a(1) = 1D1
-! csr_dbl_a(5) = 1D1
-! csr_dbl_a(elements) = 1D1
-! csr_dbl_a(elements - 4) = 1D1
-! do i=10,elements - 9,5
-! csr_dbl_a(i) = 1D1
-! end do
-! do i=1,numeqns,1
-! dbl_rhs(i) = i * 1_dp
-! end do
-! dbl_rhs = 9D3
-! dbl_rhs = 1D1
-
- ! DEBUG: Make sure our a_array isn't wrong
-! do i=1,elements,1
-! print *, "csr_dbl_a idx",i,"=",csr_dbl_a(i)
-! end do
-
- ! Figure out the default value for ipar(15) and put it in our ipar_15 int.
- !ip_15 = min(150, numeqns)
-
- ! Figure out the size of the temp array.
- !tempsize = ((((2*numeqns + 1)*numeqns)+(numeqns*(numeqns+9))/2) + 1)
- ! This ugly equation was lifted from the Intel documentation of dfgmres:
- ! http://www.intel.com/software/products/mkl/docs/webhelp/ssr/functn_rci_dfgmres.html
- ! All of the ipar(15)s have been replaced with "numeqns", as the code
- ! examples seemed to use N (numeqns) in place of ipar(15).
-
- ! Allocate the temp array.
- !allocate(temp(1:tempsize))
-
- ! Generate our preconditioner matrix with the ILU0 subroutine.
- call dcsrilu0( numeqns, csr_dbl_a, csr_ia, csr_ja, &
- prev_lu, ipar, dpar, err_code )
-
- ! On the first timestep we need to solve our preconditioner to give us
- ! our first solution estimate. After this, the previous solution will
- ! suffice as an estimate.
-! if (iteration_num(gmres_idx) == 0) then
- !solve with precond_a, csr_ia, csr_ja.
- !One thing to test, too: try just setting the solution vector to 1
- ! for the first timestep and see if it's not too unreasonably slow?
-! call pardisoinit( pt, real_nonsymm, iparm )
-#ifdef _OPENMP
-! iparm(3) = omp_get_max_threads()
-#else
-! iparm(3) = 1
-#endif
-
-! call pardiso( pt, 1, 1, real_nonsymm, 13, numeqns, & !Intent(in)
-! prev_lu, csr_ia, csr_ja, perm, 1, iparm, 0, & !Intent(in)
-! dbl_rhs, & !Intent(inout)
-! prev_soln, err_code ) !Intent(out)
-! end if !iteration_num == 1
-
- !DEBUG: Set apporximate solution vector to 0.9 (?) for now
- !prev_soln(:) = 0.9_dp
-
- !do i=1,numeqns,1
- ! print *, "Current approximate solution idx",i,"=",prev_soln(i)
- !end do
-
- ! Initialize our solution vector to the previous solution passed in
- dbl_soln = prev_soln
-
- ! Set up the GMRES solver.
- call dfgmres_init( numeqns, dbl_soln, dbl_rhs, &
- rci_req, ipar, dpar, temp )
-
- ! Set the parameters that tell GMRES to handle stopping tests
- ipar(9) = 1
- ipar(10) = 0
- ipar(12) = 1
-
- ! Set the parameter that tells GMRES to use a preconditioner
- ipar(11) = 1
-
- ! Check our GMRES settings.
- call dfgmres_check( numeqns, dbl_soln, dbl_rhs, &
- rci_req, ipar, dpar, temp )
-
- ! Start the GMRES solver. We set up a while loop which will be broken when
- ! the GMRES solver indicates that a solution has been found.
- do while(l_gmres_run)
- !print *, "********************************************************"
- !print *, "BEGINNING ANOTHER ITERATION..."
- !print *, "========================================================"
- ! Run a GMRES iteration.
- call dfgmres( numeqns, dbl_soln, dbl_rhs, &
- rci_req, ipar, dpar, temp )
-
- select case(rci_req)
- case (0)
- l_gmres_run = .false.
- case (1)
- ! Multiply our left-hand side by the vector placed in the temp array,
- ! at ipar(22), and place the result in the temp array at ipar(23).
- ! Display temp(ipar(22))
- ! print *, "------------------------------------------------"
- ! print *, "RCI_REQ=1: MULTIPLY VECTOR BY A MATRIX"
- ! do i=1,numeqns,1
- ! print *, "Tempvec before, idx",i,"=",temp(ipar(22)+i-1)
- ! end do
- call mkl_dcsrgemv( 'N', numeqns, csr_dbl_a, csr_ia, csr_ja, &
- temp(ipar(22)), temp(ipar(23)) ) ! Known magic number
- ! do i=1,numeqns,1
- ! print *, "Tempvec after, idx",i,"=",temp(ipar(23)+i-1)
- ! end do
- ! print *, "------------------------------------------------"
- case (2)
- ! Ignore this for now, see if GMRES ever escapes.
- case (3)
- ! Apply the inverse of the preconditioner to the vector placed in the
- ! temp array at ipar(22), and place the result in the temp array at
- ! ipar(23).
- !print *, "------------------------------------------------"
- !print *, "RCI_REQ=3: APPLY PRECONDITION TO VECTOR"
- !do i=1,numeqns,1
- ! print *, "Tempvec before, idx",i,"=",temp(ipar(22)+i-1)
- !end do
- call mkl_dcsrtrsv( 'L', 'N', 'U', numeqns, &
- prev_lu, csr_ia, csr_ja, &
- temp(ipar(22)), tempvec ) ! Known magic number
- call mkl_dcsrtrsv( 'U', 'N', 'N', numeqns, &
- prev_lu, csr_ia, csr_ja, &
- tempvec, temp(ipar(23)) ) ! Known magic number
- !do i=1,numeqns,1
- ! print *, "Tempvec after, idx",i,"=",temp(ipar(23)+i-1)
- !end do
- !print *, "------------------------------------------------"
-
- case (4)
-! if (dpar(7) < GMRES_TOL) then
-! l_gmres_run = .false.
-! else
-! ! Keep running, we aren't there yet.
-! l_gmres_run = .true.
-! end if
- case default
- ! We got a response we weren't expecting. This is probably bad.
- ! (Then again, maybe it's just not something we accounted for?)
- ! Regardless, let's set an error code and break out of here.
- print *, "Unknown rci_request returned from GMRES:", rci_req
- l_gmres_run = .false.
- err_code = -1
- end select
- ! Report current iteration
-! call dfgmres_get( numeqns, dbl_soln, dbl_rhs, rci_req, &
-! ipar, dpar, temp, iters )
-! print *, "========================================================"
-! print *, "END OF LOOP: REPORTING INFORMATION"
-! print *, "Current number of GMRES iterations: ", iters
-! do i=1,numeqns,1
-! print *, "double value of soln so far, idx",i,"=",dbl_soln(i)
-! end do
-! print *, "========================================================"
-! print *, "********************************************************"
- end do
- !if (err_code == 0) then
-
- ! Get the answer, convert it to single-precision
- call dfgmres_get( numeqns, dbl_soln, dbl_rhs, rci_req, &
- ipar, dpar, temp, iters )
-
- !print *, "Total iterations for GMRES:",iters
-
- !do i=1,numeqns,1
- ! print *, "double value of soln, idx",i,"=",dbl_soln(i)
- !end do
-
- ! Store our solution as the previous solution for use in the next
- ! simulation timestep.
- prev_soln = dbl_soln
-
- solution = real(dbl_soln)
- !end if
-
- end subroutine gmres_solve
-
-#endif /* MKL */
-
-end module crmx_gmres_wrap
diff --git a/src/physics/spcam/crm/CLUBB/crmx_grid_class.F90 b/src/physics/spcam/crm/CLUBB/crmx_grid_class.F90
deleted file mode 100644
index 26d1a8c86a..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_grid_class.F90
+++ /dev/null
@@ -1,2036 +0,0 @@
-!------------------------------------------------------------------------
-! $Id: grid_class.F90 6116 2013-03-22 00:37:40Z bmg2@uwm.edu $
-!===============================================================================
-module crmx_grid_class
-
- ! Description:
- !
- ! Definition of a grid class and associated functions
- !
- ! The grid specification is as follows:
- !
- ! + ================== zm(nzmax) =========GP=======
- ! |
- ! |
- ! 1/dzt(nzmax) + ------------------ zt(nzmax) ---------GP-------
- ! | |
- ! | |
- ! + 1/dzm(nzmax-1) ================== zm(nzmax-1) ================
- ! |
- ! |
- ! + ------------------ zt(nzmax-1) ----------------
- !
- ! .
- ! .
- ! .
- ! .
- !
- ! ================== zm(k+1) ===================
- !
- !
- ! + ------------------ zt(k+1) -------------------
- ! |
- ! |
- ! + 1/dzm(k) ================== zm(k) =====================
- ! | |
- ! | |
- ! 1/dzt(k) + ------------------ zt(k) ---------------------
- ! |
- ! |
- ! + ================== zm(k-1) ===================
- !
- !
- ! ------------------ zt(k-1) -------------------
- !
- ! .
- ! .
- ! .
- ! .
- !
- ! + ================== zm(2) =====================
- ! |
- ! |
- ! 1/dzt(2) + ------------------ zt(2) ---------------------
- ! | |
- ! | |
- ! + 1/dzm(1) ================== zm(1) ============GP======= zm_init
- ! | ////////////////////////////////////////////// surface
- ! |
- ! + ------------------ zt(1) ------------GP-------
- !
- !
- ! The variable zm(k) stands for the momentum level altitude at momentum
- ! level k; the variable zt(k) stands for the thermodynamic level altitude at
- ! thermodynamic level k; the variable invrs_dzt(k) is the inverse distance
- ! between momentum levels (over a central thermodynamic level k); and the
- ! variable invrs_dzm(k) is the inverse distance between thermodynamic levels
- ! (over a central momentum level k). Please note that in the above diagram,
- ! "invrs_dzt" is denoted "dzt", and "invrs_dzm" is denoted "dzm", such that
- ! 1/dzt is the distance between successive momentum levels k-1 and k (over a
- ! central thermodynamic level k), and 1/dzm is the distance between successive
- ! thermodynamic levels k and k+1 (over a central momentum level k).
- !
- ! The grid setup is compatible with a stretched (unevely-spaced) grid. Thus,
- ! the distance between successive grid levels may not always be constant.
- !
- ! The following diagram is an example of a stretched grid that is defined on
- ! momentum levels. The thermodynamic levels are placed exactly halfway
- ! between the momentum levels. However, the momentum levels do not fall
- ! halfway between the thermodynamic levels.
- !
- ! =============== zm(k+1) ===============
- !
- !
- !
- ! --------------- zt(k+1) ---------------
- !
- !
- !
- ! =============== zm(k) ===============
- !
- ! --------------- zt(k) ---------------
- !
- ! =============== zm(k-1) ===============
- !
- ! The following diagram is an example of a stretched grid that is defined on
- ! thermodynamic levels. The momentum levels are placed exactly halfway
- ! between the thermodynamic levels. However, the thermodynamic levels do not
- ! fall halfway between the momentum levels.
- !
- ! --------------- zt(k+1) ---------------
- !
- !
- !
- ! =============== zm(k) ===============
- !
- !
- !
- ! --------------- zt(k) ---------------
- !
- ! =============== zm(k-1) ===============
- !
- ! --------------- zt(k-1) ---------------
- !
- ! NOTE: Any future code written for use in the CLUBB parameterization should
- ! use interpolation formulas consistent with a stretched grid. The
- ! simplest way to do so is to call the appropriate interpolation
- ! function from this module. Interpolations should *not* be handled in
- ! the form of: ( var_zm(k) + var_zm(k-1) ) / 2; *nor* in the form of:
- ! 0.5_core_rknd*( var_zt(k+1) + var_zt(k) ). Rather, all explicit interpolations
- ! should call zt2zm or zm2zt; while interpolations for a variable being
- ! solved for implicitly in the code should use gr%weights_zt2zm (which
- ! refers to interp_weights_zt2zm_imp), or gr%weights_zm2zt (which
- ! refers to interp_weights_zm2zt_imp).
- !
- ! Momentum level 1 is placed at altitude zm_init, which is usually at the
- ! surface. However, in general, zm_init can be at any altitude defined by the
- ! user.
- !
- ! GP indicates ghost points. Variables located at those levels are not
- ! prognosed, but only used for boundary conditions.
- !
- ! Chris Golaz, 7/17/99
- ! modified 9/10/99
-
- ! References:
-
- ! Section 3c, p. 3548 /Numerical discretization/ of:
- ! ``A PDF-Based Model for Boundary Layer Clouds. Part I:
- ! Method and Model Description'' Golaz, et al. (2002)
- ! JAS, Vol. 59, pp. 3540--3551.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- public :: gr, grid, zt2zm, interp_weights_zt2zm_imp, zm2zt, &
- interp_weights_zm2zt_imp, ddzm, ddzt, &
- setup_grid, cleanup_grid, setup_grid_heights, &
- read_grid_heights, flip, zt2zm_linear, zm2zt_linear
-
- private :: linear_interpolated_azm, linear_interpolated_azmk, &
- interpolated_azmk_imp, linear_interpolated_azt, &
- linear_interpolated_aztk, interpolated_aztk_imp, &
- gradzm, gradzt, t_above, t_below, m_above, m_below, &
- cubic_interpolated_azmk, cubic_interpolated_aztk, &
- cubic_interpolated_azm, cubic_interpolated_azt
-
- private ! Default Scoping
-
- ! Constant parameters
- integer, parameter :: &
- t_above = 1, & ! Upper thermodynamic level index (gr%weights_zt2zm).
- t_below = 2, & ! Lower thermodynamic level index (gr%weights_zt2zm).
- m_above = 1, & ! Upper momentum level index (gr%weights_zm2zt).
- m_below = 2 ! Lower momentum level index (gr%weights_zm2zt).
-
-
- type grid
-
- integer :: nz ! Number of points in the grid
- ! Note: Fortran 90/95 prevents an allocatable array from appearing
- ! within a derived type. However, a pointer can be used in the same
- ! manner as an allocatable array, as we have done here (the grid
- ! pointers are always allocated rather than assigned and nullified
- ! like real pointers). Note that these must be de-allocated to prevent
- ! memory leaks.
- real( kind = core_rknd ), pointer, dimension(:) :: &
- zm, & ! Momentum grid
- zt ! Thermo grid
- real( kind = core_rknd ), pointer, dimension(:) :: &
- invrs_dzm, & ! The inverse spacing between thermodynamic grid
- ! levels; centered over momentum grid levels.
- invrs_dzt ! The inverse spacing between momentum grid levels;
- ! centered over thermodynamic grid levels.
-
- real( kind = core_rknd ), pointer, dimension(:) :: &
- dzm, & ! Spacing between thermodynamic grid levels; centered over
- ! momentum grid levels
- dzt ! Spcaing between momentum grid levels; centered over
- ! thermodynamic grid levels
-
- ! These weights are normally used in situations
- ! where a momentum level variable is being
- ! solved for implicitly in an equation and
- ! needs to be interpolated to the thermodynamic grid levels.
- real( kind = core_rknd ), pointer, dimension(:,:) :: weights_zm2zt, &
- ! These weights are normally used in situations where a
- ! thermodynamic level variable is being solved for implicitly in an equation
- ! and needs to be interpolated to the momentum grid levels.
- weights_zt2zm
-
- end type grid
-
- ! The grid is defined here so that it is common throughout the module.
- ! The implication is that only one grid can be defined !
-
- type (grid) gr
-
-! Modification for using CLUBB in a host model (i.e. one grid per column)
-!$omp threadprivate(gr)
-
- ! Interfaces provided for function overloading
-
- ! Interpolation/extension functions
- interface zt2zm_linear
- ! This performs a linear extension at the highest grid level and therefore
- ! does not guarantee, for positive definite quantities (e.g. wp2), that the
- ! extended point is indeed positive definite. Positive definiteness can be
- ! ensured with a max statement.
- ! In the future, we could add a flag (lposdef) and, when needed, apply the
- ! max statement directly within interpolated_azm and interpolated_azmk.
- module procedure linear_interpolated_azmk, linear_interpolated_azm
- end interface
-
- interface zm2zt_linear
- ! This performs a linear extension at the lowest grid level and therefore
- ! does not guarantee, for positive definite quantities (e.g. wp2), that the
- ! extended point is indeed positive definite. Positive definiteness can be
- ! ensured with a max statement.
- ! In the future, we could add a flag (lposdef) and, when needed, apply the
- ! max statement directly within interpolated_azt and interpolated_aztk.
- module procedure linear_interpolated_azt, linear_interpolated_aztk
- end interface
-
- interface zt2zm
- ! This version uses cublic spline interpolation of Stefen (1990).
- module procedure cubic_interpolated_azmk, cubic_interpolated_azm
- end interface
-
- interface zm2zt
- ! As above, but for interpolating zm to zt levels.
- module procedure cubic_interpolated_aztk, cubic_interpolated_azt
- end interface
-
- interface interp_weights_zt2zm_imp
- module procedure interpolated_azmk_imp
- end interface
-
-
- interface interp_weights_zm2zt_imp
- module procedure interpolated_aztk_imp
- end interface
-
- ! Vertical derivative functions
- interface ddzm
- module procedure gradzm
- end interface
-
- interface ddzt
- module procedure gradzt
- end interface
-
- contains
-
- !=============================================================================
- subroutine setup_grid( nzmax, sfc_elevation, l_implemented, &
- grid_type, deltaz, zm_init, zm_top, &
- momentum_heights, thermodynamic_heights, &
- begin_height, end_height )
-
- ! Description:
- ! Grid Constructor
- !
- ! This subroutine sets up the CLUBB vertical grid.
- !
- ! References:
- ! ``Equations for CLUBB'', Sec. 8, Grid Configuration.
- !-----------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
-
- use crmx_error_code, only: &
- clubb_at_least_debug_level ! Procedure(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- NWARNING = 250 ! Issue a warning if nzmax exceeds this number.
-
- ! Input Variables
- integer, intent(in) :: &
- nzmax ! Number of vertical levels in grid [#]
-
- real( kind = core_rknd ), intent(in) :: &
- sfc_elevation ! Elevation of ground level [m AMSL]
-
- ! Flag to see if CLUBB is running on it's own,
- ! or if it's implemented as part of a host model.
- logical, intent(in) :: l_implemented
-
- ! If CLUBB is running on it's own, this option determines if it is using:
- ! 1) an evenly-spaced grid;
- ! 2) a stretched (unevenly-spaced) grid entered on the thermodynamic grid
- ! levels (with momentum levels set halfway between thermodynamic levels);
- ! or
- ! 3) a stretched (unevenly-spaced) grid entered on the momentum grid levels
- ! (with thermodynamic levels set halfway between momentum levels).
- integer, intent(in) :: grid_type
-
- ! If the CLUBB model is running by itself, and is using an evenly-spaced
- ! grid (grid_type = 1), it needs the vertical grid spacing and
- ! momentum-level starting altitude as input.
- real( kind = core_rknd ), intent(in) :: &
- deltaz, & ! Vertical grid spacing [m]
- zm_init, & ! Initial grid altitude (momentum level) [m]
- zm_top ! Maximum grid altitude (momentum level) [m]
-
- ! If the CLUBB parameterization is implemented in a host model, it needs to
- ! use the host model's momentum level altitudes and thermodynamic level
- ! altitudes.
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on thermodynamic levels (grid_type = 2), it needs to use the
- ! thermodynamic level altitudes as input.
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on momentum levels (grid_type = 3), it needs to use the momentum
- ! level altitudes as input.
- real( kind = core_rknd ), intent(in), dimension(nzmax) :: &
- momentum_heights, & ! Momentum level altitudes (input) [m]
- thermodynamic_heights ! Thermodynamic level altitudes (input) [m]
-
- integer, intent(out) :: &
- begin_height, & ! Lower bound for *_heights arrays [-]
- end_height ! Upper bound for *_heights arrays [-]
-
- ! Local Variables
- integer :: ierr, & ! Allocation stat
- i ! Loop index
-
-
- ! ---- Begin Code ----
-
- ! Define the grid size
-
- if ( nzmax > NWARNING .and. clubb_at_least_debug_level( 1 ) ) then
- write(fstderr,*) "Warning: running with vertical grid "// &
- "which is larger than", NWARNING, "levels."
- write(fstderr,*) "This may take a lot of CPU time and memory."
- end if
-
- gr%nz = nzmax
-
- ! Default bounds
- begin_height = 1
-
- end_height = gr%nz
-
- !---------------------------------------------------
- if ( .not. l_implemented ) then
-
- if ( grid_type == 1 ) then
-
- ! Determine the number of grid points given the spacing
- ! to fit within the bounds without going over.
- gr%nz = floor( ( zm_top - zm_init + deltaz ) / deltaz )
-
- else if( grid_type == 2 ) then! Thermo
-
- ! Find begin_height (lower bound)
-
- i = gr%nz
-
- do while( thermodynamic_heights(i) >= zm_init .and. i > 1 )
-
- i = i - 1
-
- end do
-
- if( thermodynamic_heights(i) >= zm_init ) then
-
- stop "Stretched zt grid cannot fulfill zm_init requirement"
-
- else
-
- begin_height = i
-
- end if
-
- ! Find end_height (upper bound)
-
- i = gr%nz
-
- do while( thermodynamic_heights(i) > zm_top .and. i > 1 )
-
- i = i - 1
-
- end do
-
- if( zm_top < thermodynamic_heights(i) ) then
-
- stop "Stretched zt grid cannot fulfill zm_top requirement"
-
- else
-
- end_height = i
-
- gr%nz = size( thermodynamic_heights(begin_height:end_height) )
-
- end if
-
- else if( grid_type == 3 ) then ! Momentum
-
- ! Find begin_height (lower bound)
-
- i = 1
-
- do while( momentum_heights(i) < zm_init .and. i < gr%nz )
-
- i = i + 1
-
- end do
-
- if( momentum_heights(i) < zm_init ) then
-
- stop "Stretched zm grid cannot fulfill zm_init requirement"
-
- else
-
- begin_height = i
-
- end if
-
- ! Find end_height (lower bound)
-
- i = gr%nz
-
- do while( momentum_heights(i) > zm_top .and. i > 1 )
-
- i = i - 1
-
- end do
-
- if( momentum_heights(i) > zm_top ) then
-
- stop "Stretched zm grid cannot fulfill zm_top requirement"
-
- else
-
- end_height = i
-
- gr%nz = size( momentum_heights(begin_height:end_height) )
-
- end if
-
- endif ! grid_type
-
- endif ! l_implemented
-
- !---------------------------------------------------
-
- ! Allocate memory for the grid levels
- allocate( gr%zm(gr%nz), gr%zt(gr%nz), &
- gr%dzm(gr%nz), gr%dzt(gr%nz), &
- gr%invrs_dzm(gr%nz), gr%invrs_dzt(gr%nz), &
- gr%weights_zm2zt(m_above:m_below,gr%nz), &
- gr%weights_zt2zm(t_above:t_below,gr%nz), &
- stat=ierr )
-
- if ( ierr /= 0 ) then
- write(fstderr,*) "In setup_grid: allocation of grid variables failed."
- stop "Fatal error."
- end if
-
- ! Set the values for the derived types used for heights, derivatives, and
- ! interpolation from the momentum/thermodynamic grid
- call setup_grid_heights &
- ( l_implemented, grid_type, &
- deltaz, zm_init, &
- momentum_heights(begin_height:end_height), &
- thermodynamic_heights(begin_height:end_height) )
-
- if ( sfc_elevation > gr%zm(1) ) then
- write(fstderr,*) "The altitude of the lowest momentum level, " &
- // "gr%zm(1), must be at or above the altitude of " &
- // "the surface, sfc_elevation. The lowest model " &
- // "momentum level cannot be below the surface."
- write(fstderr,*) "Altitude of lowest momentum level =", gr%zm(1)
- write(fstderr,*) "Altitude of the surface =", sfc_elevation
- stop "Fatal error."
- endif
-
- return
-
- end subroutine setup_grid
-
- !=============================================================================
- subroutine cleanup_grid
-
- ! Description:
- ! De-allocates the memory for the grid
- !
- ! References:
- ! None
- !------------------------------------------------------------------------------
- use crmx_constants_clubb, only: &
- fstderr ! Constant
-
- implicit none
-
- ! Local Variable(s)
- integer :: ierr
-
- ! ----- Begin Code -----
-
- ! Allocate memory for grid levels
- deallocate( gr%zm, gr%zt, &
- gr%dzm, gr%dzt, &
- gr%invrs_dzm, gr%invrs_dzt, &
- gr%weights_zm2zt, gr%weights_zt2zm, &
- stat=ierr )
-
- if ( ierr /= 0 ) then
- write(fstderr,*) "Grid deallocation failed."
- end if
-
- return
- end subroutine cleanup_grid
-
- !=============================================================================
- subroutine setup_grid_heights &
- ( l_implemented, grid_type, &
- deltaz, zm_init, momentum_heights, &
- thermodynamic_heights )
-
- ! Description:
- ! Sets the heights and interpolation weights of the column.
- ! This is seperated from setup_grid for those host models that have heights
- ! that vary with time.
- ! References:
- ! None
- !------------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: fstderr ! Constant
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
-
- ! Flag to see if CLUBB is running on it's own,
- ! or if it's implemented as part of a host model.
- logical, intent(in) :: l_implemented
-
- ! If CLUBB is running on it's own, this option determines if it is using:
- ! 1) an evenly-spaced grid;
- ! 2) a stretched (unevenly-spaced) grid entered on the thermodynamic grid
- ! levels (with momentum levels set halfway between thermodynamic levels);
- ! or
- ! 3) a stretched (unevenly-spaced) grid entered on the momentum grid levels
- ! (with thermodynamic levels set halfway between momentum levels).
- integer, intent(in) :: grid_type
-
- ! If the CLUBB model is running by itself, and is using an evenly-spaced
- ! grid (grid_type = 1), it needs the vertical grid spacing and
- ! momentum-level starting altitude as input.
- real( kind = core_rknd ), intent(in) :: &
- deltaz, & ! Vertical grid spacing [m]
- zm_init ! Initial grid altitude (momentum level) [m]
-
-
- ! If the CLUBB parameterization is implemented in a host model, it needs to
- ! use the host model's momentum level altitudes and thermodynamic level
- ! altitudes.
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on thermodynamic levels (grid_type = 2), it needs to use the
- ! thermodynamic level altitudes as input.
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on momentum levels (grid_type = 3), it needs to use the momentum
- ! level altitudes as input.
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- momentum_heights, & ! Momentum level altitudes (input) [m]
- thermodynamic_heights ! Thermodynamic level altitudes (input) [m]
-
- integer :: k
-
- ! ---- Begin Code ----
-
- if ( .not. l_implemented ) then
-
-
- if ( grid_type == 1 ) then
-
- ! Evenly-spaced grid.
- ! Momentum level altitudes are defined based on the grid starting
- ! altitude, zm_init, the constant grid-spacing, deltaz, and the number
- ! of grid levels, gr%nz.
-
- ! Define momentum level altitudes. The first momentum level is at
- ! altitude zm_init.
- do k = 1, gr%nz, 1
- gr%zm(k) = zm_init + real( k-1, kind = core_rknd ) * deltaz
- enddo
-
- ! Define thermodynamic level altitudes. Thermodynamic level altitudes
- ! are located at the central altitude levels, exactly halfway between
- ! momentum level altitudes. The lowermost thermodynamic level is
- ! found by taking 1/2 the altitude difference between the bottom two
- ! momentum levels and subtracting that value from the bottom momentum
- ! level. The first thermodynamic level is below zm_init.
- gr%zt(1) = zm_init - ( 0.5_core_rknd * deltaz )
- do k = 2, gr%nz, 1
- gr%zt(k) = 0.5_core_rknd * ( gr%zm(k) + gr%zm(k-1) )
- enddo
-
-
- elseif ( grid_type == 2 ) then
-
- ! Stretched (unevenly-spaced) grid: stretched thermodynamic levels.
- ! Thermodynamic levels are defined according to a stretched grid that
- ! is entered through the use of an input file. This is similar to a
- ! SAM-style stretched grid.
-
- ! Define thermodynamic level altitudes.
- do k = 1, gr%nz, 1
- gr%zt(k) = thermodynamic_heights(k)
- enddo
-
- ! Define momentum level altitudes. Momentum level altitudes are
- ! located at the central altitude levels, exactly halfway between
- ! thermodynamic level altitudes. The uppermost momentum level
- ! altitude is found by taking 1/2 the altitude difference between the
- ! top two thermodynamic levels and adding that value to the top
- ! thermodynamic level.
- do k = 1, gr%nz-1, 1
- gr%zm(k) = 0.5_core_rknd * ( gr%zt(k+1) + gr%zt(k) )
- enddo
- gr%zm(gr%nz) = gr%zt(gr%nz) + &
- 0.5_core_rknd * ( gr%zt(gr%nz) - gr%zt(gr%nz-1) )
-
- elseif ( grid_type == 3 ) then
-
- ! Stretched (unevenly-spaced) grid: stretched momentum levels.
- ! Momentum levels are defined according to a stretched grid that is
- ! entered through the use of an input file. This is similar to a
- ! WRF-style stretched grid.
-
- ! Define momentum level altitudes.
- do k = 1, gr%nz, 1
- gr%zm(k) = momentum_heights(k)
- enddo
-
- ! Define thermodynamic level altitudes. Thermodynamic level altitudes
- ! are located at the central altitude levels, exactly halfway between
- ! momentum level altitudes. The lowermost thermodynamic level
- ! altitude is found by taking 1/2 the altitude difference between the
- ! bottom two momentum levels and subtracting that value from the
- ! bottom momentum level.
- gr%zt(1) = gr%zm(1) - 0.5_core_rknd * ( gr%zm(2) - gr%zm(1) )
- do k = 2, gr%nz, 1
- gr%zt(k) = 0.5_core_rknd * ( gr%zm(k) + gr%zm(k-1) )
- enddo
-
-
- else
-
- ! Invalid grid type.
- write(fstderr,*) "Invalid grid type: ", grid_type, &
- ". Valid options are 1, 2, or 3."
- stop "Fatal error."
-
-
- endif
-
-
- else
-
- ! The CLUBB parameterization is implemented in a host model.
- ! Use the host model's momentum level altitudes and thermodynamic level
- ! altitudes to set up the CLUBB grid.
-
- ! Momentum level altitudes from host model.
- do k = 1, gr%nz, 1
- gr%zm(k) = momentum_heights(k)
- enddo
-
- ! Thermodynamic level altitudes from host model after possible grid-index
- ! adjustment for CLUBB interface.
- do k = 1, gr%nz, 1
- gr%zt(k) = thermodynamic_heights(k)
- enddo
-
-
- endif ! not l_implemented
-
-
- ! Define dzm, the spacing between thermodynamic grid levels; centered over
- ! momentum grid levels
- do k=1,gr%nz-1
- gr%dzm(k) = gr%zt(k+1) - gr%zt(k)
- enddo
- gr%dzm(gr%nz) = gr%dzm(gr%nz-1)
-
- ! Define dzt, the spacing between momentum grid levels; centered over
- ! thermodynamic grid levels
- do k=2,gr%nz
- gr%dzt(k) = gr%zm(k) - gr%zm(k-1)
- enddo
- gr%dzt(1) = gr%dzt(2)
-
- ! Define invrs_dzm, which is the inverse spacing between thermodynamic grid
- ! levels; centered over momentum grid levels.
- do k=1,gr%nz-1
- gr%invrs_dzm(k) = 1._core_rknd / ( gr%zt(k+1) - gr%zt(k) )
- enddo
- gr%invrs_dzm(gr%nz) = gr%invrs_dzm(gr%nz-1)
-
-
- ! Define invrs_dzt, which is the inverse spacing between momentum grid
- ! levels; centered over thermodynamic grid levels.
- do k=2,gr%nz
- gr%invrs_dzt(k) = 1._core_rknd / ( gr%zm(k) - gr%zm(k-1) )
- enddo
- gr%invrs_dzt(1) = gr%invrs_dzt(2)
-
-
- ! Interpolation Weights: zm grid to zt grid.
- ! The grid index (k) is the index of the level on the thermodynamic (zt)
- ! grid. The result is the weights of the upper and lower momentum levels
- ! (that sandwich the thermodynamic level) applied to that thermodynamic
- ! level. These weights are normally used in situations where a momentum
- ! level variable is being solved for implicitly in an equation, and the
- ! aforementioned variable needs to be interpolated from three successive
- ! momentum levels (the central momentum level, as well as one momentum level
- ! above and below the central momentum level) to the intermediate
- ! thermodynamic grid levels that sandwich the central momentum level.
- ! For more information, see the comments in function interpolated_aztk_imp.
- do k = 1, gr%nz, 1
- gr%weights_zm2zt(m_above:m_below,k) &
- = interp_weights_zm2zt_imp( k )
- enddo
-
-
- ! Interpolation Weights: zt grid to zm grid.
- ! The grid index (k) is the index of the level on the momentum (zm) grid.
- ! The result is the weights of the upper and lower thermodynamic levels
- ! (that sandwich the momentum level) applied to that momentum level. These
- ! weights are normally used in situations where a thermodynamic level
- ! variable is being solved for implicitly in an equation, and the
- ! aforementioned variable needs to be interpolated from three successive
- ! thermodynamic levels (the central thermodynamic level, as well as one
- ! thermodynamic level above and below the central thermodynamic level) to
- ! the intermediate momentum grid levels that sandwich the central
- ! thermodynamic level.
- ! For more information, see the comments in function interpolated_azmk_imp.
-
- do k = 1, gr%nz, 1
- gr%weights_zt2zm(t_above:t_below,k) &
- = interp_weights_zt2zm_imp( k )
- enddo
-
- return
- end subroutine setup_grid_heights
-
- !=============================================================================
- subroutine read_grid_heights( nzmax, grid_type, &
- zm_grid_fname, zt_grid_fname, &
- file_unit, &
- momentum_heights, &
- thermodynamic_heights )
-
- ! Description:
- ! This subroutine is used foremost in cases where the grid_type corresponds
- ! with the stretched (unevenly-spaced) grid options (either grid_type = 2 or
- ! grid_type = 3). This subroutine reads in the values of the stretched grid
- ! altitude levels for either the thermodynamic level grid or the momentum
- ! level grid. This subroutine also handles basic error checking for all
- ! three grid types.
- !------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- fstderr ! Variable(s)
- use crmx_file_functions, only: &
- file_read_1d ! Procedure(s)
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables.
-
- ! Declared number of vertical levels.
- integer, intent(in) :: &
- nzmax
-
- ! If CLUBB is running on it's own, this option determines if it is using:
- ! 1) an evenly-spaced grid;
- ! 2) a stretched (unevenly-spaced) grid entered on the thermodynamic grid
- ! levels (with momentum levels set halfway between thermodynamic levels);
- ! or
- ! 3) a stretched (unevenly-spaced) grid entered on the momentum grid levels
- ! (with thermodynamic levels set halfway between momentum levels).
- integer, intent(in) :: &
- grid_type
-
- character(len=*), intent(in) :: &
- zm_grid_fname, & ! Path and filename of file for momentum level altitudes
- zt_grid_fname ! Path and filename of file for thermodynamic level altitudes
-
- integer, intent(in) :: &
- file_unit ! Unit number for zt_grid_fname & zm_grid_fname (based on the OpenMP thread)
-
- ! Output Variables.
-
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on thermodynamic levels (grid_type = 2), it needs to use the
- ! thermodynamic level altitudes as input.
- ! If the CLUBB model is running by itself, but is using a stretched grid
- ! entered on momentum levels (grid_type = 3), it needs to use the momentum
- ! level altitudes as input.
- real( kind = core_rknd ), dimension(nzmax), intent(out) :: &
- momentum_heights, & ! Momentum level altitudes (file input) [m]
- thermodynamic_heights ! Thermodynamic level altitudes (file input) [m]
-
- ! Local Variables.
-
- integer :: &
- zt_level_count, & ! Number of altitudes found in zt_grid_fname
- zm_level_count ! Number of altitudes found in zm_grid_fname
-
- integer :: input_status ! Status of file being read:
- ! > 0 ==> error reading file.
- ! = 0 ==> no error and more file to be read.
- ! < 0 ==> end of file indicator.
-
- ! Generic variable for storing file data while counting the number
- ! of file entries.
- real( kind = core_rknd ) :: generic_input_item
-
- integer :: k ! Loop index
-
- ! ---- Begin Code ----
-
- ! Declare the momentum level altitude array and the thermodynamic level
- ! altitude array to be 0 until overwritten.
- momentum_heights(1:nzmax) = 0.0_core_rknd
- thermodynamic_heights(1:nzmax) = 0.0_core_rknd
-
- ! Avoid uninitialized memory
- generic_input_item = 0.0_core_rknd
-
-
- if ( grid_type == 1 ) then
-
- ! Evenly-spaced grid.
- ! Grid level altitudes are based on a constant distance between them and
- ! a starting point for the bottom of the grid.
-
- ! As a way of error checking, make sure that there isn't any file entry
- ! for either momentum level altitudes or thermodynamic level altitudes.
- if ( zm_grid_fname /= '' ) then
- write(fstderr,*) &
- "An evenly-spaced grid has been selected. " &
- // " Please reset zm_grid_fname to ''."
- stop
- endif
- if ( zt_grid_fname /= '' ) then
- write(fstderr,*) &
- "An evenly-spaced grid has been selected. " &
- // " Please reset zt_grid_fname to ''."
- stop
- endif
-
-
- elseif ( grid_type == 2 ) then
-
- ! Stretched (unevenly-spaced) grid: stretched thermodynamic levels.
- ! Thermodynamic levels are defined according to a stretched grid that is
- ! entered through the use of an input file. Momentum levels are set
- ! halfway between thermodynamic levels. This is similar to a SAM-style
- ! stretched grid.
-
- ! As a way of error checking, make sure that there isn't any file entry
- ! for momentum level altitudes.
- if ( zm_grid_fname /= '' ) then
- write(fstderr,*) &
- "Thermodynamic level altitudes have been selected " &
- // "for use in a stretched (unevenly-spaced) grid. " &
- // " Please reset zm_grid_fname to ''."
- stop
- endif
-
- ! Open the file zt_grid_fname.
- open( unit=file_unit, file=zt_grid_fname, &
- status='old', action='read' )
-
- ! Find the number of thermodynamic level altitudes listed
- ! in file zt_grid_fname.
- zt_level_count = 0
- do
- read( unit=file_unit, fmt=*, iostat=input_status ) &
- generic_input_item
- if ( input_status < 0 ) exit ! end of file indicator
- if ( input_status > 0 ) stop & ! error reading input
- "Error reading thermodynamic level input file."
- zt_level_count = zt_level_count + 1
- enddo
-
- ! Close the file zt_grid_fname.
- close( unit=file_unit )
-
- ! Check that the number of thermodynamic grid altitudes in the input file
- ! matches the declared number of CLUBB grid levels (nzmax).
- if ( zt_level_count /= nzmax ) then
- write(fstderr,*) &
- "The number of thermodynamic grid altitudes " &
- // "listed in file " // trim(zt_grid_fname) &
- // " does not match the number of CLUBB grid " &
- // "levels specified in the model.in file."
- write(fstderr,*) &
- "Number of thermodynamic grid altitudes listed: ", &
- zt_level_count
- write(fstderr,*) &
- "Number of CLUBB grid levels specified: ", nzmax
- stop
- endif
-
- ! Read the thermodynamic level altitudes from zt_grid_fname.
- call file_read_1d( file_unit, zt_grid_fname, nzmax, 1, &
- thermodynamic_heights )
-
- ! Check that each thermodynamic level altitude increases
- ! in height as the thermodynamic level grid index increases.
- do k = 2, nzmax, 1
- if ( thermodynamic_heights(k) &
- <= thermodynamic_heights(k-1) ) then
- write(fstderr,*) &
- "The declared thermodynamic level grid " &
- // "altitudes are not increasing in height " &
- // "as grid level index increases."
- write(fstderr,*) &
- "Grid index: ", k-1, ";", &
- " Thermodynamic level altitude: ", &
- thermodynamic_heights(k-1)
- write(fstderr,*) &
- "Grid index: ", k, ";", &
- " Thermodynamic level altitude: ", &
- thermodynamic_heights(k)
- stop
- endif
- enddo
-
-
- elseif ( grid_type == 3 ) then
-
- ! Stretched (unevenly-spaced) grid: stretched momentum levels.
- ! Momentum levels are defined according to a stretched grid that is
- ! entered through the use of an input file. Thermodynamic levels are set
- ! halfway between momentum levels. This is similar to a WRF-style
- ! stretched grid.
-
- ! As a way of error checking, make sure that there isn't any file entry
- ! for thermodynamic level altitudes.
- if ( zt_grid_fname /= '' ) then
- write(fstderr,*) &
- "Momentum level altitudes have been selected " &
- // "for use in a stretched (unevenly-spaced) grid. " &
- // " Please reset zt_grid_fname to ''."
- stop
- endif
-
- ! Open the file zm_grid_fname.
- open( unit=file_unit, file=zm_grid_fname, &
- status='old', action='read' )
-
- ! Find the number of momentum level altitudes
- ! listed in file zm_grid_fname.
- zm_level_count = 0
- do
- read( unit=file_unit, fmt=*, iostat=input_status ) &
- generic_input_item
- if ( input_status < 0 ) exit ! end of file indicator
- if ( input_status > 0 ) stop & ! error reading input
- "Error reading momentum level input file."
- zm_level_count = zm_level_count + 1
- enddo
-
- ! Close the file zm_grid_fname.
- close( unit=file_unit )
-
- ! Check that the number of momentum grid altitudes in the input file
- ! matches the declared number of CLUBB grid levels (nzmax).
- if ( zm_level_count /= nzmax ) then
- write(fstderr,*) &
- "The number of momentum grid altitudes " &
- // "listed in file " // trim(zm_grid_fname) &
- // " does not match the number of CLUBB grid " &
- // "levels specified in the model.in file."
- write(fstderr,*) &
- "Number of momentum grid altitudes listed: ", &
- zm_level_count
- write(fstderr,*) &
- "Number of CLUBB grid levels specified: ", nzmax
- stop
- endif
-
- ! Read the momentum level altitudes from zm_grid_fname.
- call file_read_1d( file_unit, zm_grid_fname, nzmax, 1, &
- momentum_heights )
-
- ! Check that each momentum level altitude increases in height as the
- ! momentum level grid index increases.
- do k = 2, nzmax, 1
- if ( momentum_heights(k) &
- <= momentum_heights(k-1) ) then
- write(fstderr,*) &
- "The declared momentum level grid " &
- // "altitudes are not increasing in height " &
- // "as grid level index increases."
- write(fstderr,*) &
- "Grid index: ", k-1, ";", &
- " Momentum level altitude: ", &
- momentum_heights(k-1)
- write(fstderr,*) &
- "Grid index: ", k, ";", &
- " Momentum level altitude: ", &
- momentum_heights(k)
- stop
- endif
- enddo
-
-
- endif
-
-
- ! The purpose of this if statement is to avoid a compiler warning.
- if ( generic_input_item > 0.0_core_rknd ) then
- ! Do nothing
- endif
- ! Joshua Fasching June 2008
-
- return
-
- end subroutine read_grid_heights
-
- !=============================================================================
- pure function linear_interpolated_azm( azt )
-
- ! Description:
- ! Function to interpolate a variable located on the thermodynamic grid
- ! levels (azt) to the momentum grid levels (azm). This function inputs the
- ! entire azt array and outputs the results as an azm array. The
- ! formulation used is compatible with a stretched (unevenly-spaced) grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_interpolation, only: linear_interp_factor
-
- implicit none
-
- ! Input Variable
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azt
-
- ! Return Variable
- real( kind = core_rknd ), dimension(gr%nz) :: linear_interpolated_azm
-
- ! Local Variable
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Do the actual interpolation.
- ! Use linear interpolation.
- forall( k = 1 : gr%nz-1 : 1 )
- linear_interpolated_azm(k) = &
- linear_interp_factor( gr%weights_zt2zm(1, k), azt(k+1), azt(k) )
- end forall
-
-! ! Set the value of azm at level gr%nz (the uppermost level in the model)
-! ! to the value of azt at level gr%nz.
-! linear_interpolated_azm(gr%nz) = azt(gr%nz)
- ! Use a linear extension based on the values of azt at levels gr%nz and
- ! gr%nz-1 to find the value of azm at level gr%nz (the uppermost level
- ! in the model).
- linear_interpolated_azm(gr%nz) = &
- ( ( azt(gr%nz)-azt(gr%nz-1) ) &
- / ( gr%zt(gr%nz)-gr%zt(gr%nz-1) ) ) &
- * ( gr%zm(gr%nz)-gr%zt(gr%nz) ) + azt(gr%nz)
-
- return
-
- end function linear_interpolated_azm
-
- !=============================================================================
- pure function linear_interpolated_azmk( azt, k )
-
- ! Description:
- ! Function to interpolate a variable located on the thermodynamic grid
- ! levels (azt) to the momentum grid levels (azm). This function outputs the
- ! value of azm at a single grid level (k) after interpolating using values
- ! of azt at two grid levels. The formulation used is compatible with a
- ! stretched (unevenly-spaced) grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_interpolation, only: linear_interp_factor
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azt
-
- integer, intent(in) :: k
-
- ! Return Variable
- real( kind = core_rknd ) :: linear_interpolated_azmk
-
- ! ---- Begin Code ----
-
- ! Do the actual interpolation.
- ! Use a linear interpolation.
- if ( k /= gr%nz ) then
-
- linear_interpolated_azmk = &
- linear_interp_factor( gr%weights_zt2zm(1, k), azt(k+1), azt(k) )
-
- else
-
-! ! Set the value of azm at level gr%nz (the uppermost level in the
-! ! model) to the value of azt at level gr%nz.
-! linear_interpolated_azmk = azt(gr%nz)
- ! Use a linear extension based on the values of azt at levels gr%nz and
- ! gr%nz-1 to find the value of azm at level gr%nz (the uppermost
- ! level in the model).
- linear_interpolated_azmk = &
- ( ( azt(gr%nz)-azt(gr%nz-1) ) &
- / ( gr%zt(gr%nz)-gr%zt(gr%nz-1) ) ) &
- * ( gr%zm(gr%nz)-gr%zt(gr%nz) ) + azt(gr%nz)
-
- endif
-
- return
-
- end function linear_interpolated_azmk
-
- !=============================================================================
- pure function cubic_interpolated_azm( azt )
-
- ! Description:
- ! Function to interpolate a variable located on the thermodynamic grid
- ! levels (azt) to the momentum grid levels (azm). This function outputs the
- ! value of azt at a all grid levels using Steffen's monotonic cubic
- ! interpolation implemented by Tak Yamaguchi.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- azt
-
- ! Return Variable
- real( kind = core_rknd ), dimension(gr%nz) :: &
- cubic_interpolated_azm
-
- ! Local Variable(s)
- real( kind = core_rknd ), dimension(gr%nz) :: &
- tmp ! This is needed for variables that self-reference
- integer :: &
- k
-
- ! ---- Begin Code ----
-
- forall( k = 1 : gr%nz )
- tmp(k) = cubic_interpolated_azmk( azt, k )
- end forall
-
- cubic_interpolated_azm = tmp
-
- return
-
- end function cubic_interpolated_azm
-
- !=============================================================================
- pure function cubic_interpolated_azmk( azt, k )
-
- ! Description:
- ! Function to interpolate a variable located on the thermodynamic grid
- ! levels (azt) to the momentum grid levels (azm). This function outputs the
- ! value of azm at a single grid level (k) using Steffen's monotonic cubic
- ! interpolation implemented by Tak Yamaguchi.
- !-----------------------------------------------------------------------
-
- use crmx_interpolation, only: mono_cubic_interp
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azt
-
- integer, intent(in) :: k
-
- ! Return Variable
- real( kind = core_rknd ) :: cubic_interpolated_azmk
-
- ! Local Variable(s)
- integer :: km1, k00, kp1, kp2
-
- ! ---- Begin Code ----
-
- ! Special case for a very small domain
- if ( gr%nz < 3 ) then
- cubic_interpolated_azmk = linear_interpolated_azmk( azt, k )
- return
- end if
-
- ! k levels are based on Tak's find_indices subroutine -dschanen 24 Oct 2011
- if ( k == gr%nz-1 ) then
- km1 = gr%nz-2
- kp1 = gr%nz
- kp2 = gr%nz
- k00 = gr%nz-1
- else if ( k == gr%nz ) then ! Extrapolation
- km1 = gr%nz
- kp1 = gr%nz
- kp2 = gr%nz
- k00 = gr%nz-1
- else if ( k == 1 ) then
- km1 = 1
- kp1 = 2
- kp2 = 3
- k00 = 1
- else
- km1 = k-1
- kp1 = k+1
- kp2 = k+2
- k00 = k
- end if
-
- ! Do the actual interpolation.
- ! Use a cubic monotonic spline interpolation.
- cubic_interpolated_azmk = &
- mono_cubic_interp( gr%zm(k), km1, k00, kp1, kp2, &
- gr%zt(km1), gr%zt(k00), gr%zt(kp1), gr%zt(kp2), &
- azt(km1), azt(k00), azt(kp1), azt(kp2) )
-
- return
-
- end function cubic_interpolated_azmk
-
- !=============================================================================
- pure function interpolated_azmk_imp( m_lev ) &
- result( azt_weight )
-
- ! Description:
- ! Function used to help in an interpolation of a variable (var_zt) located
- ! on the thermodynamic grid levels (azt) to the momentum grid levels (azm).
- ! This function computes a weighting factor for both the upper thermodynamic
- ! level (k+1) and the lower thermodynamic level (k) applied to the central
- ! momentum level (k). For the uppermost momentum grid level (k=gr%nz), a
- ! weighting factor for both the thermodynamic level at gr%nz and the
- ! thermodynamic level at gr%nz-1 are calculated based on the use of a
- ! linear extension. This function outputs the weighting factors at a single
- ! momentum grid level (k). The formulation used is compatible with a
- ! stretched (unevenly-spaced) grid. The weights are defined as follows:
- !
- ! ---var_zt(k+1)------------------------------------------- t(k+1)
- ! azt_weight(t_above) = factor
- ! ===========var_zt(interp)================================ m(k)
- ! azt_weight(t_below) = 1 - factor
- ! ---var_zt(k)--------------------------------------------- t(k)
- !
- ! The vertical indices t(k+1), m(k), and t(k) correspond with altitudes
- ! zt(k+1), zm(k), and zt(k), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! For all levels k < gr%nz:
- !
- ! The formula for a linear interpolation is given by:
- !
- ! var_zt( interp to zm(k) )
- ! = [ ( var_zt(k+1) - var_zt(k) ) / ( zt(k+1) - zt(k) ) ]
- ! * ( zm(k) - zt(k) ) + var_zt(k);
- !
- ! which can be rewritten as:
- !
- ! var_zt( interp to zm(k) )
- ! = [ ( zm(k) - zt(k) ) / ( zt(k+1) - zt(k) ) ]
- ! * ( var_zt(k+1) - var_zt(k) ) + var_zt(k).
- !
- ! Furthermore, the formula can be rewritten as:
- !
- ! var_zt( interp to zm(k) )
- ! = factor * var_zt(k+1) + ( 1 - factor ) * var_zt(k);
- !
- ! where:
- !
- ! factor = ( zm(k) - zt(k) ) / ( zt(k+1) - zt(k) ).
- !
- ! One of the important uses of this function is in situations where the
- ! variable to be interpolated is being treated IMPLICITLY in an equation.
- ! Usually, the variable to be interpolated is involved in a derivative (such
- ! as d(var_zt)/dz in the diagram below). For the term of the equation
- ! containing the derivative, grid weights are needed for two interpolations,
- ! rather than just one interpolation. Thus, four grid weights (labeled
- ! A(k), B(k), C(k), and D(k) in the diagram below) are needed.
- !
- ! ---var_zt(k+1)------------------------------------------- t(k+1)
- ! A(k)
- ! ===========var_zt(interp)================================ m(k)
- ! B(k) = 1 - A(k)
- ! ---var_zt(k)-----------d(var_zt)/dz---------------------- t(k)
- ! C(k)
- ! ===========var_zt(interp)================================ m(k-1)
- ! D(k) = 1 - C(k)
- ! ---var_zt(k-1)------------------------------------------- t(k-1)
- !
- ! The vertical indices t(k+1), m(k), t(k), m(k-1), and t(k-1) correspond
- ! with altitudes zt(k+1), zm(k), zt(k), zm(k-1), and zt(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! The grid weights, indexed around the central thermodynamic level (k), are
- ! defined as follows:
- !
- ! A(k) = ( zm(k) - zt(k) ) / ( zt(k+1) - zt(k) );
- !
- ! which is the same as "factor" for the interpolation to momentum
- ! level (k). In the code, this interpolation is referenced as
- ! gr%weights_zt2zm(t_above,mk), which can be read as "grid weight in a zt2zm
- ! interpolation of the thermodynamic level above momentum level (k) (applied
- ! to momentum level (k))".
- !
- ! B(k) = 1 - [ ( zm(k) - zt(k) ) / ( zt(k+1) - zt(k) ) ]
- ! = 1 - A(k);
- !
- ! which is the same as "1 - factor" for the interpolation to momentum
- ! level (k). In the code, this interpolation is referenced as
- ! gr%weights_zt2zm(t_below,mk), which can be read as "grid weight in a zt2zm
- ! interpolation of the thermodynamic level below momentum level (k) (applied
- ! to momentum level (k))".
- !
- ! C(k) = ( zm(k-1) - zt(k-1) ) / ( zt(k) - zt(k-1) );
- !
- ! which is the same as "factor" for the interpolation to momentum
- ! level (k-1). In the code, this interpolation is referenced as
- ! gr%weights_zt2zm(t_above,mkm1), which can be read as "grid weight in a
- ! zt2zm interpolation of the thermodynamic level above momentum level (k-1)
- ! (applied to momentum level (k-1))".
- !
- ! D(k) = 1 - [ ( zm(k-1) - zt(k-1) ) / ( zt(k) - zt(k-1) ) ]
- ! = 1 - C(k);
- !
- ! which is the same as "1 - factor" for the interpolation to momentum
- ! level (k-1). In the code, this interpolation is referenced as
- ! gr%weights_zt2zm(t_below,mkm1), which can be read as "grid weight in a
- ! zt2zm interpolation of the thermodynamic level below momentum level (k-1)
- ! (applied to momentum level (k-1))".
- !
- ! Additionally, as long as the central thermodynamic level (k) in the above
- ! scenario is not the uppermost thermodynamic level or the lowermost
- ! thermodynamic level (k /= gr%nz and k /= 1), the four weighting factors
- ! have the following relationships: A(k) = C(k+1) and B(k) = D(k+1).
- !
- !
- ! Special condition for uppermost grid level, k = gr%nz:
- !
- ! The uppermost momentum grid level is above the uppermost thermodynamic
- ! grid level. Thus, a linear extension is used at this level.
- !
- ! For level k = gr%nz:
- !
- ! The formula for a linear extension is given by:
- !
- ! var_zt( extend to zm(k) )
- ! = [ ( var_zt(k) - var_zt(k-1) ) / ( zt(k) - zt(k-1) ) ]
- ! * ( zm(k) - zt(k-1) ) + var_zt(k-1);
- !
- ! which can be rewritten as:
- !
- ! var_zt( extend to zm(k) )
- ! = [ ( zm(k) - zt(k-1) ) / ( zt(k) - zt(k-1) ) ]
- ! * ( var_zt(k) - var_zt(k-1) ) + var_zt(k-1).
- !
- ! Furthermore, the formula can be rewritten as:
- !
- ! var_zt( extend to zm(k) )
- ! = factor * var_zt(k) + ( 1 - factor ) * var_zt(k-1);
- !
- ! where:
- !
- ! factor = ( zm(k) - zt(k-1) ) / ( zt(k) - zt(k-1) ).
- !
- ! Due to the fact that a linear extension is being used, the value of factor
- ! will be greater than 1. The weight of thermodynamic level k = gr%nz on
- ! momentum level k = gr%nz equals the value of factor. The weight of
- ! thermodynamic level k = gr%nz-1 on momentum level k = gr%nz equals
- ! 1 - factor, which is less than 0. However, the sum of the two weights
- ! equals 1.
- !
- !
- ! Brian Griffin; September 12, 2008.
- !
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- t_above = 1, & ! Upper thermodynamic level.
- t_below = 2 ! Lower thermodynamic level.
-
- ! Input
- integer, intent(in) :: m_lev ! Momentum level index
-
- ! Output
- real( kind = core_rknd ), dimension(2) :: azt_weight ! Weights of the thermodynamic levels.
-
- ! Local Variables
- real( kind = core_rknd ) :: factor
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Compute the weighting factors at momentum level k.
- k = m_lev
-
- if ( k /= gr%nz ) then
- ! At most levels, the momentum level is found in-between two
- ! thermodynamic levels. Linear interpolation is used.
- factor = ( gr%zm(k)-gr%zt(k) ) / ( gr%zt(k+1)-gr%zt(k) )
- else
- ! The top model level (gr%nz) is formulated differently because the top
- ! momentum level is above the top thermodynamic level. A linear
- ! extension is required, rather than linear interpolation.
- ! Note: Variable "factor" will be greater than 1 in this situation.
- factor = &
- ( gr%zm(gr%nz)-gr%zt(gr%nz-1) ) &
- / ( gr%zt(gr%nz)-gr%zt(gr%nz-1) )
- endif
-
- ! Weight of upper thermodynamic level on momentum level.
- azt_weight(t_above) = factor
- ! Weight of lower thermodynamic level on momentum level.
- azt_weight(t_below) = 1.0_core_rknd - factor
-
- return
-
- end function interpolated_azmk_imp
-
- !=============================================================================
- pure function linear_interpolated_azt( azm )
-
- ! Description:
- ! Function to interpolate a variable located on the momentum grid levels
- ! (azm) to the thermodynamic grid levels (azt). This function inputs the
- ! entire azm array and outputs the results as an azt array. The formulation
- ! used is compatible with a stretched (unevenly-spaced) grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_interpolation, only: linear_interp_factor
-
- implicit none
-
- ! Input Variable
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azm
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz) :: linear_interpolated_azt
-
- ! Local Variable
- integer :: k ! Index
-
- ! ---- Begin Code ----
-
- ! Do actual interpolation.
- ! Use a linear interpolation.
- forall( k = gr%nz : 2 : -1 )
- linear_interpolated_azt(k) = &
- linear_interp_factor( gr%weights_zm2zt(1, k), azm(k), azm(k-1) )
- end forall ! gr%nz .. 2
-! ! Set the value of azt at level 1 (the lowermost level in the model) to the
-! ! value of azm at level 1.
-! interpolated_azt(1) = azm(1)
- ! Use a linear extension based on the values of azm at levels 1 and 2 to
- ! find the value of azt at level 1 (the lowermost level in the model).
- linear_interpolated_azt(1) = &
- ( ( azm(2)-azm(1) ) / ( gr%zm(2)-gr%zm(1) ) ) &
- * ( gr%zt(1)-gr%zm(1) ) + azm(1)
-
- return
-
- end function linear_interpolated_azt
-
- !=============================================================================
- pure function linear_interpolated_aztk( azm, k )
-
- ! Description:
- ! Function to interpolate a variable located on the momentum grid levels
- ! (azm) to the thermodynamic grid levels (azt). This function outputs the
- ! value of azt at a single grid level (k) after interpolating using values
- ! of azm at two grid levels. The formulation used is compatible with a
- ! stretched (unevenly-spaced) grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_interpolation, only: linear_interp_factor
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azm
-
- integer, intent(in) :: k
-
- ! Return Variables
- real( kind = core_rknd ) :: linear_interpolated_aztk
-
- ! ---- Begin Code ----
-
- ! Do actual interpolation.
- ! Use a linear interpolation.
- if ( k /= 1 ) then
-
- linear_interpolated_aztk = &
- linear_interp_factor( gr%weights_zm2zt(1, k), azm(k), azm(k-1) )
-
- else
-
-! ! Set the value of azt at level 1 (the lowermost level in the model) to
-! ! the value of azm at level 1.
-! linear_interpolated_aztk = azm(1)
- ! Use a linear extension based on the values of azm at levels 1 and 2 to
- ! find the value of azt at level 1 (the lowermost level in the model).
- linear_interpolated_aztk = &
- ( ( azm(2)-azm(1) ) / ( gr%zm(2)-gr%zm(1) ) ) &
- * ( gr%zt(1)-gr%zm(1) ) + azm(1)
-
- endif
-
- return
-
- end function linear_interpolated_aztk
-
- !=============================================================================
- pure function cubic_interpolated_azt( azm )
-
- ! Description:
- ! Function to interpolate a variable located on the momentum grid
- ! levels (azm) to the thermodynamic grid levels (azt). This function outputs the
- ! value of azt at a all grid levels using Steffen's monotonic cubic
- ! interpolation implemented by Tak Yamaguchi.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- azm
-
- ! Return Variable
- real( kind = core_rknd ), dimension(gr%nz) :: &
- cubic_interpolated_azt
-
- ! Local Variable(s)
- real( kind = core_rknd ), dimension(gr%nz) :: &
- tmp ! This is needed for variables that self-reference
- integer :: &
- k
-
- ! ---- Begin Code ----
-
- forall ( k = 1 : gr%nz )
- tmp(k) = cubic_interpolated_aztk( azm, k )
- end forall
-
- cubic_interpolated_azt = tmp
-
- return
-
- end function cubic_interpolated_azt
-
-
- !=============================================================================
- pure function cubic_interpolated_aztk( azm, k )
-
- ! Description:
- ! Function to interpolate a variable located on the momentum grid
- ! levels (azm) to the thermodynamic grid levels (azt). This function outputs the
- ! value of azt at a single grid level (k) using Steffen's monotonic cubic
- ! interpolation implemented by Tak Yamaguchi.
- !
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_interpolation, only: mono_cubic_interp
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azm
-
- integer, intent(in) :: k
-
- ! Return Variable
- real( kind = core_rknd ) :: cubic_interpolated_aztk
-
- ! Local Variable(s)
- integer :: km1, k00, kp1, kp2
-
- ! ---- Begin Code ----
-
- ! Special case for a very small domain
- if ( gr%nz < 3 ) then
- cubic_interpolated_aztk = linear_interpolated_aztk( azm, k )
- return
- end if
-
- ! k levels are based on Tak's find_indices subroutine -dschanen 24 Oct 2011
- if ( k == gr%nz ) then
- km1 = gr%nz-2
- kp1 = gr%nz
- kp2 = gr%nz
- k00 = gr%nz-1
- else if ( k == 2 ) then
- km1 = 1
- kp1 = 2
- kp2 = 3
- k00 = 1
- else if ( k == 1 ) then ! Extrapolation for the ghost point
- km1 = gr%nz
- k00 = 1
- kp1 = 2
- kp2 = 3
- else
- km1 = k-2
- kp1 = k
- kp2 = k+1
- k00 = k-1
- end if
- ! Do the actual interpolation.
- ! Use a cubic monotonic spline interpolation.
- cubic_interpolated_aztk = &
- mono_cubic_interp( gr%zt(k), km1, k00, kp1, kp2, &
- gr%zm(km1), gr%zm(k00), gr%zm(kp1), gr%zm(kp2), &
- azm(km1), azm(k00), azm(kp1), azm(kp2) )
-
- return
-
- end function cubic_interpolated_aztk
-
- !=============================================================================
- pure function interpolated_aztk_imp( t_lev ) &
- result( azm_weight )
-
- ! Description:
- ! Function used to help in an interpolation of a variable (var_zm) located
- ! on the momentum grid levels (azm) to the thermodynamic grid levels (azt).
- ! This function computes a weighting factor for both the upper momentum
- ! level (k) and the lower momentum level (k-1) applied to the central
- ! thermodynamic level (k). For the lowermost thermodynamic grid
- ! level (k=1), a weighting factor for both the momentum level at 1 and the
- ! momentum level at 2 are calculated based on the use of a linear extension.
- ! This function outputs the weighting factors at a single thermodynamic grid
- ! level (k). The formulation used is compatible with a stretched
- ! (unevenly-spaced) grid. The weights are defined as follows:
- !
- ! ===var_zm(k)============================================= m(k)
- ! azm_weight(m_above) = factor
- ! -----------var_zm(interp)-------------------------------- t(k)
- ! azm_weight(m_below) = 1 - factor
- ! ===var_zm(k-1)=========================================== m(k-1)
- !
- ! The vertical indices m(k), t(k), and m(k-1) correspond with altitudes
- ! zm(k), zt(k), and zm(k-1), respectively. The letter "t" is used for
- ! thermodynamic levels and the letter "m" is used for momentum levels.
- !
- ! For all levels k > 1:
- !
- ! The formula for a linear interpolation is given by:
- !
- ! var_zm( interp to zt(k) )
- ! = [ ( var_zm(k) - var_zm(k-1) ) / ( zm(k) - zm(k-1) ) ]
- ! * ( zt(k) - zm(k-1) ) + var_zm(k-1);
- !
- ! which can be rewritten as:
- !
- ! var_zm( interp to zt(k) )
- ! = [ ( zt(k) - zm(k-1) ) / ( zm(k) - zm(k-1) ) ]
- ! * ( var_zm(k) - var_zm(k-1) ) + var_zm(k-1).
- !
- ! Furthermore, the formula can be rewritten as:
- !
- ! var_zm( interp to zt(k) )
- ! = factor * var_zm(k) + ( 1 - factor ) * var_zm(k-1);
- !
- ! where:
- !
- ! factor = ( zt(k) - zm(k-1) ) / ( zm(k) - zm(k-1) ).
- !
- ! One of the important uses of this function is in situations where the
- ! variable to be interpolated is being treated IMPLICITLY in an equation.
- ! Usually, the variable to be interpolated is involved in a derivative (such
- ! as d(var_zm)/dz in the diagram below). For the term of the equation
- ! containing the derivative, grid weights are needed for two interpolations,
- ! rather than just one interpolation. Thus, four grid weights (labeled
- ! A(k), B(k), C(k), and D(k) in the diagram below) are needed.
- !
- ! ===var_zm(k+1)=========================================== m(k+1)
- ! A(k)
- ! -----------var_zm(interp)-------------------------------- t(k+1)
- ! B(k) = 1 - A(k)
- ! ===var_zm(k)===========d(var_zm)/dz====================== m(k)
- ! C(k)
- ! -----------var_zm(interp)-------------------------------- t(k)
- ! D(k) = 1 - C(k)
- ! ===var_zm(k-1)=========================================== m(k-1)
- !
- ! The vertical indices m(k+1), t(k+1), m(k), t(k), and m(k-1) correspond
- ! with altitudes zm(k+1), zt(k+1), zm(k), zt(k), and zm(k-1), respectively.
- ! The letter "t" is used for thermodynamic levels and the letter "m" is used
- ! for momentum levels.
- !
- ! The grid weights, indexed around the central momentum level (k), are
- ! defined as follows:
- !
- ! A(k) = ( zt(k+1) - zm(k) ) / ( zm(k+1) - zm(k) );
- !
- ! which is the same as "factor" for the interpolation to thermodynamic
- ! level (k+1). In the code, this interpolation is referenced as
- ! gr%weights_zm2zt(m_above,tkp1), which can be read as "grid weight in a
- ! zm2zt interpolation of the momentum level above thermodynamic
- ! level (k+1) (applied to thermodynamic level (k+1))".
- !
- ! B(k) = 1 - [ ( zt(k+1) - zm(k) ) / ( zm(k+1) - zm(k) ) ]
- ! = 1 - A(k);
- !
- ! which is the same as "1 - factor" for the interpolation to thermodynamic
- ! level (k+1). In the code, this interpolation is referenced as
- ! gr%weights_zm2zt(m_below,tkp1), which can be read as "grid weight in a
- ! zm2zt interpolation of the momentum level below thermodynamic
- ! level (k+1) (applied to thermodynamic level (k+1))".
- !
- ! C(k) = ( zt(k) - zm(k-1) ) / ( zm(k) - zm(k-1) );
- !
- ! which is the same as "factor" for the interpolation to thermodynamic
- ! level (k). In the code, this interpolation is referenced as
- ! gr%weights_zm2zt(m_above,tk), which can be read as "grid weight in a zm2zt
- ! interpolation of the momentum level above thermodynamic level (k) (applied
- ! to thermodynamic level (k))".
- !
- ! D(k) = 1 - [ ( zt(k) - zm(k-1) ) / ( zm(k) - zm(k-1) ) ]
- ! = 1 - C(k);
- !
- ! which is the same as "1 - factor" for the interpolation to thermodynamic
- ! level (k). In the code, this interpolation is referenced as
- ! gr%weights_zm2zt(m_below,tk), which can be read as "grid weight in a zm2zt
- ! interpolation of the momentum level below thermodynamic level (k) (applied
- ! to thermodynamic level (k))".
- !
- ! Additionally, as long as the central momentum level (k) in the above
- ! scenario is not the lowermost momentum level or the uppermost momentum
- ! level (k /= 1 and k /= gr%nz), the four weighting factors have the
- ! following relationships: A(k) = C(k+1) and B(k) = D(k+1).
- !
- !
- ! Special condition for lowermost grid level, k = 1:
- !
- ! The lowermost thermodynamic grid level is below the lowermost momentum
- ! grid level. Thus, a linear extension is used at this level. It should
- ! be noted that the thermodynamic level k = 1 is considered to be below the
- ! model lower boundary, which is defined to be at momentum level k = 1.
- ! Thus, the values of most variables at thermodynamic level k = 1 are not
- ! often needed or referenced.
- !
- ! For level k = 1:
- !
- ! The formula for a linear extension is given by:
- !
- ! var_zm( extend to zt(k) )
- ! = [ ( var_zm(k+1) - var_zm(k) ) / ( zm(k+1) - zm(k) ) ]
- ! * ( zt(k) - zm(k) ) + var_zm(k);
- !
- ! which can be rewritten as:
- !
- ! var_zm( extend to zt(k) )
- ! = [ ( zt(k) - zm(k) ) / ( zm(k+1) - zm(k) ) ]
- ! * ( var_zm(k+1) - var_zm(k) ) + var_zm(k).
- !
- ! Furthermore, the formula can be rewritten as:
- !
- ! var_zm( extend to zt(k) )
- ! = factor * var_zm(k+1) + ( 1 - factor ) * var_zm(k);
- !
- ! where:
- !
- ! factor = ( zt(k) - zm(k) ) / ( zm(k+1) - zm(k) ).
- !
- ! Due to the fact that a linear extension is being used, the value of factor
- ! will be less than 0. The weight of the upper momentum level, which is
- ! momentum level k = 2, on thermodynamic level k = 1 equals the value of
- ! factor. The weight of the lower momentum level, which is momentum level
- ! k = 1, on thermodynamic level k = 1 equals 1 - factor, which is greater
- ! than 1. However, the sum of the weights equals 1.
- !
- !
- ! Brian Griffin; September 12, 2008.
- !
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- m_above = 1, & ! Upper momentum level.
- m_below = 2 ! Lower momentum level.
-
- ! Input
- integer, intent(in) :: t_lev ! Thermodynamic level index.
-
- ! Output
- real( kind = core_rknd ), dimension(2) :: azm_weight ! Weights of the momentum levels.
-
- ! Local Variables
- real( kind = core_rknd ) :: factor
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Compute the weighting factors at thermodynamic level k.
- k = t_lev
-
- if ( k /= 1 ) then
- ! At most levels, the thermodynamic level is found in-between two
- ! momentum levels. Linear interpolation is used.
- factor = ( gr%zt(k)-gr%zm(k-1) ) / ( gr%zm(k)-gr%zm(k-1) )
- else
- ! The bottom model level (1) is formulated differently because the bottom
- ! thermodynamic level is below the bottom momentum level. A linear
- ! extension is required, rather than linear interpolation.
- ! Note: Variable "factor" will have a negative sign in this situation.
- factor = ( gr%zt(1)-gr%zm(1) ) / ( gr%zm(2)-gr%zm(1) )
- endif
-
- ! Weight of upper momentum level on thermodynamic level.
- azm_weight(m_above) = factor
- ! Weight of lower momentum level on thermodynamic level.
- azm_weight(m_below) = 1.0_core_rknd - factor
-
- return
-
- end function interpolated_aztk_imp
-
- !=============================================================================
- pure function gradzm( azm )
-
- ! Description:
- ! Function to compute the vertical derivative of a variable (azm) located on
- ! the momentum grid. The results are returned in an array defined on the
- ! thermodynamic grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variable
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azm
-
- ! Return Variable
- real( kind = core_rknd ), dimension(gr%nz) :: gradzm
-
- ! Local Variable
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Compute vertical derivatives.
- forall( k = gr%nz : 2 : -1 )
- ! Take derivative of momentum-level variable azm over the central
- ! thermodynamic level (k).
- gradzm(k) = ( azm(k) - azm(k-1) ) * gr%invrs_dzt(k)
- end forall ! gr%nz .. 2
-! ! Thermodynamic level 1 is located below momentum level 1, so there is not
-! ! enough information to calculate the derivative over thermodynamic
-! ! level 1. Thus, the value of the derivative at thermodynamic level 1 is
-! ! set equal to 0. This formulation is consistent with setting the value of
-! ! the variable azm below the model grid to the value of the variable azm at
-! ! the lowest grid level.
-! gradzm(1) = 0.
- ! Thermodynamic level 1 is located below momentum level 1, so there is not
- ! enough information to calculate the derivative over thermodynamic level 1.
- ! Thus, the value of the derivative at thermodynamic level 1 is set equal to
- ! the value of the derivative at thermodynamic level 2. This formulation is
- ! consistent with using a linear extension to find the values of the
- ! variable azm below the model grid.
- gradzm(1) = gradzm(2)
-
- return
-
- end function gradzm
-
- !=============================================================================
- pure function gradzt( azt )
-
- ! Description:
- ! Function to compute the vertical derivative of a variable (azt) located on
- ! the thermodynamic grid. The results are returned in an array defined on
- ! the momentum grid.
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variable
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: azt
-
- ! Output Variable
- real( kind = core_rknd ), dimension(gr%nz) :: gradzt
-
- ! Local Variable
- integer :: k
-
- ! ---- Begin Code ----
-
- ! Compute vertical derivative.
- forall( k = 1 : gr%nz-1 : 1 )
- ! Take derivative of thermodynamic-level variable azt over the central
- ! momentum level (k).
- gradzt(k) = ( azt(k+1) - azt(k) ) * gr%invrs_dzm(k)
- end forall ! 1 .. gr%nz-1
-! ! Momentum level gr%nz is located above thermodynamic level gr%nz, so
-! ! there is not enough information to calculate the derivative over momentum
-! ! level gr%nz. Thus, the value of the derivative at momentum level
-! ! gr%nz is set equal to 0. This formulation is consistent with setting
-! ! the value of the variable azt above the model grid to the value of the
-! ! variable azt at the highest grid level.
-! gradzt(gr%nz) = 0.
- ! Momentum level gr%nz is located above thermodynamic level gr%nz, so
- ! there is not enough information to calculate the derivative over momentum
- ! level gr%nz. Thus, the value of the derivative at momentum level
- ! gr%nz is set equal to the value of the derivative at momentum level
- ! gr%nz-1. This formulation is consistent with using a linear extension
- ! to find the values of the variable azt above the model grid.
- gradzt(gr%nz) = gradzt(gr%nz-1)
-
- return
-
- end function gradzt
-
- !=============================================================================
- pure function flip( x, xdim )
-
- ! Description:
- ! Flips a single dimension array (i.e. a vector), so the first element
- ! becomes the last and vice versa for the whole column. This is a
- ! necessary part of the code because BUGSrad and CLUBB store altitudes in
- ! reverse order.
- !
- ! References:
- ! None
- !-------------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- dp ! double precision
-
- implicit none
-
- ! Input Variables
- integer, intent(in) :: xdim
-
- real(kind = dp), dimension(xdim), intent(in) :: x
-
- ! Output Variables
- real(kind = dp), dimension(xdim) :: flip
-
- ! Local Variables
- real(kind = dp), dimension(xdim) :: tmp
-
- integer :: indx
-
- ! ---- Begin Code ----
-
- forall ( indx = 1 : xdim )
- tmp(indx) = x((xdim+1) - (indx))
- end forall
-
- flip = tmp
-
- return
- end function flip
-
-!===============================================================================
-
-end module crmx_grid_class
diff --git a/src/physics/spcam/crm/CLUBB/crmx_hydrostatic_module.F90 b/src/physics/spcam/crm/CLUBB/crmx_hydrostatic_module.F90
deleted file mode 100644
index 48231ba015..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_hydrostatic_module.F90
+++ /dev/null
@@ -1,746 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: hydrostatic_module.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_hydrostatic_module
-
- implicit none
-
- private ! Default Scope
-
- public :: hydrostatic, &
- inverse_hydrostatic
-
- private :: calc_exner_const_thvm, &
- calc_exner_linear_thvm, &
- calc_z_linear_thvm
-
- contains
-
-!===============================================================================
- subroutine hydrostatic( thvm, p_sfc, &
- p_in_Pa, p_in_Pa_zm, &
- exner, exner_zm, &
- rho, rho_zm )
-
- ! Description:
- ! This subroutine integrates the hydrostatic equation.
- !
- ! The hydrostatic equation is of the form:
- !
- ! dp/dz = - rho * grav.
- !
- ! This equation can be re-written in terms of d(exner)/dz, such that:
- !
- ! [ { p0^(R_d/C_p) * p^(C_v/C_p) } / { R_d * rho } ] * d(exner)/dz
- ! = - grav / C_p;
- !
- ! which can also be expressed as:
- !
- ! [ { p0^(R_d/C_p) * p^(C_v/C_p) } / { R_d * rho_d * ( 1 + r_v + r_c ) } ]
- ! * d(exner)/dz
- ! = - grav / C_p.
- !
- ! Furthermore, the moist equation of state can be written as:
- !
- ! theta =
- ! [ { p0^(R_d/C_p) * p^(C_v/C_p) }
- ! / { R_d * rho_d * ( 1 + (R_v/R_d)*r_v ) } ].
- !
- ! The relationship between theta and theta_v (including water vapor and
- ! cloud water) is:
- !
- ! theta_v = theta * [ ( 1 + (R_v/R_d)*r_v ) / ( 1 + r_v + r_c ) ];
- !
- ! which, when substituted into the above equation, changes the equation of
- ! state to:
- !
- ! theta_v =
- ! [ { p0^(R_d/C_p) * p^(C_v/C_p) }
- ! / { R_d * rho_d * ( 1 + r_v + r_c ) } ].
- !
- ! This equation is substituted into the d(exner)/dz form of the hydrostatic
- ! equation, resulting in:
- !
- ! theta_v * d(exner)/dz = - grav / C_p;
- !
- ! which can be re-written as:
- !
- ! d(exner)/dz = - grav / ( C_p * theta_v ).
- !
- ! This subroutine integrates the above equation to solve for exner, such
- ! that:
- !
- ! INT(exner_1:exner_2) d(exner) =
- ! - ( grav / C_p ) * INT(z_1:z_2) ( 1 / theta_v ) dz.
- !
- !
- ! The resulting value of exner is used to calculate pressure. Then, the
- ! values of pressure, exner, and theta_v can be used to calculate density.
-
- ! References:
- !
- !------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- kappa, & ! Variable(s)
- p0, &
- Rd, &
- zero_threshold
-
- use crmx_grid_class, only: &
- gr, & ! Variable(s)
- zm2zt, & ! Procedure(s)
- zt2zm
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- p_sfc ! Pressure at the surface [Pa]
-
- real( kind = core_rknd ), intent(in), dimension(gr%nz) :: &
- thvm ! Virtual potential temperature [K]
-
- ! Output Variables
- real( kind = core_rknd ), intent(out), dimension(gr%nz) :: &
- p_in_Pa, & ! Pressure (thermodynamic levels) [Pa]
- p_in_Pa_zm, & ! Pressure on momentum levels [Pa]
- exner, & ! Exner function (thermodynamic levels) [-]
- exner_zm, & ! Exner function on momentum levels [-]
- rho, & ! Density (thermodynamic levels) [kg/m^3]
- rho_zm ! Density on momentum levels [kg/m^3]
-
- ! Local Variables
- real( kind = core_rknd ), dimension(gr%nz) :: &
- thvm_zm ! Theta_v interpolated to momentum levels [K]
-
- real( kind = core_rknd ) :: &
- dthvm_dz ! Constant d(thvm)/dz between successive levels [K/m]
-
- integer :: k
-
- ! Interpolate thvm from thermodynamic to momentum levels. Linear
- ! interpolation is used, except for the uppermost momentum level, where a
- ! linear extension is used. Since thvm is considered to either be constant
- ! or vary linearly over the depth of a grid level, this interpolation is
- ! consistent with the rest of this code.
- thvm_zm = zt2zm( thvm )
-
- ! Exner is defined on thermodynamic grid levels except for the value at
- ! index 1. Since thermodynamic level 1 is below the surface, it is
- ! disregarded, and the value of exner(1) corresponds to surface value, which
- ! is actually at momentum level 1.
- exner(1) = ( p_sfc/p0 )**kappa
- exner_zm(1) = ( p_sfc/p0 )**kappa
-
- ! Consider the value of exner at thermodynamic level (2) to be based on
- ! a constant thvm between thermodynamic level (2) and momentum level (1),
- ! which is the surface or model lower boundary. Since thlm(1) is set equal
- ! to thlm(2), the values of thvm are considered to be basically constant
- ! near the ground.
- exner(2) &
- = calc_exner_const_thvm( thvm(2), gr%zt(2), gr%zm(1), exner(1) )
-
- ! Given the value of exner at thermodynamic level k-1, and considering
- ! thvm to vary linearly between its values at thermodynamic levels k
- ! and k-1, the value of exner can be found at thermodynamic level k,
- ! as well as at intermediate momentum level k-1.
- do k = 3, gr%nz
-
- dthvm_dz = gr%invrs_dzm(k-1) * ( thvm(k) - thvm(k-1) )
-
- if ( dthvm_dz /= 0.0_core_rknd ) then
-
- exner(k) &
- = calc_exner_linear_thvm( thvm(k-1), dthvm_dz, &
- gr%zt(k-1), gr%zt(k), exner(k-1) )
-
- exner_zm(k-1) &
- = calc_exner_linear_thvm( thvm(k-1), dthvm_dz, &
- gr%zt(k-1), gr%zm(k-1), exner(k-1) )
-
- else ! dthvm_dz = 0
-
- exner(k) &
- = calc_exner_const_thvm &
- ( thvm(k), gr%zt(k), gr%zt(k-1), exner(k-1) )
-
- exner_zm(k-1) &
- = calc_exner_const_thvm &
- ( thvm(k), gr%zm(k-1), gr%zt(k-1), exner(k-1) )
-
- endif
-
- enddo ! k = 3, gr%nz
-
- ! Find the value of exner_zm at momentum level gr%nz by using a linear
- ! extension of thvm from the two thermodynamic level immediately below
- ! momentum level gr%nz.
- dthvm_dz = ( thvm_zm(gr%nz) - thvm(gr%nz) ) &
- / ( gr%zm(gr%nz) - gr%zt(gr%nz) )
-
- if ( dthvm_dz /= 0.0_core_rknd ) then
-
- exner_zm(gr%nz) &
- = calc_exner_linear_thvm &
- ( thvm(gr%nz), dthvm_dz, &
- gr%zt(gr%nz), gr%zm(gr%nz), exner(gr%nz) )
-
- else ! dthvm_dz = 0
-
- exner_zm(gr%nz) &
- = calc_exner_const_thvm &
- ( thvm(gr%nz), gr%zm(gr%nz), gr%zt(gr%nz), exner(gr%nz) )
-
- endif
-
- ! Calculate pressure based on the values of exner.
-
- do k = 1, gr%nz
- p_in_Pa(k) = p0 * exner(k)**( 1._core_rknd/kappa )
- p_in_Pa_zm(k) = p0 * exner_zm(k)**( 1._core_rknd/kappa )
- enddo
-
- ! Calculate density based on pressure, exner, and thvm.
-
- do k = 1, gr%nz
- rho(k) = p_in_Pa(k) / ( Rd * thvm(k) * exner(k) )
- rho_zm(k) = p_in_Pa_zm(k) / ( Rd * thvm_zm(k) * exner_zm(k) )
- enddo
-
-
- return
- end subroutine hydrostatic
-
-!===============================================================================
- subroutine inverse_hydrostatic( p_sfc, zm_init, nlevels, thvm, exner, &
- z )
-
- ! Description:
- ! Subprogram to integrate the inverse of hydrostatic equation
-
- ! References:
- !
- !------------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- p0, & ! Constant(s)
- kappa, &
- fstderr
-
- use crmx_interpolation, only: &
- binary_search ! Procedure(s)
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- p_sfc, & ! Pressure at the surface [Pa]
- zm_init ! Altitude at the surface [m]
-
- integer, intent(in) :: &
- nlevels ! Number of levels in the sounding [-]
-
- real( kind = core_rknd ), intent(in), dimension(nlevels) :: &
- thvm, & ! Virtual potential temperature [K]
- exner ! Exner function [-]
-
- ! Output Variables
- real( kind = core_rknd ), intent(out), dimension(nlevels) :: &
- z ! Height [m]
-
- ! Local Variables
- integer :: k
-
- real( kind = core_rknd ), dimension(nlevels) :: &
- ref_z_snd ! Altitude minus altitude of the lowest sounding level [m]
-
- real( kind = core_rknd ), dimension(nlevels) :: &
- exner_reverse_array ! Array of exner snd. values in reverse order [-]
-
- real( kind = core_rknd ) :: &
- exner_sfc, & ! Value of exner at the surface [-]
- ref_z_sfc, & ! Alt. diff between surface and lowest snd. level [m]
- z_snd_bottom, & ! Altitude of the bottom of the input sounding [m]
- dthvm_dexner ! Constant rate of change of thvm with respect to
- ! exner between sounding levels k-1 and k [K]
-
- integer :: &
- rev_low_idx, &
- low_idx, &
- high_idx
-
-
- ! Variable ref_z_sfc is initialized to 0.0 to avoid a compiler warning.
- ref_z_sfc = 0.0_core_rknd
-
- ! The variable ref_z_snd is the altitude of each sounding level compared to
- ! the altitude of the lowest sounding level. Thus, the value of ref_z_snd
- ! at sounding level 1 is 0. The lowest sounding level may or may not be
- ! right at the surface, and therefore an adjustment may be required to find
- ! the actual altitude above ground.
- ref_z_snd(1) = 0.0_core_rknd
-
- do k = 2, nlevels
-
- ! The value of thvm is given at two successive sounding levels. For
- ! purposes of achieving a quality estimate of altitude at each pressure
- ! sounding level, the value of thvm is considered to vary linearly
- ! with respect to exner between two successive sounding levels. Thus,
- ! there is a constant d(thvm)/d(exner) between the two successive
- ! sounding levels. If thvm is constant, then d(thvm)/d(exner) is 0.
- dthvm_dexner = ( thvm(k) - thvm(k-1) ) / ( exner(k) - exner(k-1) )
-
- ! Calculate the value of the reference height at sounding level k, based
- ! the value of thvm at sounding level k-1, the constant value of
- ! d(thvm)/d(exner), the value of exner at sounding levels k-1 and k, and
- ! the reference altitude at sounding level k-1.
- ref_z_snd(k) &
- = calc_z_linear_thvm( thvm(k-1), dthvm_dexner, &
- exner(k-1), exner(k), ref_z_snd(k-1) )
-
- enddo
-
- ! Find the actual (above ground) altitude of the sounding levels from the
- ! reference altitudes.
-
- ! The pressure at the surface (or model lower boundary), p_sfc, is found at
- ! the altitude of the surface (or model lower boundary), zm_init.
-
- ! Find the value of exner at the surface from the pressure at the surface.
- exner_sfc = ( p_sfc / p0 )**kappa
-
- ! Find the value of exner_sfc compared to the values of exner in the exner
- ! sounding profile.
-
- if ( exner_sfc < exner(nlevels) ) then
-
- ! Since the values of exner decrease monotonically with height (and thus
- ! with sounding level), the value of exner_sfc is less than all the
- ! values of exner in the sounding (and thus the surface is located above
- ! all the levels of the sounding), then there is insufficient information
- ! to run the model. Stop the run.
-
- write(fstderr,*) "The entire sounding is below the model surface."
- stop
-
- elseif ( exner_sfc > exner(1) ) then
-
- ! Since the values of exner decrease monotonically with height (and thus
- ! with sounding level), the value of exner_sfc is greater than all the
- ! values of exner in the sounding (and thus the surface is located below
- ! all the levels of the sounding), use a linear extension of thvm to find
- ! thvm at the surface. Thus, d(thvm)/d(exner) is the same as its value
- ! between sounding levels 1 and 2. If the surface is so far below the
- ! sounding that gr%zt(2) is below the first sounding level, the code in
- ! subroutine read_sounding (found in sounding.F90) will stop the run.
-
- ! Calculate the appropriate d(thvm)/d(exner).
- dthvm_dexner = ( thvm(2) - thvm(1) ) / ( exner(2) - exner(1) )
-
- ! Calculate the difference between the altitude of the surface (or model
- ! lower boundary) and the altitude of the lowest level of the sounding.
- ref_z_sfc &
- = calc_z_linear_thvm( thvm(1), dthvm_dexner, &
- exner(1), exner_sfc, ref_z_snd(1) )
-
- else ! exner(nlevels) < exner_sfc < exner(1)
-
- ! Since the values of exner decrease monotonically with height (and thus
- ! with sounding level), the value of exner_sfc is between two values of
- ! exner (at some levels k-1 and k) in the sounding, and the value of
- ! d(thvm)/d(exner) is the same as between those two levels in the above
- ! calculation.
-
- ! The value of exner_sfc is between two levels of the exner sounding.
- ! Find the index of the lower level.
-
- ! In order to use the binary search, the array must be sorted from least
- ! value to greatest value. Since exner decreases with altitude (and
- ! vertical level), the array that is sent to function binary_search must
- ! be the exact reverse of exner.
- ! Thus, exner(1) becomes exner_reverse_array(nlevels), exner(nlevels)
- ! becomes exner_reverse_array(1), etc.
- do k = 1, nlevels, 1
- exner_reverse_array(k) = exner(nlevels-k+1)
- enddo
- ! The output from the binary search yields the first value in the
- ! exner_reverse_array that is greater than or equal to exner_sfc. Thus,
- ! in regards to the regular exner array, this is the reverse index of
- ! the lower sounding level for exner_sfc. For example, if exner_sfc
- ! is found between exner(1) and exner(2), the binary search for exner_sfc
- ! in regards to exner_reverse_index will return a value of nlevels.
- ! Once the actual lower level index is calculated, the result will be 1.
- rev_low_idx = binary_search( nlevels, exner_reverse_array, exner_sfc )
-
- ! Find the lower level index for the regular exner profile from the
- ! lower level index for the reverse exner profile.
- low_idx = nlevels - rev_low_idx + 1
-
- ! Find the index of the upper level.
- high_idx = low_idx + 1
-
- ! Calculate the appropriate d(thvm)/d(exner).
- dthvm_dexner = ( thvm(high_idx) - thvm(low_idx) ) &
- / ( exner(high_idx) - exner(low_idx) )
-
- ! Calculate the difference between the altitude of the surface (or model
- ! lower boundary) and the altitude of the lowest level of the sounding.
- ref_z_sfc &
- = calc_z_linear_thvm( thvm(low_idx), dthvm_dexner, &
- exner(low_idx), exner_sfc, ref_z_snd(low_idx) )
-
- endif ! exner_sfc
-
- ! Find the altitude of the bottom of the sounding.
- z_snd_bottom = zm_init - ref_z_sfc
-
- ! Calculate the sounding altitude profile based
- ! on z_snd_bottom and ref_z_snd.
- do k = 1, nlevels, 1
- z(k) = z_snd_bottom + ref_z_snd(k)
- enddo
-
-
- return
- end subroutine inverse_hydrostatic
-
-!===============================================================================
- pure function calc_exner_const_thvm( thvm, z_2, z_1, exner_1 ) &
- result( exner_2 )
-
- ! Description:
- ! This function solves for exner at a level, given exner at another level,
- ! the altitudes of both levels, and a constant thvm over the depth of the
- ! level.
- !
- ! The derivative of exner is given by the following equation:
- !
- ! d(exner)/dz = - grav / (Cp * thvm).
- !
- ! This equation is integrated to solve for exner, such that:
- !
- ! INT(exner_1:exner_2) d(exner)
- ! = - ( grav / Cp ) INT(z_1:z_2) (1/thvm) dz.
- !
- ! Since thvm is considered to be a constant over the depth of the layer
- ! between z_1 and z_2, the equation can be written as:
- !
- ! INT(exner_1:exner_2) d(exner) = - grav / ( Cp * thvm ) INT(z_1:z_2) dz.
- !
- ! Solving the integral:
- !
- ! exner_2 = exner_1 - [ grav / ( Cp * thvm ) ] * ( z_2 - z_1 ).
-
- ! References:
- !-------------------------------------------------------------------
-
- use crmx_constants_clubb, only: &
- grav, & ! Gravitational acceleration [m/s^2]
- Cp ! Specific heat of dry air at const. pressure [J/(kg*K)]
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- thvm, & ! Constant value of thvm over the layer [K]
- z_2, & ! Altitude at the top of the layer [m]
- z_1, & ! Altitude at the bottom of the layer [m]
- exner_1 ! Exner at the bottom of the layer [-]
-
- ! Return Variable
- real( kind = core_rknd ) :: exner_2 ! Exner at the top of the layer [-]
-
- ! Calculate exner at top of the layer.
- exner_2 = exner_1 - ( grav / ( Cp * thvm ) ) * ( z_2 - z_1 )
-
- return
- end function calc_exner_const_thvm
-
-!===============================================================================
- pure function calc_exner_linear_thvm( thvm_km1, dthvm_dz, &
- z_km1, z_2, exner_km1 ) &
- result( exner_2 )
-
- ! Description:
- ! This function solves for exner at a level, given exner at another level,
- ! the altitudes of both levels, and a value of thvm that is considered to
- ! vary linearly over the depth of the level.
- !
- ! The derivative of exner is given by the following equation:
- !
- ! d(exner)/dz = - grav / (Cp * thvm).
- !
- ! This equation is integrated to solve for exner, such that:
- !
- ! INT(exner_1:exner_2) d(exner)
- ! = - ( grav / Cp ) INT(z_1:z_2) (1/thvm) dz.
- !
- ! The value of thvm is considered to vary linearly (with respect to height)
- ! over the depth of the level (resulting in a constant d(thvm)/dz over the
- ! depth of the level). The entire level between z_1 and z_2 must be
- ! encompassed between two levels with two known values of thvm. The value
- ! of thvm at the upper level (z_up) is called thvm_up, and the value of thvm
- ! at the lower level (z_low) is called thvm_low. Again, the values of thvm
- ! at all interior altitudes, z_low <= z_1 < z <= z_2 <= z_up, behave
- ! linearly between thvm_low and thvm_up, such that:
- !
- ! thvm(z)
- ! = [ ( thvm_up - thvm_low ) / ( z_up - z_low ) ] * ( z - z_low)
- ! + thvm_low
- ! = [ d(thvm)/dz ] * ( z - z_low ) + thvm_low
- ! = C_a*z + C_b;
- !
- ! where:
- !
- ! C_a
- ! = ( thvm_up - thvm_low ) / ( z_up - z_low )
- ! = d(thvm)/dz;
- !
- ! and:
- !
- ! C_b
- ! = thvm_low - [ ( thvm_up - thvm_low ) / ( z_up - z_low ) ] * z_low
- ! = thvm_low - [ d(thvm)/dz ] * z_low.
- !
- ! The integral becomes:
- !
- ! INT(exner_1:exner_2) d(exner)
- ! = - ( grav / Cp ) INT(z_1:z_2) [ 1 / ( C_a*z + C_b ) ] dz.
- !
- ! Performing a u-substitution ( u = C_a*z + C_b ), the equation becomes:
- !
- ! INT(exner_1:exner_2) d(exner)
- ! = - ( grav / Cp ) * ( 1 / C_a ) INT(z=z_1:z=z_2) (1/u) du.
- !
- ! Solving the integral, and then re-substituting for u:
- !
- ! exner_2 = exner_1
- ! - ( grav / Cp ) * ( 1 / C_a )
- ! * ln [ ( C_a*z_2 + C_b ) / ( C_a*z_1 + C_b ) ].
- !
- ! Re-substituting for C_a and C_b:
- !
- ! exner_2
- ! = exner_1
- ! - ( grav / Cp ) * ( 1 / {d(thvm)/dz} )
- ! * ln [ ( {d(thvm)/dz}*z_2 + thvm_low - {d(thvm)/dz}*z_low )
- ! / ( {d(thvm)/dz}*z_1 + thvm_low - {d(thvm)/dz}*z_low ) ].
- !
- ! This equation is used to calculate exner_2 using exner_1, which is at the
- ! same level as z_1. Furthermore, thvm_low and z_low are taken from the
- ! same level as z_1 and exner_1. Thus, z_1 = z_low. Therefore:
- !
- ! exner_2
- ! = exner_low
- ! - ( grav / Cp ) * ( 1 / {d(thvm)/dz} )
- ! * ln [ ( thvm_low + {d(thvm)/dz}*(z_2-z_low) ) / thvm_low ].
- !
- ! Considering either a thermodynamic or sounding level k-1 as the low level
- ! in the integration, and that thvm varies linearly between level k-1 and
- ! level k:
- !
- ! exner_2
- ! = exner(k-1)
- ! - ( grav / Cp ) * ( 1 / {d(thvm)/dz} )
- ! * ln [ ( thvm(k-1) + {d(thvm)/dz}*(z_2-z(k-1)) ) / thvm(k-1) ];
- !
- ! where:
- !
- ! d(thvm)/dz = ( thvm(k) - thvm(k-1) ) / ( z(k) - z(k-1) );
- !
- ! and where z(k-1) < z_2 <= z(k); and {d(thvm)/dz} /= 0. If the value of
- ! {d(thvm)/dz} is 0, then thvm is considered to be a constant over the depth
- ! of the level. The appropriate equation is found in pure function
- ! calc_exner_const_thvm.
-
- ! References:
- !-------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: &
- grav, & ! Gravitational acceleration [m/s^2]
- Cp ! Specific heat of dry air at const. pressure [J/(kg*K)]
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- thvm_km1, & ! Value of thvm at level k-1 [K]
- dthvm_dz, & ! Constant d(thvm)/dz between levels k-1 and k [K/m]
- z_km1, & ! Altitude at level k-1 [m]
- z_2, & ! Altitude at the top of the layer [m]
- exner_km1 ! Exner at level k-1 [-]
-
- ! Return Variable
- real( kind = core_rknd ) :: exner_2 ! Exner at the top of the layer [-]
-
- ! Calculate exner at the top of the layer.
- exner_2 &
- = exner_km1 &
- - ( grav / Cp ) * ( 1.0_core_rknd / dthvm_dz ) &
- * log( ( thvm_km1 + dthvm_dz * ( z_2 - z_km1 ) ) / thvm_km1 )
-
- return
- end function calc_exner_linear_thvm
-
-!===============================================================================
- pure function calc_z_linear_thvm( thvm_km1, dthvm_dexner, &
- exner_km1, exner_2, z_km1 ) &
- result( z_2 )
-
- ! Description:
- ! This function solves for z (altitude) at a level, given altitude at
- ! another level, the values of exner at both levels, and a value of thvm
- ! that is considered to vary linearly over the depth of the level.
- !
- ! The derivative of exner is given by the following equation:
- !
- ! d(exner)/dz = - grav / (Cp * thvm).
- !
- ! This equation is integrated to solve for z, such that:
- !
- ! INT(exner_1:exner_2) thvm d(exner) = - ( grav / Cp ) INT(z_1:z_2) dz.
- !
- ! The value of thvm is considered to vary linearly (with respect to exner)
- ! over the depth of the level (resulting in a constant d(thvm)/d(exner) over
- ! the depth of the level). The entire level between exner_1 and exner_2
- ! must be encompassed between two levels with two known values of thvm. The
- ! value of thvm at the upper level (exner_up) is called thvm_up, and the
- ! value of thvm at the lower level (exner_low) is called thvm_low. Again,
- ! the values of thvm at all interior exner levels,
- ! exner_low >= exner_1 > exner >= exner_2 >= exner_up, behave linearly
- ! between thvm_low and thvm_up, such that:
- !
- ! thvm(exner)
- ! = [ ( thvm_up - thvm_low ) / ( exner_up - exner_low ) ]
- ! * ( exner - exner_low )
- ! + thvm_low
- ! = [ d(thvm)/d(exner) ] * ( exner - exner_low ) + thvm_low
- ! = C_a*z + C_b;
- !
- ! where:
- !
- ! C_a
- ! = ( thvm_up - thvm_low ) / ( exner_up - exner_low )
- ! = d(thvm)/d(exner);
- !
- ! and:
- !
- ! C_b
- ! = thvm_low
- ! - [ ( thvm_up - thvm_low ) / ( exner_up - exner_low ) ] * exner_low
- ! = thvm_low - [ d(thvm)/d(exner) ] * exner_low.
- !
- ! The integral becomes:
- !
- ! INT(exner_1:exner_2) ( C_a*exner + C_b ) d(exner)
- ! = - ( grav / Cp ) INT(z_1:z_2) dz.
- !
- ! Solving the integral:
- !
- ! z_2
- ! = z_1
- ! - ( Cp / grav )
- ! * [ (1/2) * {d(thvm)/d(exner)} * ( {exner_2}^2 - {exner_1}^2 )
- ! + ( thvm_low - {d(thvm)/d(exner)} * exner_low )
- ! * ( exner_2 - exner_1 ) ].
- !
- ! This equation is used to calculate z_2 using z_1, which is at the same
- ! level as exner_1. Furthermore, thvm_low and exner_low are taken from the
- ! same level as exner_1 and z_1. Thus, exner_1 = exner_low. Therefore:
- !
- ! z_2
- ! = z_low
- ! - ( Cp / grav )
- ! * [ (1/2) * {d(thvm)/d(exner)} * ( {exner_2}^2 - {exner_low}^2 )
- ! + ( thvm_low - {d(thvm)/d(exner)} * exner_low )
- ! * ( exner_2 - exner_low ) ].
- !
- ! Considering a sounding level k-1 as the low level in the integration, and
- ! that thvm varies linearly (with respect to exner) between level k-1 and
- ! level k:
- !
- ! z_2
- ! = z(k-1)
- ! - ( Cp / grav )
- ! * [ (1/2) * {d(thvm)/d(exner)} * ( {exner_2}^2 - {exner(k-1)}^2 )
- ! + ( thvm(k-1) - {d(thvm)/d(exner)} * exner(k-1) )
- ! * ( exner_2 - exner(k-1) ) ];
- !
- ! where:
- !
- ! d(thvm)/d(exner)
- ! = ( thvm(k) - thvm(k-1) ) / ( exner(k) - exner(k-1) );
- !
- ! and where exner(k-1) > exner_2 >= exner(k). If the value of
- ! d(thvm)/d(exner) is 0, then thvm is considered to be a constant over the
- ! depth of the level, and the equation will reduce to:
- !
- ! z_2 = z(k-1) - ( Cp / grav ) * thvm(k-1) * ( exner_2 - exner(k-1) ).
- !
- !
- ! IMPORTANT NOTE:
- !
- ! CLUBB is an altitude-based model. All linear interpolations (and
- ! extensions) are based on considering a variable to change linearly with
- ! respect to altitude, rather than with respect to exner. An exception is
- ! made here to calculate the altitude of a sounding level based on a
- ! sounding given in terms of a pressure coordinate rather than a height
- ! coordinate. After the altitude of the sounding level has been calculated,
- ! the values of the sounding variables are interpolated onto the model grid
- ! linearly with respect to altitude. Therefore, considering a variable to
- ! change linearly with respect to exner is not consistent with the rest of
- ! the model code, but provides for a better estimation of the altitude of
- ! the sounding levels (than simply considering thvm to be constant over the
- ! depth of the sounding level).
-
- ! References:
- !-------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_constants_clubb, only: &
- grav, & ! Gravitational acceleration [m/s^2]
- Cp ! Specific heat of dry air at const. pressure [J/(kg*K)]
-
- implicit none
-
- ! Input Variables
- real( kind = core_rknd ), intent(in) :: &
- thvm_km1, & ! Value of thvm at sounding level k-1 [K]
- dthvm_dexner, & ! Constant d(thvm)/d(exner) between levels k-1 and k [K]
- exner_km1, & ! Value of exner at sounding level k-1 [-]
- exner_2, & ! Value of exner at the top of the layer [-]
- z_km1 ! Altitude at sounding level k-1 [m]
-
- ! Return Variable
- real( kind = core_rknd ) :: z_2 ! Altitude at the top of the layer [m]
-
- ! Calculate z_2 at the top of the layer.
- z_2 &
- = z_km1 &
- - ( Cp / grav ) &
- * ( 0.5_core_rknd * dthvm_dexner * ( exner_2**2 - exner_km1**2 ) &
- + ( thvm_km1 - dthvm_dexner * exner_km1 ) &
- * ( exner_2 - exner_km1 ) &
- )
-
- return
- end function calc_z_linear_thvm
-
-!===============================================================================
-
-end module crmx_hydrostatic_module
diff --git a/src/physics/spcam/crm/CLUBB/crmx_hyper_diffusion_4th_ord.F90 b/src/physics/spcam/crm/CLUBB/crmx_hyper_diffusion_4th_ord.F90
deleted file mode 100644
index a59714a42b..0000000000
--- a/src/physics/spcam/crm/CLUBB/crmx_hyper_diffusion_4th_ord.F90
+++ /dev/null
@@ -1,1685 +0,0 @@
-!-----------------------------------------------------------------------
-! $Id: hyper_diffusion_4th_ord.F90 5623 2012-01-17 17:55:26Z connork@uwm.edu $
-!===============================================================================
-module crmx_hyper_diffusion_4th_ord
-
- ! Description:
- ! Module hyper_diffusion_4th_ord computes the 4th-order numerical diffusion
- ! for any equation to which it is applied. Hyper-diffusion will only be
- ! called if the model flag l_hyper_dfsn is set to true. Function
- ! hyper_dfsn_4th_ord_zt_lhs handles 4th-order hyper-diffusion for variables
- ! that reside on thermodynamic levels. Function hyper_dfsn_4th_ord_zm_lhs
- ! handles 4th-order hyper-diffusion for variables that reside on momentum
- ! levels. A special constant coefficient of 4th-order numerical diffusion,
- ! nu_hd (which is sent in this module as nu), is used and has units of m^4/s.
-
- implicit none
-
- private ! Default Scope
-
- public :: hyper_dfsn_4th_ord_zt_lhs, &
- hyper_dfsn_4th_ord_zm_lhs
-
- contains
-
- !=============================================================================
- pure function hyper_dfsn_4th_ord_zt_lhs( boundary_cond, nu, invrs_dzt, &
- invrs_dzm, invrs_dzmm1, &
- invrs_dztp1, invrs_dztm1, &
- invrs_dzmp1, invrs_dzmm2, level ) &
- result( lhs )
-
- ! Note: In the "Description" section of this function, the variable
- ! "invrs_dzm" will be written as simply "dzm", and the variable
- ! "invrs_dzt" will be written as simply "dzt". This is being done as
- ! as device to save space and to make some parts of the description
- ! more readable. This change does not pertain to the actual code.
-
- ! Description:
- ! Vertical 4th-order numerical diffusion of var_zt: implicit portion of the
- ! code.
- !
- ! Fourth-order numerical diffusion, or fourth-order hyper-diffusion, is used
- ! to help eliminate small-scale noise without altering larger-scale
- ! features.
- !
- ! The variable "var_zt" stands for a variable that is located at
- ! thermodynamic grid levels.
- !
- ! The d(var_zt)/dt equation contains a 4th-order numerical diffusion term:
- !
- ! - nu * d^4(var_zt)/dz^4.
- !
- ! This term is solved for completely implicitly, such that:
- !
- ! - nu * d^4( var_zt(t+1) )/dz^4.
- !
- ! Note: When the term is brought over to the left-hand side, the sign
- ! is reversed and the leading "-" in front of the term is changed
- ! to a "+".
- !
- ! The timestep index (t+1) means that the value of var_zt being used is from
- ! the next timestep, which is being advanced to in solving the d(var_zt)/dt
- ! equation.
- !
- ! The term is discretized as follows:
- !
- ! The five values of var_zt are found on the thermodynamic levels. All four
- ! derivatives (d/dz) of var_zt are taken over all the intermediate momentum
- ! levels. Then, all three derivatives (d/dz) of d(var_zt)/dz are taken over
- ! all the intermediate thermodynamic levels, which results in the second
- ! derivatives. Then, both derivatives (d/dz) of d^2(var_zt)/dz^2 are taken
- ! over the intermediate momentum levels, which results in the third
- ! derivatives. Finally, the derivative (d/dz) of d^3(var_zt)/dz^3 is taken
- ! over the intermediate (central) thermodynamic level, which results in the
- ! fourth derivative. At the central thermodynamic level, d^4(var_zt)/dz^4
- ! is multiplied by constant coefficient nu.
- !
- ! --var_ztp2----------------------------------------------- t(k+2)
- !
- ! ======d(var_zt)/dz======================================= m(k+1)
- !
- ! --var_ztp1----d^2(var_zt)/dz^2--------------------------- t(k+1)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(k)
- !
- ! --var_zt------d^2(var_zt)/dz^2--------d^4(var_zt)/dz^4--- t(k)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(k-1)
- !
- ! --var_ztm1----d^2(var_zt)/dz^2--------------------------- t(k-1)
- !
- ! ======d(var_zt)/dz======================================= m(k-2)
- !
- ! --var_ztm2----------------------------------------------- t(k-2)
- !
- ! The vertical indices t(k+2), m(k+1), t(k+1), m(k), t(k), m(k-1), t(k-1),
- ! m(k-2), and t(k-2) correspond with altitudes zt(k+2), zm(k+1), zt(k+1),
- ! zm(k), zt(k), zm(k-1), zt(k-1), zm(k-2), and zt(k-2) respectively. The
- ! letter "t" is used for thermodynamic levels and the letter "m" is used for
- ! momentum levels.
- !
- ! dzt(k) = 1 / ( zm(k) - zm(k-1) )
- ! dzm(k) = 1 / ( zt(k+1) - zt(k) )
- ! dzm(k-1) = 1 / ( zt(k) - zt(k-1) )
- ! dzt(k+1) = 1 / ( zm(k+1) - zm(k) )
- ! dzt(k-1) = 1 / ( zm(k-1) - zm(k-2) )
- ! dzm(k+1) = 1 / ( zt(k+2) - zt(k+1) )
- ! dzm(k-2) = 1 / ( zt(k-1) - zt(k-2) )
- !
- ! The discretization of -nu*d^4(var_zt)/dz^4 at thermodynamic level (k)
- ! is written out as follows:
- !
- ! -nu
- ! *dzt(k)*[ dzm(k)*{ dzt(k+1)*( dzm(k+1)*(var_zt(k+2)-var_zt(k+1))
- ! -dzm(k)*(var_zt(k+1)-var_zt(k)) )
- ! -dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) ) }
- ! -dzm(k-1)*{ dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) )
- ! -dzt(k-1)*( dzm(k-1)*(var_zt(k)-var_zt(k-1))
- ! -dzm(k-2)*(var_zt(k-1)-var_zt(k-2)) ) } ].
- !
- ! Again, the term is treated completely implicitly, so the leading "-" sign
- ! changes to a "+" sign when the term is brought over to the left-hand side,
- ! and var_zt is considered to be at timestep (t+1).
- !
- !
- ! Boundary Conditions:
- !
- ! 1) Zero-flux boundary conditions.
- ! This function is set up to use zero-flux boundary conditions at both
- ! the lower boundary level and the upper boundary level. The flux, F,
- ! is the amount of var_zt flowing normal through the boundary per unit
- ! time per unit surface area. The derivative of the flux effects the
- ! time-tendency of var_zt, such that:
- !
- ! d(var_zt)/dt = -dF/dz.
- !
- ! For the 4th-order numerical diffusion term, -nu*d^4(var_zt)/dz^4 (which
- ! is actually -d[nu*d^3(var_zt)/dz^3]/dz with a constant coefficient,
- ! nu), the flux is:
- !
- ! F = +nu*d^3(var_zt)/dz^3.
- !
- ! In order to have zero-flux boundary conditions, the third derivative of
- ! var_zt, d^3(var_zt)/dz^3, needs to equal 0 at both the lower boundary
- ! and the upper boundary.
- !
- ! Fourth-order numerical diffusion is used in conjunction with
- ! second-order eddy diffusion, +d[(K_zm+nu)*d(var_zt)/dz]/dz, where the
- ! coefficient of eddy diffusivity, (K_zm+nu), varies in the vertical.
- ! Both 4th-order numerical diffusion and 2nd-order eddy diffusion use the
- ! same boundary condition type at all times, which in this case is
- ! zero-flux boundary conditions. For 2nd-order eddy diffusion, the flux
- ! is: F = -(K_zm+nu)*d(var_zt)/dz. In order to have zero-flux boundary
- ! conditions, the derivative of var_zt, d(var_zt)/dz, needs to equal 0 at
- ! both the lower boundary and the upper boundary.
- !
- ! Thus, the boundary conditions used for 4th-order numerical diffusion
- ! are: d^3(var_zt)/dz^3 = 0 and d(var_zt)/dz = 0 at both the upper
- ! boundary and the lower boundary, resulting in four boundary conditions,
- ! which is the number of boundary conditions needed for a 4th-order term.
- !
- ! In order to discretize the lower boundary condition, consider a new
- ! level outside the model (thermodynamic level 0) just below the lower
- ! boundary level (thermodynamic level 1). The value of var_zt at the
- ! level just outside the model is defined to be the same as the value of
- ! var_zt at the lower boundary level. Therefore, the value of
- ! d(var_zt)/dz between the level just outside the model and the lower
- ! boundary level is 0, satisfying one of the boundary conditions. The
- ! boundary condition d^3(var_zt)/dz^3 = 0 is also set at this level. The
- ! rest of the levels involved are discretized normally, as listed above.
- !
- ! Since the normal discretization includes two levels on either side of
- ! the central level, the lower boundary begins to effect the
- ! discretization at thermodynamic level 2.
- !
- ! -var_zt(4)----------------------------------------------- t(4)
- !
- ! ======d(var_zt)/dz======================================= m(3)
- !
- ! -var_zt(3)----d^2(var_zt)/dz^2--------------------------- t(3)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(2)
- !
- ! -var_zt(2)----d^2(var_zt)/dz^2--------d^4(var_zt)/dz^4--- t(2)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(1)
- !
- ! -var_zt(1)----d^2(var_zt)/dz^2--------------------------- t(1) Boundary
- !
- ! ======[d(var_zt)/dz = 0]================================= m(0)
- !
- ! -[var_zt(0) = var_zt(1)]-----(level outside model)------- t(0)
- !
- ! The discretization of -nu*d^4(var_zt)/dz^4 at thermodynamic level (k=2)
- ! is written out as follows:
- !
- ! -nu
- ! *dzt(k)*[ dzm(k)*{ dzt(k+1)*( dzm(k+1)*(var_zt(k+2)-var_zt(k+1))
- ! -dzm(k)*(var_zt(k+1)-var_zt(k)) )
- ! -dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) ) }
- ! -dzm(k-1)*{ dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) )
- ! -dzt(k-1)*dzm(k-1)*(var_zt(k)-var_zt(k-1)) } ].
- !
- ! Again, the term is treated completely implicitly, so the leading "-"
- ! sign changes to a "+" sign when the term is brought over to the
- ! left-hand side, and var_zt is considered to be at timestep (t+1).
- !
- ! The result is dependent only on values of var_zt found at thermodynamic
- ! levels 1, 2, 3, and 4. Thus, it only affects 4 diagonals on the
- ! left-hand side matrix.
- !
- ! The lower boundary also effects the discretization at thermodynamic
- ! level 1.
- !
- ! -var_zt(3)----------------------------------------------- t(3)
- !
- ! ======d(var_zt)/dz======================================= m(2)
- !
- ! -var_zt(2)----d^2(var_zt)/dz^2--------------------------- t(2)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(1)
- !
- ! -var_zt(1)----d^2(var_zt)/dz^2--------d^4(var_zt)/dz^4--- t(1) Boundary
- !
- ! ======[d(var_zt)/dz = 0]==[d^3(var_zt)/dz^3 = 0]========= m(0)
- !
- ! -[var_zt(0) = var_zt(1)]-----(level outside model)------- t(0)
- !
- ! The discretization of -nu*d^4(var_zt)/dz^4 at thermodynamic level (k=1)
- ! is written out as follows:
- !
- ! -nu
- ! *dzt(k)*[ dzm(k)*{ dzt(k+1)*( dzm(k+1)*(var_zt(k+2)-var_zt(k+1))
- ! -dzm(k)*(var_zt(k+1)-var_zt(k)) )
- ! -dzt(k)*dzm(k)*(var_zt(k+1)-var_zt(k)) } ].
- !
- ! Again, the term is treated completely implicitly, so the leading "-"
- ! sign changes to a "+" sign when the term is brought over to the
- ! left-hand side, and var_zt is considered to be at timestep (t+1).
- !
- ! The result is dependent only on values of var_zt found at thermodynamic
- ! levels 1, 2, and 3. Thus, it only affects 3 diagonals on the left-hand
- ! side matrix.
- !
- ! The same method can be used to discretize the upper boundary by
- ! considering a new level outside the model just above the upper boundary
- ! level.
- !
- ! 2) Fixed-point boundary conditions.
- ! Many equations in the model use fixed-point boundary conditions rather
- ! than zero-flux boundary conditions. This means that the value of
- ! var_zt stays the same over the course of the timestep at the lower
- ! boundary, as well as at the upper boundary.
- !
- ! For a 4th-order term, four boundary conditions are needed. Two
- ! boundary conditions are applied at each boundary. For the case of
- ! fixed-point boundary conditions, one of those two conditions is setting
- ! var_zt = A, where A is a constant value. One more condition is needed.
- ! Setting the values of d(var_zt)/dz and d^3(var_zt)/dz^3 are inherently
- ! used for zero-flux (or perhaps fixed-flux) boundary conditions.
- ! Fixed-point and zero-flux boundary conditions inherently should not be
- ! invoked at the same time. The only remaining choice for a second
- ! boundary condition for the fixed-point case is setting
- ! d^2(var_zt)/dz^2. As it turns out, setting d^2(var_zt)/dz^2 = 0 is the
- ! appropriate condition to use because it prevents values of var_zt at
- ! levels outside the model from being involved in the discretization of
- ! -nu*d^4(var_zt)/dz^4 at thermodynamic level 2. Setting
- ! d^3(var_zt)/dz^3 = 0 does not accomplish the same thing for the
- ! discretization of -nu*d^4(var_zt)/dz^4 at thermodynamic level 2. Also,
- ! as stated above, fourth-order numerical diffusion is used in
- ! conjunction with second-order eddy diffusion,
- ! +d[(K_zm+nu)*d(var_zt)/dz]/dz, where the coefficient of eddy
- ! diffusivity, (K_zm+nu), varies in the vertical. Both 4th-order
- ! numerical diffusion and 2nd-order eddy diffusion use the same boundary
- ! condition type at all times, which in this case is fixed-point boundary
- ! conditions. For 2nd-order eddy diffusion, fixed-point boundary
- ! conditions set var_zt = A, and do not set d(var_zt)/dz. Thus,
- ! d(var_zt)/dz cannot be set for fixed-point boundary conditions. As
- ! previously stated, the only other boundary condition that can be
- ! invoked for a fixed-point boundary case is d^2(var_zt)/dz^2 = 0.
- !
- ! Since the normal discretization includes two levels on either side of
- ! the central level, the lower boundary begins to effect the
- ! discretization at thermodynamic level 2.
- !
- ! -var_zt(4)----------------------------------------------- t(4)
- !
- ! ======d(var_zt)/dz======================================= m(3)
- !
- ! -var_zt(3)----d^2(var_zt)/dz^2--------------------------- t(3)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(2)
- !
- ! -var_zt(2)----d^2(var_zt)/dz^2--------d^4(var_zt)/dz^4--- t(2)
- !
- ! ======d(var_zt)/dz========d^3(var_zt)/dz^3=============== m(1)
- !
- ! -var_zt(1)----[d^2(var_zt)/dz^2 = 0]--------------------- t(1) Boundary
- !
- ! ======d(var_zt)/dz======================================= m(0)
- !
- ! -var_zt(0)-------------------(level outside model)------- t(0)
- !
- ! The discretization of -nu*d^4(var_zt)/dz^4 at thermodynamic level (k=2)
- ! is written out as follows:
- !
- ! -nu
- ! *dzt(k)*[ dzm(k)*{ dzt(k+1)*( dzm(k+1)*(var_zt(k+2)-var_zt(k+1))
- ! -dzm(k)*(var_zt(k+1)-var_zt(k)) )
- ! -dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) ) }
- ! -dzm(k-1)*{ dzt(k)*( dzm(k)*(var_zt(k+1)-var_zt(k))
- ! -dzm(k-1)*(var_zt(k)-var_zt(k-1)) ) } ].
- !
- ! Again, the term is treated completely implicitly, so the leading "-"
- ! sign changes to a "+" sign when the term is brought over to the
- ! left-hand side, and var_zt is considered to be at timestep (t+1).
- !
- ! The result is dependent only on values of var_zt found at thermodynamic
- ! levels 1, 2, 3, and 4. Thus, it only affects 4 diagonals on the
- ! left-hand side matrix.
- !
- ! The same method can be used to discretize -nu*d^4(var_zt)/dz^4 at the
- ! second-highest thermodynamic level (k=top-1) by setting
- ! d^2(var_zt)/dz^2 = 0 at the highest thermodynamic level.
- !
- ! The discretization at thermodynamic level (k=1) is written to simply
- ! set the value var_zt(1) = A. Likewise, the discretization at
- ! thermodynamic level (k=top) is written to simply set the value
- ! var_zt(top) = B. In order to discretize the boundary conditions at the
- ! lowest and highest vertical levels for equations requiring fixed-point
- ! boundary conditions, either:
- ! a) in the parent subroutine or function (that calls this function),
- ! loop over all vertical levels from the second-lowest to the
- ! second-highest, ignoring the lowest and highest levels. Then set
- ! the values at the lowest and highest levels in the parent
- ! subroutine; or
- ! b) in the parent subroutine or function, loop over all vertical levels
- ! and then overwrite the results at the lowest and highest levels.
- !
- ! Either way, at the lowest and highest levels, an array with a value
- ! of 1 at the main diagonal on the left-hand side and with values of 0 at
- ! all other diagonals on the left-hand side will preserve the right-hand
- ! side value at that level, thus satisfying the fixed-point boundary
- ! conditions.
- !
- !
- ! Conservation Properties:
- !
- ! When zero-flux boundary conditions are used, this technique of
- ! discretizing the 4th-order numerical diffusion term leads to conservative
- ! differencing. When conservative differencing is in place, the column
- ! totals for each column in the left-hand side matrix (for the 4th-order
- ! numerical diffusion term) should be equal to 0. This ensures that the
- ! total amount of the quantity var_zt over the entire vertical domain is
- ! being conserved, meaning that nothing is lost due to diffusional effects.
- !
- ! To see that this conservation law is satisfied, compute the 4th-order
- ! numerical diffusion of var_zt and integrate vertically. In discretized
- ! matrix notation (where "i" stands for the matrix column and "j" stands for
- ! the matrix row):
- !
- ! 0 = Sum_j Sum_i ( 1/dzt )_i ( nu*dzt*dzm*dzt*dzm )_ij (var_zt)_j.
- !
- ! The left-hand side matrix, ( nu*dzt*dzm*dzt*dzm )_ij, is partially written
- ! below. The sum over i in the above equation removes the first dzt(k)
- ! everywhere from the matrix below. The sum over j leaves the column totals
- ! that are desired.
- !
- ! Left-hand side matrix contributions from 4th-order numerical diffusion
- ! (or hyper-diffusion) term; first five vertical levels:
- !
- ! column 1 || column 2 || column 3 || column 4 || column 5
- ! ------------------------------------------------------------------------------------------>
- ! | +nu -nu +nu
- ! | *dzt(k) *dzt(k) *dzt(k)
- ! | *[ dzm(k) *[ dzm(k) *dzm(k)
- !k=1| *{ dzt(k+1) *{ dzt(k+1) *dzt(k+1) 0 0
- ! | *dzm(k) *( dzm(k+1) *dzm(k+1)
- ! | +dzt(k) +dzm(k) )
- ! | *dzm(k) } ] +dzt(k)
- ! | *dzm(k) } ]
- ! |
- ! | -nu +nu -nu +nu
- ! | *dzt(k) *dzt(k) *dzt(k) *dzt(k)
- ! | *[ dzm(k) *[ dzm(k) *[ dzm(k) *dzm(k)
- ! | *dzt(k) *{ dzt(k+1) *{ dzt(k+1) *dzt(k+1)
- ! | *dzm(k-1) *dzm(k) *( dzm(k+1) *dzm(k+1)
- ! | +dzm(k-1) +dzt(k) +dzm(k) )
- ! | *{ dzt(k) *( dzm(k) +dzt(k)
- !k=2| *dzm(k-1) +dzm(k-1) ) *dzm(k) } 0
- ! | +dzt(k-1) } +dzm(k-1)
- ! | *dzm(k-1) +dzm(k-1) *dzt(k)
- ! | } ] *{ dzt(k) *dzm(k) ]
- ! | *( dzm(k)
- ! | +dzm(k-1) )
- ! | +dzt(k-1)
- ! | *dzm(k-1) } ]
- ! |
- ! | +nu -nu +nu -nu +nu
- ! | *dzt(k) *dzt(k) *dzt(k) *dzt(k) *dzt(k)
- ! | *dzm(k-1) *[ dzm(k) *[ dzm(k) *[ dzm(k) *dzm(k)
- ! | *dzt(k-1) *dzt(k) *{ dzt(k+1) *{ dzt(k+1) *dzt(k+1)
- ! | *dzm(k-2) *dzm(k-1) *dzm(k) *( dzm(k+1) *dzm(k+1)
- ! | +dzm(k-1) +dzt(k) +dzm(k) )
- ! | *{ dzt(k) *( dzm(k) +dzt(k)
- !k=3| *dzm(k-1) +dzm(k-1) ) *dzm(k) }
- ! | +dzt(k-1) } +dzm(k-1)
- ! | *( dzm(k-1) +dzm(k-1) *dzt(k)
- ! | +dzm(k-2) ) *{ dzt(k) *dzm(k) ]
- ! | } ] *( dzm(k)
- ! | +dzm(k-1) )
- ! | +dzt(k-1)
- ! | *dzm(k-1) } ]
- ! |
- ! | +nu -nu +nu -nu
- ! | *dzt(k) *dzt(k) *dzt(k) *dzt(k)
- ! | *dzm(k-1) *[ dzm(k) *[ dzm(k) *[ dzm(k)
- ! | *dzt(k-1) *dzt(k) *{ dzt(k+1) *{ dzt(k+1)
- ! | *dzm(k-2) *dzm(k-1) *dzm(k) *( dzm(k+1)
- ! | +dzm(k-1) +dzt(k) +dzm(k) )
- ! | *{ dzt(k) *( dzm(k) +dzt(k)
- !k=4| 0 *dzm(k-1) +dzm(k-1) ) *dzm(k) }
- ! | +dzt(k-1) } +dzm(k-1)
- ! | *( dzm(k-1) +dzm(k-1) *dzt(k)
- ! | +dzm(k-2) ) *{ dzt(k) *dzm(k) ]
- ! | } ] *( dzm(k)
- ! | +dzm(k-1) )
- ! | +dzt(k-1)
- ! | *dzm(k-1) } ]
- ! |
- ! | +nu -nu +nu
- ! | *dzt(k) *dzt(k) *dzt(k)
- ! | *dzm(k-1) *[ dzm(k) *[ dzm(k)
- ! | *dzt(k-1) *dzt(k) *{ dzt(k+1)
- ! | *dzm(k-2) *dzm(k-1) *dzm(k)
- ! | +dzm(k-1) +dzt(k)
- ! | *{ dzt(k) *( dzm(k)
- !k=5| 0 0 *dzm(k-1) +dzm(k-1) )
- ! | +dzt(k-1) }
- ! | *( dzm(k-1) +dzm(k-1)
- ! | +dzm(k-2) ) *{ dzt(k)
- ! | } ] *( dzm(k)
- ! | +dzm(k-1) )
- ! | +dzt(k-1)
- ! | *dzm(k-1) } ]
- ! \ /
- !
- ! Note: The super-super diagonal term from level 4 and both the super
- ! diagonal and super-super diagonal terms from level 5 are not shown
- ! on this diagram.
- !
- ! Note: The matrix shown is a five-diagonal matrix. For a nine-diagonal
- ! matrix, there would be an extra row between each of the rows shown
- ! and an extra column between each of the columns shown. However,
- ! for the purposes of the var_zt 4th-order hyper-diffusion term,
- ! those extra row and column values are all 0, and the conservation
- ! properties of the matrix aren't effected.
- !
- ! For the case of fixed-point boundary conditions, the contributions of the
- ! 4th-order hyper-diffusion term are as follows (only the top 2 levels
- ! differ from the matrix diagram above):
- !
- ! column 1 || column 2 || column 3 || column 4 || column 5
- ! ------------------------------------------------------------------------------------------>
- !k=1| 0 0 0 0 0
- ! |
- ! | -nu +nu -nu +nu
- ! | *dzt(k) *dzt(k) *dzt(k) *dzt(k)
- ! | *[ dzm(k) *[ dzm(k) *[ dzm(k) *dzm(k)
- ! | *dzt(k) *{ dzt(k+1) *{ dzt(k+1) *dzt(k+1)
- ! | *dzm(k-1) *dzm(k) *( dzm(k+1) *dzm(k+1)
- ! | +dzm(k-1) +dzt(k) +dzm(k) )
- !k=2| *dzt(k) *( dzm(k) +dzt(k) 0
- ! | *dzm(k-1) ] +dzm(k-1) ) *dzm(k) }
- ! | } +dzm(k-1)
- ! | +dzm(k-1) *dzt(k)
- ! | *{ dzt(k) *dzm(k) ]
- ! | *( dzm(k)
- ! | +dzm(k-1) )
- ! | } ]
- ! \ /
- !
- ! For the left-hand side matrix as a whole, the matrix entries at level 1
- ! (k=1) read: 1 0 0 0 0. For the case of fixed-point boundary
- ! conditions, conservative differencing is not in play. The total amount of
- ! var_zt over the entire vertical domain is not being conserved, as amounts
- ! of var_zt may be fluxed out through the upper boundary or lower boundary
- ! through the effects of diffusion.
- !
- ! Brian Griffin. October 7, 2008.
-
- ! References:
- ! None
- !-----------------------------------------------------------------------
-
- use crmx_clubb_precision, only: &
- core_rknd ! Variable(s)
-
- use crmx_grid_class, only: &
- gr ! Variable(s) gr%nz
-
- implicit none
-
- ! Constant parameters
- integer, parameter :: &
- kp2_tdiag = 1, & ! Thermodynamic super-super diagonal index.
- kp1_tdiag = 2, & ! Thermodynamic super diagonal index.
- k_tdiag = 3, & ! Thermodynamic main diagonal index.
- km1_tdiag = 4, & ! Thermodynamic sub diagonal index.
- km2_tdiag = 5 ! Thermodynamic sub-sub diagonal index.
-
- ! Input Variables
- character (len=*), intent(in) :: &
- boundary_cond ! Type of boundary conditions being used
- ! ('zero-flux' or 'fixed-point').
-
- real( kind = core_rknd ), intent(in) :: &
- nu, & ! Constant coef. of 4th-order numerical diffusion [m^4/s]
- invrs_dzt, & ! Inverse of grid spacing over thermo. level (k) [1/m]
- invrs_dzm, & ! Inverse of grid spacing over momentum level (k) [1/m]
- invrs_dzmm1, & ! Inverse of grid spacing over momentum level (k-1) [1/m]
- invrs_dztp1, & ! Inverse of grid spacing over thermo. level (k+1) [1/m]
- invrs_dztm1, & ! Inverse of grid spacing over thermo. level (k-1) [1/m]
- invrs_dzmp1, & ! Inverse of grid spacing over momentum level (k+1) [1/m]
- invrs_dzmm2 ! Inverse of grid spacing over momentum level (k-2) [1/m]
-
- integer, intent(in) :: &
- level ! Thermodynamic level where calculation occurs. [-]
-
- ! Return Variable
- real( kind = core_rknd ), dimension(5) :: lhs
-
-
- if ( level == 1 ) then
-
- ! Lowest level
- ! k = 1; lower boundery level at surface.
- ! Only relevant if zero-flux boundary conditions are used.
-
- if ( trim( boundary_cond ) == 'zero-flux' ) then
-
- ! Zero-flux boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzm*(invrs_dztp1*invrs_dzm + invrs_dzt*invrs_dzm)
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *invrs_dzm*( invrs_dztp1*(invrs_dzmp1 + invrs_dzm) &
- +invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzm*invrs_dztp1*invrs_dzmp1
-
- elseif ( trim( boundary_cond ) == 'fixed-point' ) then
-
- ! Fixed-point boundary conditions
- ! The left-hand side matrix contributions from level 1 are
- ! over-written or set in the parent subroutine.
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = 0.0_core_rknd
-
- endif
-
-
- elseif ( level == 2 ) then
-
- ! Second-lowest level
-
- if ( trim( boundary_cond ) == 'zero-flux' ) then
-
- ! Zero-flux boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzmm1 &
- +invrs_dzmm1*( invrs_dzt*invrs_dzmm1 &
- +invrs_dztm1*invrs_dzmm1 ) )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*invrs_dzm &
- +invrs_dzt*(invrs_dzm + invrs_dzmm1) ) &
- +invrs_dzmm1*( invrs_dzt*(invrs_dzm + invrs_dzmm1) &
- +invrs_dztm1*invrs_dzmm1 ) )
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*(invrs_dzmp1 + invrs_dzm) &
- +invrs_dzt*invrs_dzm ) &
- +invrs_dzmm1*invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzm*invrs_dztp1*invrs_dzmp1
-
- elseif ( trim( boundary_cond ) == 'fixed-point' ) then
-
- ! Fixed-point boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = 0.0_core_rknd
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzmm1 &
- +invrs_dzmm1*invrs_dzt*invrs_dzmm1 )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*invrs_dzm &
- +invrs_dzt*(invrs_dzm + invrs_dzmm1) ) &
- +invrs_dzmm1*( invrs_dzt*(invrs_dzm + invrs_dzmm1) ) )
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*(invrs_dzmp1 + invrs_dzm) &
- +invrs_dzt*invrs_dzm ) &
- +invrs_dzmm1*invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzm*invrs_dztp1*invrs_dzmp1
-
- endif
-
-
- elseif ( level > 2 .and. level < gr%nz-1 ) then
-
- ! k > 2 and k < num_levels-1
- ! These interior level are not effected by boundary conditions.
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzmm1*invrs_dztm1*invrs_dzmm2
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzmm1 &
- +invrs_dzmm1*( invrs_dzt*invrs_dzmm1 &
- +invrs_dztm1*(invrs_dzmm1 + invrs_dzmm2) ) )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*invrs_dzm &
- +invrs_dzt*(invrs_dzm + invrs_dzmm1) ) &
- +invrs_dzmm1*( invrs_dzt*(invrs_dzm + invrs_dzmm1) &
- +invrs_dztm1*invrs_dzmm1 ) )
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*(invrs_dzmp1 + invrs_dzm) &
- +invrs_dzt*invrs_dzm ) &
- +invrs_dzmm1*invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzm*invrs_dztp1*invrs_dzmp1
-
-
- elseif ( level == gr%nz-1 ) then
-
- ! Second-highest level
-
- if ( trim( boundary_cond ) == 'zero-flux' ) then
-
- ! Zero-flux boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzmm1*invrs_dztm1*invrs_dzmm2
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzmm1 &
- +invrs_dzmm1*( invrs_dzt*invrs_dzmm1 &
- +invrs_dztm1*(invrs_dzmm1 + invrs_dzmm2) ) )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*invrs_dzm &
- +invrs_dzt*(invrs_dzm + invrs_dzmm1) ) &
- +invrs_dzmm1*( invrs_dzt*(invrs_dzm + invrs_dzmm1) &
- +invrs_dztm1*invrs_dzmm1 ) )
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*( invrs_dztp1*invrs_dzm &
- +invrs_dzt*invrs_dzm ) &
- +invrs_dzmm1*invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = 0.0_core_rknd
-
- elseif ( trim( boundary_cond ) == 'fixed-point' ) then
-
- ! Fixed-point boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzmm1*invrs_dztm1*invrs_dzmm2
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzmm1 &
- +invrs_dzmm1*( invrs_dzt*invrs_dzmm1 &
- +invrs_dztm1*(invrs_dzmm1 + invrs_dzmm2) ) )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *( invrs_dzm*( invrs_dzt*(invrs_dzm + invrs_dzmm1) ) &
- +invrs_dzmm1*( invrs_dzt*(invrs_dzm + invrs_dzmm1) &
- +invrs_dztm1*invrs_dzmm1 ) )
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,) ]
- lhs(kp1_tdiag) &
- = -nu*invrs_dzt &
- *( invrs_dzm*invrs_dzt*invrs_dzm &
- +invrs_dzmm1*invrs_dzt*invrs_dzm )
-
- ! Thermodynamic super-super diagonal: [ x var_zt(k+2,) ]
- lhs(kp2_tdiag) &
- = 0.0_core_rknd
-
- endif
-
-
- elseif ( level == gr%nz ) then
-
- ! Highest level
- ! k = gr%nz; upper boundery level at model top.
- ! Only relevant if zero-flux boundary conditions are used.
-
- if ( trim( boundary_cond ) == 'zero-flux' ) then
-
- ! Zero-flux boundary conditions
-
- ! Thermodynamic sub-sub diagonal: [ x var_zt(k-2,) ]
- lhs(km2_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzmm1*invrs_dztm1*invrs_dzmm2
-
- ! Thermodynamic sub diagonal: [ x var_zt(k-1,) ]
- lhs(km1_tdiag) &
- = -nu*invrs_dzt &
- *invrs_dzmm1*( invrs_dzt*invrs_dzmm1 &
- +invrs_dztm1*(invrs_dzmm1 + invrs_dzmm2) )
-
- ! Thermodynamic main diagonal: [ x var_zt(k,) ]
- lhs(k_tdiag) &
- = +nu*invrs_dzt &
- *invrs_dzmm1*(invrs_dzt*invrs_dzmm1 + invrs_dztm1*invrs_dzmm1)
-
- ! Thermodynamic super diagonal: [ x var_zt(k+1,