diff --git a/.gitmodules b/.gitmodules
index 752c3bf4f7..dc3866ecd8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -36,7 +36,7 @@
[submodule "atmos_phys"]
path = src/atmos_phys
url = https://github.com/ESCOMP/atmospheric_physics
- fxtag = atmos_phys0_07_001
+ fxtag = atmos_phys0_08_000
fxrequired = AlwaysRequired
fxDONOTUSEurl = https://github.com/ESCOMP/atmospheric_physics
@@ -144,7 +144,7 @@ fxDONOTUSEurl = https://github.com/ESCOMP/mizuRoute
[submodule "ccs_config"]
path = ccs_config
url = https://github.com/ESMCI/ccs_config_cesm.git
-fxtag = ccs_config_cesm1.0.8
+fxtag = ccs_config_cesm1.0.21
fxrequired = ToplevelRequired
fxDONOTUSEurl = https://github.com/ESMCI/ccs_config_cesm.git
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index 10dc362beb..0000000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# Contributor Code of Conduct
-_The Contributor Code of Conduct is for participants in our software projects and community._
-
-## Our Pledge
-We, as contributors, creators, stewards, and maintainers (participants), of the Community Atmosphere Model (CAM) pledge to make participation in our software, system or hardware project and community a safe, productive, welcoming and inclusive experience for everyone.
-All participants are required to abide by this Code of Conduct.
-This includes respectful treatment of everyone regardless of age, body size, disability, ethnicity, gender identity or expression, level of experience, nationality, political affiliation, veteran status, pregnancy, genetic information, physical appearance, race, religion, or sexual orientation, as well as any other characteristic protected under applicable US federal or state law.
-
-## Our Standards
-Examples of behaviors that contribute to a positive environment include:
-
-* All participants are treated with respect and consideration, valuing a diversity of views and opinions
-* Be considerate, respectful, and collaborative
-* Communicate openly with respect for others, critiquing ideas rather than individuals and gracefully accepting criticism
-* Acknowledging the contributions of others
-* Avoid personal attacks directed toward other participants
-* Be mindful of your surroundings and of your fellow participants
-* Alert UCAR staff and suppliers/vendors if you notice a dangerous situation or someone in distress
-* Respect the rules and policies of the project and venue
-
-Examples of unacceptable behavior include, but are not limited to:
-
-* Harassment, intimidation, or discrimination in any form
-* Physical, verbal, or written abuse by anyone to anyone, including repeated use of pronouns other than those requested
-* Unwelcome sexual attention or advances
-* Personal attacks directed at other guests, members, participants, etc.
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Alarming, intimidating, threatening, or hostile comments or conduct
-* Inappropriate use of nudity and/or sexual images
-* Threatening or stalking anyone, including a participant
-* Other conduct which could reasonably be considered inappropriate in a professional setting
-
-## Scope
-This Code of Conduct applies to all spaces managed by the Project whether they be physical, online or face-to-face.
-This includes project code, code repository, associated web pages, documentation, mailing lists, project websites and wiki pages, issue tracker, meetings, telecons, events, project social media accounts, and any other forums created by the project team which the community uses for communication.
-In addition, violations of this Code of Conduct outside these spaces may affect a person's ability to participate within them.
-Representation of a project may be further defined and clarified by project maintainers.
-
-## Community Responsibilities
-Everyone in the community is empowered to respond to people who are showing unacceptable behavior.
-They can talk to them privately or publicly.
-Anyone requested to stop unacceptable behavior is expected to comply immediately.
-If the behavior continues concerns may be brought to the project administrators or to any other party listed in the [Reporting](#reporting) section below.
-
-## Project Administrator Responsibilities
-Project administrators are responsible for clarifying the standards of acceptable behavior and are encouraged to model appropriate behavior and provide support when people in the community point out inappropriate behavior.
-Project administrator(s) are normally the ones that would be tasked to carry out the actions in the [Consequences](#consequences) section below.
-
-Project administrators are also expected to keep this Code of Conduct updated with the main one housed at UCAR, as listed below in the [Attribution](#attribution) section.
-
-## Reporting
-Instances of unacceptable behavior can be brought to the attention of the project administrator(s) who may take any action as outlined in the [Consequences](#consequences) section below.
-However, making a report to a project administrator is not considered an 'official report' to UCAR.
-
-Instances of unacceptable behavior may also be reported directly to UCAR pursuant to [UCAR's Harassment Reporting and Complaint Procedure](https://www2.fin.ucar.edu/procedures/hr/harassment-reporting-and-complaint-procedure), or anonymously through [UCAR's EthicsPoint Hotline](https://www2.fin.ucar.edu/ethics/anonymous-reporting).
-
-Complaints received by UCAR will be handled pursuant to the procedures outlined in UCAR's Harassment Reporting and Complaint Procedure.
-Complaints to UCAR will be held as confidential as practicable under the circumstances, and retaliation against a person who initiates a complaint or an inquiry about inappropriate behavior will not be tolerated.
-
-Any Contributor can use these reporting methods even if they are not directly affiliated with UCAR.
-The Frequently Asked Questions (FAQ) page for reporting is [here](https://www2.fin.ucar.edu/procedures/hr/reporting-faqs).
-
-## Consequences
-Upon receipt of a complaint, the project administrator(s) may take any action deemed necessary and appropriate under the circumstances.
-Such action can include things such as: removing, editing, or rejecting comments, commits, code, wiki edits, email, issues, and other contributions that are not aligned to this Code of Conduct, or banning temporarily or permanently any contributor for other behaviors that are deemed inappropriate, threatening, offensive, or harmful.
-Project administrators also have the right to report violations to UCAR HR and/or UCAR's Office of Diversity, Equity and Inclusion (ODEI), as well as a participant's home institution and/or law enforcement.
-In the event an incident is reported to UCAR, UCAR will follow its Harassment Reporting and Complaint Procedure.
-
-## Process for Changes
-All UCAR managed projects are required to adopt this Contributor Code of Conduct.
-Adoption is assumed even if not expressly stated in the repository.
-Projects should fill in sections where prompted with project-specific information, including, project name and adoption date.
-
-Projects that adopt this Code of Conduct need to stay up to date with UCAR's Contributor Code of Conduct, linked with a DOI in the [Attribution](#attribution) section below.
-Projects can make limited substantive changes to the Code of Conduct, however, the changes must be limited in scope and may not contradict the UCAR Contributor Code of Conduct.
-
-## Attribution
-This Code of Conduct was originally adapted from the [Contributor Covenant](http://contributor-covenant.org/version/1/4), version 1.4.
-We then aligned it with the UCAR Participant Code of Conduct, which also borrows from the American Geophysical Union (AGU) Code of Conduct.
-The UCAR Participant Code of Conduct applies to both UCAR employees as well as participants in activities run by UCAR.
-The original version of this for all software projects that have strong management from UCAR or UCAR staff is available on the UCAR website at https://doi.org/10.5065/6w2c-a132.
-The date that it was adopted by this project was 2020-04-08 and replaces the previous version.
-When responding to complaints, UCAR HR and ODEI will do so based on the latest published version.
-Therefore, any project-specific changes should follow the [Process for Changes](#process-for-changes) section above.
diff --git a/bld/build-namelist b/bld/build-namelist
index fd59776073..665b5767da 100755
--- a/bld/build-namelist
+++ b/bld/build-namelist
@@ -760,11 +760,9 @@ if ($rad_pkg =~ /rrtmg/ or $chem =~ /waccm/) {
# use solar data file as the default for rrtmg and waccm_ma
add_default($nl, 'solar_irrad_data_file');
- # This option only used by camrt and rrtmg radiation schemes.
- # The solar spectral scaling is done internal to RRTMGP code.
- if ($rad_pkg ne 'rrtmgp') {
- add_default($nl, 'solar_htng_spctrl_scl', 'val'=>'.true.');
- }
+ # The solar spectral scaling is done based on the distribution from
+ # the solar_irrad_data_file.
+ add_default($nl, 'solar_htng_spctrl_scl', 'val'=>'.true.');
}
elsif (!$simple_phys) {
@@ -3660,7 +3658,6 @@ if (!$simple_phys) {
add_default($nl, 'zmconv_c0_ocn');
add_default($nl, 'zmconv_ke');
add_default($nl, 'zmconv_ke_lnd');
- add_default($nl, 'zmconv_org');
add_default($nl, 'zmconv_num_cin');
add_default($nl, 'zmconv_dmpdz');
add_default($nl, 'zmconv_tiedke_add');
@@ -3689,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');
@@ -3777,6 +3774,14 @@ if (!$simple_phys) {
add_default($nl, 'use_gw_movmtn_pbl', 'val'=>'.true.');
}
+ my $use_gw_movmtn_pbl = $nl->get_value('use_gw_movmtn_pbl');
+ if ($use_gw_movmtn_pbl =~ /$TRUE/io) {
+ if ( ! ($dyn =~ /se/) ) {
+ die "$ProgName - ERROR: use_gw_movmtn_pbl is only available with the SE dycore \n";
+
+ }
+ }
+
add_default($nl, 'use_gw_rdg_gamma' , 'val'=>'.false.');
add_default($nl, 'use_gw_front_igw' , 'val'=>'.false.');
add_default($nl, 'use_gw_convect_sh', 'val'=>'.false.');
@@ -3838,6 +3843,7 @@ my $do_gw_convect_sh = ($nl->get_value('use_gw_convect_sh') =~ /$TRUE/io);
my $do_gw_movmtn_pbl = ($nl->get_value('use_gw_movmtn_pbl') =~ /$TRUE/io);
my $do_gw_rdg_beta = ($nl->get_value('use_gw_rdg_beta') =~ /$TRUE/io);
my $do_gw_rdg_gamma = ($nl->get_value('use_gw_rdg_gamma') =~ /$TRUE/io);
+my $do_gw_rdg_resid = ($nl->get_value('use_gw_rdg_resid') =~ /$TRUE/io);
my $do_divstream = ($nl->get_value('gw_rdg_do_divstream') =~ /$TRUE/io);
@@ -3892,6 +3898,10 @@ if ($do_gw_convect_sh) {
if ($do_gw_movmtn_pbl) {
add_default($nl, 'gw_drag_file_mm');
add_default($nl, 'alpha_gw_movmtn');
+ add_default($nl, 'effgw_movmtn_pbl');
+ add_default($nl, 'movmtn_source');
+ add_default($nl, 'movmtn_psteer');
+ add_default($nl, 'movmtn_plaunch');
}
if ($do_gw_rdg_beta) {
@@ -3911,6 +3921,10 @@ if ($do_gw_rdg_beta) {
add_default($nl, 'gw_prndl');
}
+if ($do_gw_rdg_resid) {
+ add_default($nl, 'effgw_rdg_resid' );
+}
+
if ($do_gw_rdg_gamma) {
add_default($nl, 'n_rdg_gamma', 'val'=>'-1');
add_default($nl, 'effgw_rdg_gamma', 'val'=>'1.0D0');
@@ -4334,11 +4348,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 eeee8504a2..c3af153035 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,13 +83,9 @@ Switch to turn on UNICON package: 0 => off, 1 => on
Switch to turn on/off advecting CLUBB moments: 0 => no, 1 => yes
-
-Switch to turn on/off parameterization for sub-grid scale convective organization for the ZM deep convective scheme based
-on Mapes and Neale (2011): 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:
@@ -305,23 +301,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 11d66c55a7..138436315a 100755
--- a/bld/configure
+++ b/bld/configure
@@ -98,26 +98,18 @@ 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.
The user does not need to set this if one of the waccm chemistry options
is chosen.
-waccmx Build CAM/WACCM with WACCM upper Thermosphere/Ionosphere extended package
- -zmconv_org Include parameterization for sub-grid scale convective organization for the ZM deep convective scheme based
- on Mapes and Neale (2011)
Options relevent to SCAM mode:
@@ -254,7 +246,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'},
@@ -303,10 +294,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'},
@@ -316,7 +303,6 @@ GetOptions(
"version" => \$opts{'version'},
"waccm_phys" => \$opts{'waccm_phys'},
"waccmx" => \$opts{'waccmx'},
- "zmconv_org" => \$opts{'zmconv_org'},
) or usage();
# Give usage message.
@@ -567,10 +553,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') {
@@ -601,7 +587,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".
@@ -758,46 +744,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
@@ -815,12 +761,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'}) {
@@ -910,14 +850,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
@@ -930,16 +862,6 @@ if (defined $opts{'clubb_opts'}) {
my $clubb_do_adv = $cfg_ref->get('clubb_do_adv');
if ($print>=2) { print "clubb_do_adv: $clubb_do_adv$eol"; }
-#-----------------------------------------------------------------------------------------------
-# ZM convective organization
-
-if (defined $opts{'zmconv_org'}) {
- $cfg_ref->set('zmconv_org', $opts{'zmconv_org'});
-}
-
-my $zmconv_org = $cfg_ref->get('zmconv_org');
-if ($print>=2) { print "zmconv_org: $zmconv_org$eol"; }
-
#-----------------------------------------------------------------------------------------------
# Macro-physics package
@@ -959,18 +881,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';
}
@@ -1002,15 +918,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) {
@@ -1062,10 +972,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/) {
@@ -1120,31 +1030,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'}) {
@@ -1350,10 +1235,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 {
@@ -1378,10 +1263,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
@@ -1540,11 +1421,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"; }
}
@@ -1557,11 +1438,6 @@ else {
if ($print>=2) { print "Advected constituents added by $microphys_pkg microphysics: 10$eol"; }
}
- if ($zmconv_org == 1 ) {
- $nadv += 1;
- if ($print>=2) { print "Advected constituents added by $microphys_pkg microphysics: 8$eol"; }
- }
-
if ($clubb_do_adv) {
$nadv += 9;
if ($print>=2) { print "Advected constituents added by $microphys_pkg microphysics: 8$eol"; }
@@ -1849,28 +1725,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 +2143,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
@@ -2326,6 +2157,8 @@ sub write_filepath
print $fh "$camsrcdir/src/atmos_phys/schemes/check_energy\n";
print $fh "$camsrcdir/src/atmos_phys/schemes/utilities\n";
+ print $fh "$camsrcdir/src/atmos_phys/schemes/cloud_fraction\n";
+
# Dynamics package and test utilities
print $fh "$camsrcdir/src/dynamics/$dyn\n";
if($dyn eq 'se') {
diff --git a/bld/namelist_files/namelist_defaults_cam.xml b/bld/namelist_files/namelist_defaults_cam.xml
index f004d2cca2..b84e2b6cf2 100644
--- a/bld/namelist_files/namelist_defaults_cam.xml
+++ b/bld/namelist_files/namelist_defaults_cam.xml
@@ -296,7 +296,7 @@
atm/cam/topo/se/ne3pg3_gmted2010_modis_bedmachine_nc0540_Laplace1000_noleak_20230209.nc
atm/cam/topo/se/ne5pg3_nc3000_Co360_Fi001_MulG_PF_nullRR_Nsw064_20170516.nc
atm/cam/topo/se/ne16pg3_nc3000_Co120_Fi001_PF_nullRR_Nsw084_20171012.nc
-atm/cam/topo/se/ne30pg3_gmted2010_modis_bedmachine_nc3000_Laplace0100_noleak_20240117.nc
+atm/cam/topo/se/ne30pg3_gmted2010_modis_bedmachine_nc3000_Laplace0100_noleak_20240720.nc
atm/cam/topo/se/ne60pg3_nc3000_Co030_Fi001_PF_nullRR_Nsw021_20171012.nc
atm/cam/topo/se/ne120pg3_nc3000_Co015_Fi001_PF_nullRR_Nsw010_20171014.nc
atm/cam/topo/se/ne240pg3_nc3000_Co008_Fi001_PF_nullRR_Nsw005_20171015.nc
@@ -316,8 +316,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
@@ -773,6 +771,8 @@
0.0625D0
+1.0D0
+
1.0D0
0.5D0
0.5D0
@@ -839,6 +839,11 @@
0.002d0
0.1d0
0.01d0
+ 1.0d0
+ 65000.0d0
+ 32500.0d0
+ 1
+
15
@@ -1932,8 +1937,6 @@
NEU
-MOZ
-OFF
@@ -2023,7 +2026,6 @@
.true.
.false.
.false.
- .true.
0.075D0
0.100D0
@@ -2040,14 +2042,12 @@
.true.
.true.
.true.
- .true.
0
1
1
1
- 1
0.01d0
0.001d0
@@ -2055,7 +2055,6 @@
.false.
.true.
- .true.
.false.
@@ -2096,7 +2095,7 @@
0.1
0.5
4.2
- 4.25
+ 4.5
0.0
1.0
0.1
@@ -2153,7 +2152,7 @@
.true.
.false.
.false.
- .true.
+ .false.
.true.
.true.
.true.
@@ -2265,10 +2264,8 @@
RK
MG
MG
-SPCAM_m2005
-SPCAM_sam1mom
-MG
-MG
+MG
+MG
1
0
@@ -2394,8 +2391,6 @@
rk
park
CLUBB_SGS
-SPCAM_sam1mom
-SPCAM_m2005
@@ -2443,59 +2438,57 @@
0.37D0
0.35D0
-0.35D0
0.45D0
-0.45D0
0.45D0
0.35D0
-1.30D0
+2.30D0
0.30D0
-0.30D0
+2.30D0
0.45D0
-0.45D0
+2.30D0
0.45D0
-0.45D0
-0.45D0
+2.30D0
0.55D0
0.22D0
0.70D0
-1.30D0
+2.30D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.8D0
-0.8D0
+2.30D0
0.70D0
-0.70D0
-0.13D0
+2.300D0
0.26D0
-0.26D0
+2.30D0
0.7D0
-0.7D0
+2.30D0
0.24D0
-0.24D0
+2.30D0
0.9D0
-0.9D0
+2.30D0
-Zender_2003
+ Zender_2003
+ Leung_2023
atm
@@ -2504,9 +2497,8 @@
1.62D0
0.90D0
1.00D0
-0.75D0
+1.50D0
1.10D0
-1.2D0
0.60D0
@@ -2521,9 +2513,6 @@
0.4D0
1.0D0
-1.00D0
-1.00D0
-
.false.
.true.
@@ -2586,16 +2575,12 @@
HB
HBR
CLUBB_SGS
-SPCAM_m2005
-SPCAM_sam1mom
ZM
off
UNICON
NONE
-SPCAM
-SPCAM
NONE
UW
@@ -2603,8 +2588,6 @@
Hack
Hack
CLUBB_SGS
-SPCAM
-SPCAM
.true.
@@ -2717,10 +2700,8 @@
1.1D0
1.0D0
1.05D0
- 1.0D0
1.1D0
1.0D0
- 1.0D0
1.e-7
5.e-3
@@ -2830,9 +2811,6 @@
5.0E-6
5.0E-6
- .false.
- .true.
-
5
1
1
@@ -2857,19 +2835,14 @@
4
4
4
- 4
42
-42
42
-42
42
-42
42
42
42
42
42
-42
1
2
@@ -3054,8 +3027,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 241cc4826a..38f643253d 100644
--- a/bld/namelist_files/namelist_definition.xml
+++ b/bld/namelist_files/namelist_definition.xml
@@ -1222,6 +1222,13 @@ Whether or not to enable gravity waves from PBL moving mountains source.
Default: .false.
+
+Whether or not to enable gravity waves from residual (non-ridge)
+orography
+Default: set by build-namelist.
+
+
Gravity wave spectrum dimension (wave numbers are from -pgwv to pgwv).
@@ -1325,6 +1332,36 @@ Max efficiency associated with anisotropic OGW.
Default: 1.0
+
+Efficiency scaling factor associated with residual non-ridge topo
+Default: set by build-namelist.
+
+
+
+Efficiency scaling factor for moving mountain source
+Default: set by build-namelist.
+
+
+
+Global steering level (Pa) for moving mtns. If negative steering level, it will be provided by future code
+Default: set by build-namelist.
+
+
+
+Global launch level (Pa) for moving mtns. If negative launch level, it will be provided by future code
+Default: set by build-namelist.
+
+
+
+Integer code for movmtn source: 1=vorticity, 2=upwp
+Default: set by build-namelist.
+
+
Drag coefficient for obstacles in low-level flow.
@@ -3156,13 +3193,6 @@ Tunable evaporation efficiency in ZM deep convection scheme.
Default: set by build-namelist
-
-Include organization parameterization in ZM. This value is set to true automatically
-if -zmconv_org is set in configure.
-Default: .false., unless -zmconv_org set in configure
-
-
The number of negative buoyancy regions that are allowed before the convection top and CAPE calculations are completed.
@@ -3480,16 +3510,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).
@@ -3509,14 +3537,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 }}).
@@ -5239,7 +5265,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'.
@@ -6058,7 +6084,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
-
-
@@ -246,16 +237,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
-
-
QPC6
2000_CAM60_SLND_SICE_DOCN%AQP3_SROF_SGLC_SWAV
@@ -332,16 +313,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_BDRD
HIST_CAM60_CLM50%BGC-CROP_CICE%PRES_DOCN%DOM_MOSART_SGLC_SWAV_BGC%BDRD
@@ -372,19 +343,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
-
-
-
@@ -759,76 +717,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 81256661b7..7b57d05162 100644
--- a/cime_config/testdefs/testlist_cam.xml
+++ b/cime_config/testdefs/testlist_cam.xml
@@ -350,26 +350,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -2008,70 +1988,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 16489fbe43..bea6f244a5 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,8 +1,8 @@
===============================================================
-Tag name:
+Tag name: cam6_4_068
Originator(s): eaton
-Date:
+Date: 19 February 2025
One-line Summary: remove Eulerian dycore; fix fire emissions
Github PR URL: https://github.com/ESCOMP/CAM/pull/1215
@@ -23,7 +23,7 @@ List any changes to the defaults for the boundary datasets:
Describe any substantial timing or memory changes: none
-Code reviewed by:
+Code reviewed by: cacraig
List all files eliminated:
@@ -176,6 +176,1143 @@ Summarize any changes to answers: BFB. Compset FCfireHIST has answer
===============================================================
===============================================================
+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
+One-line Summary: Limit extraneous log file messages from aerosol wet deposition
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1228
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+
+ Ignore the insignificant negative values produced by aerosol wet deposition module.
+ Report the larger (possibly significant) negative values in the log file and abort
+ when DEBUG is TRUE.
+ Issue #1132 -- Extraneous output to cesm.log
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist: N/A
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: cacraigucar
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M src/chemistry/aerosol/wetdep.F90
+ - limit log messages as described above
+
+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:
+ FAIL ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s COMPARE_base_rest
+ FAIL SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d BASELINE /glade/campaign/cesm/community/amwg/cam_baselines/cam6_4_065_intel: DIFF
+ - pre-existing failures due to HEMCO not having reproducible results (issues #1018 and #856)
+
+ FAIL SMS_D_Ln9.f19_f19_mg17.FXHIST.derecho_intel.cam-outfrq9s_amie SETUP
+ FAIL SMS_D_Ln9_P1280x1.ne0CONUSne30x8_ne0CONUSne30x8_mt12.FCHIST.derecho_intel.cam-outfrq9s SETUP
+ - 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:
+ FAIL ERC_D_Ln9.f10_f10_mg37.QPSPCAMM.izumi_gnu.cam-outfrq3s
+ - pre-existing failure introduced in cam6_4_065
+
+Summarize any changes to answers: Bit-for-bit unchanged
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_065
+Originator(s): cacraig
+Date: Feb 11, 2025
+One-line Summary: ZM CCPP'ization round 4 (completes CCPP conversion of ZM)
+
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1218
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - Convert ZM to CCPP and move into atmospheric_physics github repo: https://github.com/ESCOMP/CAM/issues/873
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist:
+ - Removed zmconv_org namelist as that partially implemented capability has been removed
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: nusbaume, jimmielin
+
+List all files eliminated: N/A
+D src/physics/cam/wv_sat_methods.F90
+D src/physics/cam/wv_saturation.F90
+D src/utils/error_messages.F90
+D src/utils/namelist_utils.F90
+ - Moved to atmospheric_physics (and currently reside in the to_be_ccppized directory)
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M .gitmodules
+ - update atmospheric_physics to bring in ZM changes
+
+M bld/build-namelist
+M bld/config_files/definition.xml
+M bld/configure
+M bld/namelist_files/namelist_defaults_cam.xml
+M bld/namelist_files/namelist_definition.xml
+ - remove zmconv_org namelist
+
+M src/physics/cam/cloud_fraction.F90
+ - moved cldfrc_fice to atmospheric_physics and ccppized
+
+M src/physics/cam/clubb_intr.F90
+ - removed difzm declarations as no longer needed
+
+M src/physics/cam/convect_shallow.F90
+M src/physics/cam/macrop_driver.F90
+M src/physics/cam/physpkg.F90
+M src/physics/cam/rk_stratiform.F90
+M src/physics/cam/zm_conv_intr.F90
+M src/physics/cam7/physpkg.F90
+M src/physics/simple/physpkg.F90
+ - various mods to get this to work with the routines that are ccppized
+
+M src/utils/cam_ccpp/ccpp_constituent_prop_mod.F90
+ - Add routine:
+ ccp_set_standard_name to set constituent's standard name
+ ccp_is_dry to return whether species is dry
+ ccp_set_dry to set constituent's dry property based on what is passed in
+
+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: all BFB, except:
+ ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s (Overall: FAIL)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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: BFB
+
+izumi/nag/aux_cam: BFB
+
+izumi/gnu/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPSPCAMM.izumi_gnu.cam-outfrq3s (Overall: FAIL) details:
+ - New failure, but since SPCAM is being removed(PR 1217) will create this new pre-existing failure
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_064
+Originator(s): sjsprecious, huebleruwm
+Date: Feb 10, 2025
+One-line Summary: Fix broken GPU tests for CLUBB code
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1226
+
+Purpose of changes (include the issue number and title text for each relevant
+GitHub issue):
+
+This PR fixes the broken ERS tests due to the recent GPU changes of CLUBB code
+(PR #1175).
+
+Note that this PR need a new ccs_config tag from ESMCI/ccs_config_cesm#204 to
+complete this PR.
+
+Closes #1220 - GPU test fails restart comparison
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist: N/A
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: nusbaume
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+
+M .gitmodules
+ - Update ccs_config submodule to ccs_config_cesm1.0.21 (needed for test fix)
+
+M src/physics/cam/clubb_intr.F90
+ - Move variables from OpenACC create call to copy call to fix restart test.
+
+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)
+SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF)
+ - Missed baseline update from previous CAM tag (cam6_4_063)
+
+derecho/nvhpc/aux_cam:
+
+ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: DIFF)
+ - Expected change in baseline answers.
+
+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, i.e.,
+- what code configurations:
+- what platforms/compilers:
+- nature of change (roundoff; larger than roundoff but same climate; new
+ climate):
+
+If bitwise differences were observed, how did you show they were no worse
+than roundoff?
+
+If this tag changes climate describe the run(s) done to evaluate the new
+climate in enough detail that it(they) could be reproduced, i.e.,
+- source tag (all code used must be in the repository):
+- platform/compilers:
+- configure commandline:
+- build-namelist command (or complete namelist):
+- MSS location of output:
+
+MSS location of control simulations used to validate new climate:
+
+URL for AMWG diagnostics output used to validate new climate:
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_063
+Originator(s): cacraig, PeterHjortLauritzen
+Date: Feb 9, 2025
+One-line Summary: Update namelist settings for beta06
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1252
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - Namelist changes for CAM7: https://github.com/ESCOMP/CAM/issues/1251
+ - Need new dust-related namelist settings on by default: https://github.com/ESCOMP/CAM/1249
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist:
+ - Change namelist settings for seasalt_emis_scale, clubb_c8, dust_emis_method and dust_emis_fact
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: PeterHjortLauritzen, adamrher, ekluzek
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M bld/namelist_files/namelist_defaults_cam.xml
+ - Change namelist settings for seasalt_emis_scale, clubb_c8, dust_emis_method and dust_emis_fact
+
+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)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPLT.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPMT.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF) details:
+ SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF) details:
+ - All CAM7 runs will have answer changes (NLCOMP differences for these runs as well)
+
+derecho/nvhpc/aux_cam:
+ ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL)
+ - pre-existing failure -- issue #1220
+
+izumi/nag/aux_cam: All BFB
+
+izumi/gnu/aux_cam:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPLT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPMT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ - All CAM7 runs will have answer changes (NLCOMP differences for these runs as well)
+
+===============================================================
+
+===============================================================
+
+Tag name: cam6_4_062
+Originator(s): juliob, cacraig, PeterHjortLauritzen
+Date: Feb 7, 2025
+One-line Summary: Phase 2 of GW development
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1117
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - Continuing development of gravity wave parameterization (//github.com/ESCOMP/CAM/issues/1115)
+ - Added vorticity calculation to SE dycore. Vorticity is passed to the gravity wave (GW) scheme in
+ model physics to provide a possible source for ‘moving mountain’ GW, i.e., low-phase speed GW forced
+ by atmospheric circulations. This provides another forcing option, in addition to boundary layer
+ momentum flux implemented earlier. Vorticity anomalies as sources for GW have been proposed by other
+ researchers in published papers.
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist:
+ - Added the following namelist options
+ - use_gw_rdg_resid
+ - effgw_movmtn_pbl
+ - movmtn_source
+ - movmtn_psteer
+ - movmtn_plaunch
+ - effgw_rdg_resid
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: nusbaume, PeterHjortLauritzen
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M bld/build-namelist
+M bld/namelist_files/namelist_defaults_cam.xml
+M bld/namelist_files/namelist_definition.xml
+ - added new GW namelist settings (see above)
+
+M src/dynamics/se/dp_coupling.F90
+M src/dynamics/se/dyn_comp.F90
+M src/dynamics/se/gravity_waves_sources.F90
+M src/physics/cam/gw_common.F90
+M src/physics/cam/gw_drag.F90
+M src/physics/cam/gw_movmtn.F90
+M src/physics/cam/gw_rdg.F90
+ - See description listed above
+
+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)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPLT.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPMT.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FCnudged.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF) details:
+ SMS_Ld1.ne30pg3_ne30pg3_mg17.FC2010climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s_rrtmgp (Overall: DIFF) details:
+ - Answer changes expected for CAM7 runs
+
+derecho/nvhpc/aux_cam:
+ ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL)
+ - pre-existing failure -- issue #1220
+
+izumi/nag/aux_cam: All BFB
+
+izumi/gnu/aux_cam:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPLT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPMT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ - Answer changes expected for CAM7 runs
+
+Summarize any changes to answers, i.e.,
+- what code configurations: All CAM7
+- what platforms/compilers: All
+- nature of change (roundoff; larger than roundoff but same climate; new
+ climate): new climate when GW namelists are set
+
+If this tag changes climate describe the run(s) done to evaluate the new
+climate in enough detail that it(they) could be reproduced, i.e.,
+ - Simulations were made by Julio Bacmeister and were presented at AMWG
+ - Dave Lawrence presented results to the SCC at their January meeting
+
+===============================================================
+
+===============================================================
+
+Tag name: cam6_4_061
+Originator(s): liyptardis, PeterHjortLauritzen, cacraig
+Date: Feb 6, 2025
+
+One-line Summary: fix heating depth bug for gravity wave parameterization
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1232
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - Gravity wave scheme fails to catch the right maximum latent heating rate and convective top from the ZM scheme.
+ This PR fixes that isuue. (Github issue #1229)
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist: N/A
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: nusbaume, liyptardis
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M src/physics/cam/gw_convect.F90
+ - fix heating depth
+
+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)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+ ERC_D_Ln9.f19_f19_mg17.QPX2000.derecho_intel.cam-outfrq3s (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPLT.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPMT.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ld3.f09_f09_mg17.FWHIST.derecho_intel.cam-reduced_hist1d (Overall: DIFF) details:
+ ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF) details:
+ ERP_Lh12.f19_f19_mg17.FW4madSD.derecho_intel.cam-outfrq3h (Overall: DIFF) details:
+ ERP_Ln9.f19_f19_mg17.FWsc1850.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FCnudged.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f09_f09_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f19_f19_mg17.FXSD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCts2nudged.derecho_intel.cam-outfrq9s_leapday (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCvbsxHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s_waccm_ma_mam4 (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.QPC2000climo.derecho_intel.cam-outfrq3s_usecase (Overall: DIFF) details:
+ SMS_D_Ln9.ne16_ne16_mg17.QPX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne16pg3_ne16pg3_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF) details:
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ld1.f09_f09_mg17.FW2000climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ld1.ne30pg3_ne30pg3_mg17.FC2010climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Lh12.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq3h (Overall: DIFF) details:
+ SMS_Ln9.f09_f09_mg17.FW1850.derecho_intel.cam-reduced_hist3s (Overall: DIFF) details:
+ SMS_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s_rrtmgp (Overall: DIFF) details:
+ - Expect baseline differences
+
+derecho/nvhpc/aux_cam:
+ ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL)
+ - pre-existing failure -- issue #1220
+
+izumi/nag/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-carma_sea_salt (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_subcol (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_am (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_cospsathist (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPWmaC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QSPCAMS.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.mpasa480z32_mpasa480.FHS94.izumi_nag.cam-outfrq3s_usecase (Overall: DIFF) details:
+ ERC_D_Ln9.ne16_ne16_mg17.QPC4.izumi_nag.cam-outfrq3s_usecase (Overall: DIFF) details:
+ ERC_D_Ln9.ne16pg3_ne16pg3_mg17.QPC4.izumi_nag.cam-outfrq3s_usecase (Overall: DIFF) details:
+ ERC_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-outfrq3s_ttrac (Overall: DIFF) details:
+ ERC_D_Ln9.T5_T5_mg37.QPC4.izumi_nag.cam-outfrq3s_usecase (Overall: DIFF) details:
+ ERI_D_Ln18.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_eoyttrac (Overall: DIFF) details:
+ ERI_D_Ln18.f19_f19_mg17.QPC6.izumi_nag.cam-ghgrmp_e8 (Overall: DIFF) details:
+ ERI_D_Ln18.ne5_ne5_mg37.FADIAB.izumi_nag.cam-outfrq3s_bwic (Overall: DIFF) details:
+ ERI_D_Ln18.ne5pg3_ne5pg3_mg37.FADIAB.izumi_nag.cam-outfrq3s_bwic (Overall: DIFF) details:
+ ERP_Ln9.ne5pg3_ne5pg3_mg37.QPC6.izumi_nag.cam-outfrq9s_clubbmf (Overall: DIFF) details:
+ ERS_Ln27.ne5pg3_ne5pg3_mg37.FKESSLER.izumi_nag.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.ne5_ne5_mg37.FADIAB.izumi_nag.cam-outfrq9s (Overall: DIFF) details:
+ PEM_D_Ln9.ne5_ne5_mg37.FADIAB.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 (Overall: DIFF) details:
+ SMS_D_Ld2.f45_f45_mg37.PC5.izumi_nag.cam-outfrq24h_port (Overall: DIFF) details:
+ SMS_D_Ln3.ne5pg3_ne5pg3_mg37.QPX2000.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ SMS_D_Ln6.ne5_ne5_mg37.QPWmaC4.izumi_nag.cam-outfrq3s_physgrid_tem (Overall: DIFF) details:
+ SMS_D_Ln7.T42_T42_mg17.QPSCAMC5.izumi_nag.cam-scmarm (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-rad_diag_mam (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_ba (Overall: DIFF) details:
+ SMS_D_Ln9_P1x1.ne5_ne5_mg37.FADIAB.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ SMS_P48x1_D_Ln3.f09_f09_mg17.QPC6HIST.izumi_nag.cam-outfrq3s_co2cycle_usecase (Overall: DIFF) details:
+ SUB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ TMC_D.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_eoyttrac (Overall: DIFF) details:
+ TMC_D.T5_T5_mg37.QPC5.izumi_nag.cam-ghgrmp_e8 (Overall: DIFF) details:
+ - Expect baseline differences
+
+
+izumi/gnu/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPC4.izumi_gnu.cam-outfrq3s_diags (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPLT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPMT.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9_P24x2.f45_f45_mg37.QPWmaC6.izumi_gnu.cam-outfrq9s_mee_fluxes (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPWmaC4.izumi_gnu.cam-outfrq9s_apmee (Overall: DIFF) details:
+ SMS_P48x1_D_Ln9.f19_f19_mg17.FW4madSD.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ - Expect baseline differences
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_060
+Originator(s): klindsay, PeterHjortLauritzen, cacraig
+Date: Feb 5, 2025
+One-line Summary: Preserve constant dry mixing ratios in gw and vertical diffusion code
+
+Github PR URL: Preserve constant dry mixing ratios in gw_drag and vertical diffusion code (https://github.com/ESCOMP/CAM/pull/1234)
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - Keith Lindsay's modifications for preservation of dry constant mixing ratios. (Github issue #1233)
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist: N/A
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: cacraig
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M src/physics/cam/gw_drag.F90
+M src/physics/cam/vertical_diffusion.F90
+ - changes to preserve dry mixing ratios
+
+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)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+ ERC_D_Ln9.f19_f19_mg17.QPC6.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERC_D_Ln9.f19_f19_mg17.QPMOZ.derecho_intel.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.f19_f19_mg17.QPX2000.derecho_intel.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.ne16_ne16_mg17.QPC5HIST.derecho_intel.cam-outfrq3s_usecase (Overall: DIFF) details:
+ ERC_D_Ln9_P144x1.ne16pg3_ne16pg3_mg17.QPC6HIST.derecho_intel.cam-outfrq3s_ttrac_usecase (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9_P64x2.f09_f09_mg17.QSC6.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ld3.f09_f09_mg17.FWHIST.derecho_intel.cam-reduced_hist1d (Overall: DIFF) details:
+ ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF) details:
+ ERP_Lh12.f19_f19_mg17.FW4madSD.derecho_intel.cam-outfrq3h (Overall: DIFF) details:
+ ERP_Ln9.C96_C96_mg17.F2000climo.derecho_intel.cam-outfrq9s_mg3 (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F1850.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F2010climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.FHIST_BDRD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f19_f19_mg17.FWsc1850.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FCnudged.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f09_f09_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f19_f19_mg17.FXSD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9_P288x1.mpasa120_mpasa120.F2000climo.derecho_intel.cam-outfrq9s_mpasa120 (Overall: DIFF) details:
+ SCT_D_Ln7.ne3_ne3_mg37.QPC5.derecho_intel.cam-scm_prep (Overall: DIFF) details:
+ SCT_D_Ln7.T42_T42_mg17.QPC5.derecho_intel.cam-scm_prep (Overall: DIFF) details:
+ SMS_D_Ld2.f19_f19_mg17.QPC5HIST.derecho_intel.cam-volc_usecase (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCts2nudged.derecho_intel.cam-outfrq9s_leapday (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCvbsxHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FSD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s_waccm_ma_mam4 (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.QPC2000climo.derecho_intel.cam-outfrq3s_usecase (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.QPC5M7.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne16_ne16_mg17.QPX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne16pg3_ne16pg3_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne0ARCTICne30x4_ne0ARCTICne30x4_mt12.FHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF) details:
+ SMS_D_Ln9.T42_T42.FSCAMARM97.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ld1.f09_f09_mg17.FW2000climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ld1.ne30pg3_ne30pg3_mg17.FC2010climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Lh12.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq3h (Overall: DIFF) details:
+ SMS_Ln9.f09_f09_mg17.F2010climo.derecho_intel.cam-nudging (Overall: DIFF) details:
+ SMS_Ln9.f09_f09_mg17.FW1850.derecho_intel.cam-reduced_hist3s (Overall: DIFF) details:
+ SMS_Ln9.f19_f19.F2000climo.derecho_intel.cam-silhs (Overall: DIFF) details:
+ SMS_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s_rrtmgp (Overall: DIFF) details:
+ - expect answer changes for most regression tests
+
+derecho/nvhpc/aux_cam:
+ ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL) details:
+ - expect answer changes for most regression tests
+
+izumi/nag/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-carma_sea_salt (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_subcol (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_am (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_cospsathist (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPWmaC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-outfrq3s_ttrac (Overall: DIFF) details:
+ ERI_D_Ln18.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_eoyttrac (Overall: DIFF) details:
+ ERI_D_Ln18.f19_f19_mg17.QPC6.izumi_nag.cam-ghgrmp_e8 (Overall: DIFF) details:
+ ERP_Ln9.ne5pg3_ne5pg3_mg37.QPC6.izumi_nag.cam-outfrq9s_clubbmf (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 (Overall: DIFF) details:
+ PLB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal0 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal1 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5_ne5_mg37.QPC5.izumi_nag.cam-ttrac_loadbal3 (Overall: DIFF) details:
+ SMS_D_Ln3.ne5pg3_ne5pg3_mg37.QPX2000.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ SMS_D_Ln6.ne5_ne5_mg37.QPWmaC4.izumi_nag.cam-outfrq3s_physgrid_tem (Overall: DIFF) details:
+ SMS_D_Ln7.T42_T42_mg17.QPSCAMC5.izumi_nag.cam-scmarm (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-rad_diag_mam (Overall: DIFF) details:
+ SMS_P48x1_D_Ln3.f09_f09_mg17.QPC6HIST.izumi_nag.cam-outfrq3s_co2cycle_usecase (Overall: DIFF) details:
+ SUB_D_Ln9.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ TMC_D.f10_f10_mg37.QPC5.izumi_nag.cam-outfrq3s_eoyttrac (Overall: DIFF) details:
+ TMC_D.T5_T5_mg37.QPC5.izumi_nag.cam-ghgrmp_e8 (Overall: DIFF) details:
+ - expect answer changes for most regression tests
+
+izumi/gnu/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPC4.izumi_gnu.cam-outfrq3s_diags (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC5.izumi_gnu.cam-outfrq3s_unicon (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPSPCAMM.izumi_gnu.cam-outfrq3s (Overall: DIFF) details:
+ ERP_D_Ln9.C48_C48_mg17.QPC6.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne5_ne5_mg37.QPC5.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9_P24x2.f45_f45_mg37.QPWmaC6.izumi_gnu.cam-outfrq9s_mee_fluxes (Overall: DIFF) details:
+ ERS_Ln9_P24x1.mpasa480_mpasa480.F2000climo.izumi_gnu.cam-outfrq9s_mpasa480 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal0 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal1 (Overall: DIFF) details:
+ PLB_D_Ln9.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-ttrac_loadbal3 (Overall: DIFF) details:
+ SCT_D_Ln7.ne3_ne3_mg37.QPC6.izumi_gnu.cam-scm_prep_c6 (Overall: DIFF) details:
+ SCT_D_Ln7.T42_T42_mg17.QPC6.izumi_gnu.cam-scm_prep_c6 (Overall: DIFF) details:
+ SMS_D_Ln3.f10_f10_mg37.QPMOZ.izumi_gnu.cam-outfrq3s_chemproc (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPWmaC4.izumi_gnu.cam-outfrq9s_apmee (Overall: DIFF) details:
+ SMS_D_Ln9.ne5pg3_ne5pg3_mg37.QPC5.izumi_gnu.cam-outfrq3s_ttrac (Overall: DIFF) details:
+ SMS_P48x1_D_Ln9.f19_f19_mg17.FW4madSD.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ - expect answer changes for most regression tests
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_059
+Originator(s): adamrher, PeterHjortLauritzen, cacraig
+Date: Feb 4, 2025
+One-line Summary: cloud frac bug in nucleate_ice_cam.F90
+Github PR URL: issue 1212 bug fix (cloud frac ice+liquid): https://github.com/ESCOMP/CAM/pull/1230
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+ - ice cloud fraction not set correctly (set to ice+liquid but should only be ice). (Github issue #1212)
+
+Describe any changes made to build system: N/A
+
+Describe any changes made to the namelist: N/A
+
+List any changes to the defaults for the boundary datasets: N/A
+
+Describe any substantial timing or memory changes: N/A
+
+Code reviewed by: cacraig
+
+List all files eliminated: N/A
+
+List all files added and what they do: N/A
+
+List all existing files that have been modified, and describe the changes:
+M src/physics/cam/nucleate_ice_cam.F90
+ - Fix ice cloud fraction
+
+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)
+ SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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
+
+ ERC_D_Ln9.f19_f19_mg17.QPC6.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF) details:
+ ERC_D_Ln9_P144x1.ne16pg3_ne16pg3_mg17.QPC6HIST.derecho_intel.cam-outfrq3s_ttrac_usecase (Overall: DIFF) details:
+ ERP_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9_P64x2.f09_f09_mg17.QSC6.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ld3.f09_f09_mg17.FWHIST.derecho_intel.cam-reduced_hist1d (Overall: DIFF) details:
+ ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF) details:
+ ERP_Ln9.C96_C96_mg17.F2000climo.derecho_intel.cam-outfrq9s_mg3 (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F1850.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.F2010climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s (Overall: FAIL) details:
+ ERP_Ln9.f09_f09_mg17.FHIST_BDRD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.f19_f19_mg17.FWsc1850.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FCnudged.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f09_f09_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9.f19_f19_mg17.FXSD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ ERS_Ln9_P288x1.mpasa120_mpasa120.F2000climo.derecho_intel.cam-outfrq9s_mpasa120 (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCts2nudged.derecho_intel.cam-outfrq9s_leapday (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FCvbsxHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f09_f09_mg17.FSD.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FWma2000climo.derecho_intel.cam-outfrq9s_waccm_ma_mam4 (Overall: DIFF) details:
+ SMS_D_Ln9.f19_f19_mg17.FXHIST.derecho_intel.cam-outfrq9s_amie (Overall: FAIL) details:
+ SMS_D_Ln9.f19_f19_mg17.QPC2000climo.derecho_intel.cam-outfrq3s_usecase (Overall: DIFF) details:
+ SMS_D_Ln9.ne16pg3_ne16pg3_mg17.FX2000.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne0ARCTICne30x4_ne0ARCTICne30x4_mt12.FHIST.derecho_intel.cam-outfrq9s (Overall: DIFF) details:
+ SMS_D_Ln9_P1280x1.ne0CONUSne30x8_ne0CONUSne30x8_mt12.FCHIST.derecho_intel.cam-outfrq9s (Overall: FAIL) details:
+ SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF) details:
+ SMS_Ld1.f09_f09_mg17.FW2000climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Ld1.ne30pg3_ne30pg3_mg17.FC2010climo.derecho_intel.cam-outfrq1d (Overall: DIFF) details:
+ SMS_Lh12.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq3h (Overall: DIFF) details:
+ SMS_Ln9.f09_f09_mg17.F2010climo.derecho_intel.cam-nudging (Overall: DIFF) details:
+ SMS_Ln9.f09_f09_mg17.FW1850.derecho_intel.cam-reduced_hist3s (Overall: DIFF) details:
+ SMS_Ln9.f19_f19.F2000climo.derecho_intel.cam-silhs (Overall: DIFF) details:
+ SMS_Ln9.f19_f19_mg17.FHIST.derecho_intel.cam-outfrq9s_nochem (Overall: DIFF) details:
+ SMS_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s_rrtmgp (Overall: DIFF) details:
+ - Changes expected due to bug fix
+
+derecho/nvhpc/aux_cam:
+ ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL) details:
+ - Changes expected due to bug fix
+
+izumi/nag/aux_cam:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_am (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_cospsathist (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERC_D_Ln9.f10_f10_mg37.QPWmaC6.izumi_nag.cam-outfrq3s (Overall: DIFF) details:
+ ERI_D_Ln18.f19_f19_mg17.QPC6.izumi_nag.cam-ghgrmp_e8 (Overall: DIFF) details:
+ ERP_Ln9.ne5pg3_ne5pg3_mg37.QPC6.izumi_nag.cam-outfrq9s_clubbmf (Overall: DIFF) details:
+ SMS_D_Ln9.f10_f10_mg37.QPC6.izumi_nag.cam-outfrq3s_ba (Overall: DIFF) details:
+ SMS_P48x1_D_Ln3.f09_f09_mg17.QPC6HIST.izumi_nag.cam-outfrq3s_co2cycle_usecase (Overall: DIFF) details:
+ - Changes expected due to bug fix
+
+izumi/gnu/aux_cam:
+ ERP_D_Ln9.C48_C48_mg17.QPC6.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF) details:
+ ERP_Ln9_P24x2.f45_f45_mg37.QPWmaC6.izumi_gnu.cam-outfrq9s_mee_fluxes (Overall: DIFF) details:
+ ERS_Ln9_P24x1.mpasa480_mpasa480.F2000climo.izumi_gnu.cam-outfrq9s_mpasa480 (Overall: DIFF) details:
+ SCT_D_Ln7.ne3_ne3_mg37.QPC6.izumi_gnu.cam-scm_prep_c6 (Overall: DIFF) details:
+ SCT_D_Ln7.T42_T42_mg17.QPC6.izumi_gnu.cam-scm_prep_c6 (Overall: DIFF) details:
+ - Changes expected due to bug fix
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_058
+Originator(s): PeterHjortLauritzen, adamrher, bstephens82, jimmielin, nusbaume
+Date: Jan 31 2025
+One-line Summary: Fix Exner bug in CLUBB interface and change CLUBB namelist
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1231
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+
+The computation of the Exner function in the CLUBB interface code currently passes an incorrect version to the PBL utilities.
+The PBL utilities expect the "Stull" definition of the Exner function rather than the traditional "atmospheric" Exner function.
+(Github issue #1222)
+
+The CLUBB group has recommended a namelist change to address this issue.
+(Github issue #1208)
+
+Snapshots of tphysbc/tphysac subroutine-level variables are always in the "after" state for both tapes
+(Github issue #1241)
+
+Describe any changes made to build system: none
+
+Describe any changes made to the namelist:
+
+M bld/namelist_files/namelist_defaults_cam.xml
+ - Turn off 'clubb_l_min_wp2_from_corr_wx' option in CLUBB
+
+List any changes to the defaults for the boundary datasets: none
+
+Describe any substantial timing or memory changes: not evaluated
+
+Code reviewed by: cacraigucar
+
+List all files eliminated: none
+
+List all files added and what they do: none
+
+List all existing files that have been modified, and describe the changes:
+
+M src/physics/cam/cam_snapshot.F90
+ - Switch to correct tape for tphysac/tphysbc snapshot
+
+M src/physics/cam/clubb_intr.F90
+ - Replace CLUBB exner with "Stull" Exner, which is what is actually expected.
+
+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.
+
+NLCOMP and baseline failures for all applications using CLUBB.
+
+derecho/intel/aux_cam:
+
+ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s (Overall: FAIL)
+SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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:
+
+ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL)
+ - pre-existing failure -- issue #1220
+
+izumi/nag/aux_cam:
+
+All non-CAM6/CAM7 tests pass (CAM6 and CAM7 differences expected)
+
+izumi/gnu/aux_cam:
+
+All non-CAM6/CAM7 tests pass (CAM6 and CAM7 differences expected)
+
+CAM tag used for the baseline comparison tests if different than previous
+tag:
+
+Summarize any changes to answers, i.e.,
+- what code configurations:
+- what platforms/compilers:
+- nature of change (roundoff; larger than roundoff but same climate; new
+ climate):
+
+If bitwise differences were observed, how did you show they were no worse
+than roundoff?
+
+If this tag changes climate describe the run(s) done to evaluate the new
+climate in enough detail that it(they) could be reproduced, i.e.,
+- source tag (all code used must be in the repository):
+- platform/compilers:
+- configure commandline:
+- build-namelist command (or complete namelist):
+- MSS location of output:
+
+MSS location of control simulations used to validate new climate:
+
+URL for AMWG diagnostics output used to validate new climate:
+
+===============================================================
+===============================================================
+
+Tag name: cam6_4_057
+Originator(s): brianpm, eaton, nusbaume
+Date: Jan 29 2025
+One-line Summary: Restore spectral scaling to RRTMGP
+Github PR URL: https://github.com/ESCOMP/CAM/pull/1194
+
+Purpose of changes (include the issue number and title text for each relevant GitHub issue):
+
+resolve issue #1193 - Restore spectral scaling to RRTMGP
+
+Describe any changes made to build system: none
+
+Describe any changes made to the namelist: none
+
+List any changes to the defaults for the boundary datasets: none
+
+Describe any substantial timing or memory changes: not evaluated
+
+Code reviewed by: peverwhee
+
+List all files eliminated: none
+
+List all files added and what they do:
+
+src/physics/rrtmgp/rad_solar_var.F90
+. compute scale factors for solar irradiance based on input dataset
+
+List all existing files that have been modified, and describe the changes:
+
+bld/build-namelist
+. change default setting of solar_htng_spctrl_scl to true for rrtmgp
+
+src/physics/rrtmgp/radconstants.F90
+. add module data band2gpt_sw and set using kdist_sw%get_band_lims_gpoint()
+
+src/physics/rrtmgp/radiation.F90
+. radiation_init
+ - add call to rad_solar_var_init
+. radiation_tend
+ - replace code that scales the solar source based on internal RRTMGP
+ spectral distribution by a scaling based on distribution from the
+ solar_irrad_data_file.
+
+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_D_Ln9.ne30pg3_ne30pg3_mg17.FLTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF)
+ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPLT.derecho_intel.cam-outfrq3s_cosp (Overall: DIFF)
+ERP_D_Ln9.ne30pg3_ne30pg3_mg17.QPMT.derecho_intel.cam-outfrq9s (Overall: DIFF)
+ERP_Ld3.ne30pg3_ne30pg3_mg17.FHISTC_MTt4s.derecho_intel.cam-outfrq1d_aoa (Overall: DIFF)
+SMS_D_Ln9.ne30pg3_ne30pg3_mg17.FMTHIST.derecho_intel.cam-outfrq9s (Overall: DIFF)
+SMS_D_Ln9_P1280x1.ne30pg3_ne30pg3_mg17.FHISTC_MTt1s.derecho_intel.cam-outfrq9s_Leung_dust (Overall: DIFF)
+SMS_Ln9.ne30pg3_ne30pg3_mg17.FW2000climo.derecho_intel.cam-outfrq9s_rrtmgp (Overall: DIFF)
+ - expected NLCOMP (solar_htng_spctrl_scl) and baseline answer changes due to restored RRTMGP spectral scaling.
+
+
+ERP_Ln9.f09_f09_mg17.FCSD_HCO.derecho_intel.cam-outfrq9s (Overall: FAIL)
+SMS_Ld1.f09_f09_mg17.FCHIST_GC.derecho_intel.cam-outfrq1d (Overall: DIFF)
+ - pre-existing failure 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:
+
+ERS_Ln9.ne30pg3_ne30pg3_mg17.F2000dev.derecho_nvhpc.cam-outfrq9s_gpu_default (Overall: FAIL)
+ - pre-existing failure -- issue #1220
+
+izumi/nag/aux_cam: ALL PASS
+
+izumi/gnu/aux_cam:
+
+ERP_D_Ln9.ne3pg3_ne3pg3_mg37.FLTHIST.izumi_gnu.cam-outfrq9s (Overall: DIFF)
+ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPLT.izumi_gnu.cam-outfrq9s (Overall: DIFF)
+ERP_D_Ln9.ne3pg3_ne3pg3_mg37.QPMT.izumi_gnu.cam-outfrq9s (Overall: DIFF)
+SMS_Ld5.f09_f09_mg17.PC6.izumi_gnu.cam-cam6_port_f09_rrtmgp (Overall: DIFF)
+ - expected NLCOMP (solar_htng_spctrl_scl) and baseline answer changes due to restored RRTMGP spectral scaling.
+
+CAM tag used for the baseline comparison tests if different than previous
+tag:
+
+Summarize any changes to answers, i.e.,
+- what code configurations:
+- what platforms/compilers:
+- nature of change (roundoff; larger than roundoff but same climate; new
+ climate):
+
+If bitwise differences were observed, how did you show they were no worse
+than roundoff?
+
+If this tag changes climate describe the run(s) done to evaluate the new
+climate in enough detail that it(they) could be reproduced, i.e.,
+- source tag (all code used must be in the repository):
+- platform/compilers:
+- configure commandline:
+- build-namelist command (or complete namelist):
+- MSS location of output:
+
+MSS location of control simulations used to validate new climate:
+
+URL for AMWG diagnostics output used to validate new climate:
+
+===============================================================
+===============================================================
+
Tag name: cam6_4_056
Originator(s): fvitt
Date: 16 Jan 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/atmos_phys b/src/atmos_phys
index c3de8468f7..89b628646b 160000
--- a/src/atmos_phys
+++ b/src/atmos_phys
@@ -1 +1 @@
-Subproject commit c3de8468f7b245a939448f4ca6d3ef386584e92d
+Subproject commit 89b628646b1506f36b35e67038552f09fb0662e6
diff --git a/src/chemistry/aerosol/wetdep.F90 b/src/chemistry/aerosol/wetdep.F90
index b63ebec338..a1f80d9deb 100644
--- a/src/chemistry/aerosol/wetdep.F90
+++ b/src/chemistry/aerosol/wetdep.F90
@@ -1,9 +1,9 @@
module wetdep
-!-----------------------------------------------------------------------
+!-----------------------------------------------------------------------
!
! Wet deposition routines for both aerosols and gas phase constituents.
-!
+!
!-----------------------------------------------------------------------
use shr_kind_mod, only: r8 => shr_kind_r8
@@ -44,23 +44,23 @@ module wetdep
real(r8) :: totcond(pcols, pver) ! total condensate
real(r8) :: cldv(pcols,pver) ! cloudy volume undergoing wet chem and scavenging
real(r8) :: cldvcu(pcols,pver) ! Convective precipitation area at the top interface of current layer
- real(r8) :: cldvst(pcols,pver) ! Stratiform precipitation area at the top interface of current layer
+ real(r8) :: cldvst(pcols,pver) ! Stratiform precipitation area at the top interface of current layer
end type wetdep_inputs_t
integer :: cld_idx = 0
-integer :: qme_idx = 0
-integer :: prain_idx = 0
-integer :: bergso_idx = 0
-integer :: nevapr_idx = 0
-
-integer :: icwmrdp_idx = 0
-integer :: icwmrsh_idx = 0
-integer :: rprddp_idx = 0
-integer :: rprdsh_idx = 0
-integer :: sh_frac_idx = 0
-integer :: dp_frac_idx = 0
-integer :: nevapr_shcu_idx = 0
-integer :: nevapr_dpcu_idx = 0
+integer :: qme_idx = 0
+integer :: prain_idx = 0
+integer :: bergso_idx = 0
+integer :: nevapr_idx = 0
+
+integer :: icwmrdp_idx = 0
+integer :: icwmrsh_idx = 0
+integer :: rprddp_idx = 0
+integer :: rprdsh_idx = 0
+integer :: sh_frac_idx = 0
+integer :: dp_frac_idx = 0
+integer :: nevapr_shcu_idx = 0
+integer :: nevapr_dpcu_idx = 0
integer :: ixcldice, ixcldliq
!==============================================================================
@@ -75,20 +75,20 @@ subroutine wetdep_init()
integer :: ierr
- cld_idx = pbuf_get_index('CLD')
- qme_idx = pbuf_get_index('QME')
- prain_idx = pbuf_get_index('PRAIN')
- bergso_idx = pbuf_get_index('BERGSO', errcode=ierr )
- nevapr_idx = pbuf_get_index('NEVAPR')
+ cld_idx = pbuf_get_index('CLD')
+ qme_idx = pbuf_get_index('QME')
+ prain_idx = pbuf_get_index('PRAIN')
+ bergso_idx = pbuf_get_index('BERGSO', errcode=ierr )
+ nevapr_idx = pbuf_get_index('NEVAPR')
- icwmrdp_idx = pbuf_get_index('ICWMRDP')
- rprddp_idx = pbuf_get_index('RPRDDP')
- icwmrsh_idx = pbuf_get_index('ICWMRSH')
- rprdsh_idx = pbuf_get_index('RPRDSH')
+ icwmrdp_idx = pbuf_get_index('ICWMRDP')
+ rprddp_idx = pbuf_get_index('RPRDDP')
+ icwmrsh_idx = pbuf_get_index('ICWMRSH')
+ rprdsh_idx = pbuf_get_index('RPRDSH')
sh_frac_idx = pbuf_get_index('SH_FRAC' )
- dp_frac_idx = pbuf_get_index('DP_FRAC')
- nevapr_shcu_idx = pbuf_get_index('NEVAPR_SHCU')
- nevapr_dpcu_idx = pbuf_get_index('NEVAPR_DPCU')
+ dp_frac_idx = pbuf_get_index('DP_FRAC')
+ nevapr_shcu_idx = pbuf_get_index('NEVAPR_SHCU')
+ nevapr_dpcu_idx = pbuf_get_index('NEVAPR_DPCU')
call cnst_get_ind('CLDICE', ixcldice)
call cnst_get_ind('CLDLIQ', ixcldliq)
@@ -177,22 +177,22 @@ subroutine clddiag(t, pmid, pdel, cmfdqr, evapc, &
prain, cldv, cldvcu, cldvst, rain, &
ncol)
- ! ------------------------------------------------------------------------------------
+ ! ------------------------------------------------------------------------------------
! Estimate the cloudy volume which is occupied by rain or cloud water as
! the max between the local cloud amount or the
! sum above of (cloud*positive precip production) sum total precip from above
! ---------------------------------- x ------------------------
! sum above of (positive precip ) sum positive precip from above
! Author: P. Rasch
- ! Sungsu Park. Mar.2010
+ ! Sungsu Park. Mar.2010
! ------------------------------------------------------------------------------------
! Input arguments:
real(r8), intent(in) :: t(pcols,pver) ! temperature (K)
real(r8), intent(in) :: pmid(pcols,pver) ! pressure at layer midpoints
real(r8), intent(in) :: pdel(pcols,pver) ! pressure difference across layers
- real(r8), intent(in) :: cmfdqr(pcols,pver) ! dq/dt due to convective rainout
- real(r8), intent(in) :: evapc(pcols,pver) ! Evaporation rate of convective precipitation ( >= 0 )
+ real(r8), intent(in) :: cmfdqr(pcols,pver) ! dq/dt due to convective rainout
+ real(r8), intent(in) :: evapc(pcols,pver) ! Evaporation rate of convective precipitation ( >= 0 )
real(r8), intent(in) :: cldt(pcols,pver) ! total cloud fraction
real(r8), intent(in) :: cldcu(pcols,pver) ! Cumulus cloud fraction
real(r8), intent(in) :: cldst(pcols,pver) ! Stratus cloud fraction
@@ -202,7 +202,7 @@ subroutine clddiag(t, pmid, pdel, cmfdqr, evapc, &
integer, intent(in) :: ncol
! Output arguments:
- real(r8), intent(out) :: cldv(pcols,pver) ! fraction occupied by rain or cloud water
+ real(r8), intent(out) :: cldv(pcols,pver) ! fraction occupied by rain or cloud water
real(r8), intent(out) :: cldvcu(pcols,pver) ! Convective precipitation volume
real(r8), intent(out) :: cldvst(pcols,pver) ! Stratiform precipitation volume
real(r8), intent(out) :: rain(pcols,pver) ! mixing ratio of rain (kg/kg)
@@ -299,10 +299,10 @@ subroutine wetdepa_v2( &
convproc_do_aer, rcscavt, rsscavt, &
sol_facti_in, sol_factic_in, convproc_do_evaprain_atonce_in, bergso_in )
- !-----------------------------------------------------------------------
+ !-----------------------------------------------------------------------
!
! scavenging code for very soluble aerosols
- !
+ !
!-----------------------------------------------------------------------
real(r8), intent(in) ::&
@@ -314,7 +314,7 @@ subroutine wetdepa_v2( &
cmfdqr(pcols,pver), &! rate of production of convective precip
evapc(pcols,pver), &! Evaporation rate of convective precipitation
conicw(pcols,pver), &! convective cloud water
- cwat(pcols,pver), &! cloud water amount
+ cwat(pcols,pver), &! cloud water amount
precs(pcols,pver), &! rate of production of stratiform precip
conds(pcols,pver), &! rate of production of condensate
evaps(pcols,pver), &! rate of evaporation of precip
@@ -334,7 +334,7 @@ subroutine wetdepa_v2( &
integer, intent(in) :: ncol
real(r8), intent(in) :: scavcoef(pcols,pver) ! Dana and Hales coefficient (/mm) (0.1 if not MODAL_AERO)
real(r8), intent(out) ::&
- scavt(pcols,pver), &! scavenging tend
+ scavt(pcols,pver), &! scavenging tend
iscavt(pcols,pver), &! incloud scavenging tends
fracis(pcols,pver) ! fraction of species not scavenged
@@ -344,13 +344,13 @@ subroutine wetdepa_v2( &
! Setting is_strat_cloudborne=.false. is being used to indicate that the tracers are the
! interstitial modal aerosols. In this case the optional qqcw (the cloud borne mixing ratio
! corresponding to the interstitial aerosol) must be provided, as well as the optional f_act_conv.
- logical, intent(in), optional :: is_strat_cloudborne
+ logical, intent(in), optional :: is_strat_cloudborne
real(r8), intent(in), optional :: qqcw(pcols,pver)
real(r8), intent(in), optional :: f_act_conv(pcols,pver)
real(r8), intent(in), optional :: sol_facti_in(pcols,pver) ! solubility factor (frac of aerosol scavenged in cloud)
real(r8), intent(in), optional :: sol_factic_in(pcols,pver) ! sol_facti_in for convective clouds
-
+
real(r8), intent(out), optional :: icscavt(pcols,pver) ! incloud, convective
real(r8), intent(out), optional :: isscavt(pcols,pver) ! incloud, stratiform
@@ -447,7 +447,7 @@ subroutine wetdepa_v2( &
! the assumption is that within the cloud that
! all the tracer is in the cloud water
!
- ! for both convective and stratiform clouds,
+ ! for both convective and stratiform clouds,
! the fraction of cloud water converted to precip defines
! the amount of tracer which is pulled out.
@@ -465,11 +465,11 @@ subroutine wetdepa_v2( &
rdeltat = 1.0_r8/deltat
! ****************** Evaporation **************************
- ! calculate the fraction of strat precip from above
+ ! calculate the fraction of strat precip from above
! which evaporates within this layer
fracev(i) = evaps(i,k)*pdog(i) &
/max(1.e-12_r8,precabs(i))
-
+
! If resuspending aerosol only when all the rain has totally
! evaporated then zero out any aerosol tendency for partial
! evaporation.
@@ -486,7 +486,7 @@ subroutine wetdepa_v2( &
! ****************** Convection ***************************
!
- ! set odds proportional to fraction of the grid box that is swept by the
+ ! set odds proportional to fraction of the grid box that is swept by the
! precipitation =precabc/rhoh20*(area of sphere projected on plane
! /volume of sphere)*deltat
! assume the radius of a raindrop is 1 e-3 m from Rogers and Yau,
@@ -579,7 +579,7 @@ subroutine wetdepa_v2( &
fracp(i) = precs(i,k)*deltat / &
max( 1.e-12_r8, cwat(i,k) + precs(i,k)*deltat )
fracp(i) = max( 0._r8, min( 1._r8, fracp(i) ) )
-
+
! assume the corresponding amnt of tracer is removed
st_scav_ic(i) = sol_facti(i,k)*clds(i)*fracp(i)*tracer(i,k)*rdeltat
@@ -606,7 +606,7 @@ subroutine wetdepa_v2( &
endif
srct(i) = (srcc(i)+srcs(i))*omsm
-
+
! fraction that is not removed within the cloud
! (assumed to be interstitial, and subject to convective transport)
fracp(i) = deltat*srct(i)/max(cldvst(i,k)*tracer(i,k),1.e-36_r8) ! amount removed
@@ -628,7 +628,7 @@ subroutine wetdepa_v2( &
if (present(bsscavt)) bsscavt(i,k) = -(srcs(i) * (1-fins(i))) * omsm + &
fracev(i)*scavab(i)*rpdog(i)
else
- bcscavt(i,k) = -(srcc(i) * (1-finc(i))) * omsm
+ bcscavt(i,k) = -(srcc(i) * (1-finc(i))) * omsm
rcscavt(i,k) = fracev_cu(i)*scavabc(i)*rpdog(i)
bsscavt(i,k) = -(srcs(i) * (1-fins(i))) * omsm
@@ -645,22 +645,22 @@ subroutine wetdepa_v2( &
end do ! End of i = 1, ncol
+#ifdef DEBUG
+ ! only check in debug mode which aborts when larger negative values are found
found = .false.
do i = 1,ncol
- if ( dblchek(i) < 0._r8 ) then
+ ! catch the larger negative values, ignore insignificant small negaive values
+ if (dblchek(i) < -1.e-10_r8) then
found = .true.
- exit
- end if
+ write(iulog,*) ' wetdapa_v2: negative value ', i, k, tracer(i,k), &
+ dblchek(i), scavt(i,k), srct(i), rat(i), fracev(i)
+ endif
end do
- if ( found ) then
- do i = 1,ncol
- if (dblchek(i) < 0._r8) then
- write(iulog,*) ' wetdapa: negative value ', i, k, tracer(i,k), &
- dblchek(i), scavt(i,k), srct(i), rat(i), fracev(i)
- endif
- end do
- endif
+ if (found) then
+ call endrun('wetdapa_v2: negative values found')
+ end if
+#endif
end do ! End of k = 1, pver
@@ -680,10 +680,10 @@ subroutine wetdepa_v1( t, p, q, pdel, &
sol_facti_in, sol_factbi_in, sol_factii_in, &
sol_factic_in, sol_factiic_in )
- !-----------------------------------------------------------------------
- ! Purpose:
+ !-----------------------------------------------------------------------
+ ! Purpose:
! scavenging code for very soluble aerosols
- !
+ !
! Author: P. Rasch
! Modified by T. Bond 3/2003 to track different removals
!-----------------------------------------------------------------------
@@ -699,7 +699,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
cldc(pcols,pver), &! convective cloud fraction
cmfdqr(pcols,pver), &! rate of production of convective precip
conicw(pcols,pver), &! convective cloud water
- cwat(pcols,pver), &! cloud water amount
+ cwat(pcols,pver), &! cloud water amount
precs(pcols,pver), &! rate of production of stratiform precip
conds(pcols,pver), &! rate of production of condensate
evaps(pcols,pver), &! rate of evaporation of precip
@@ -718,11 +718,11 @@ subroutine wetdepa_v1( t, p, q, pdel, &
real(r8), intent(in), optional :: sol_factic_in(pcols,pver) ! sol_facti_in for convective clouds
real(r8), intent(in), optional :: sol_factiic_in ! sol_factii_in for convective clouds
real(r8), intent(in) :: scavcoef(pcols,pver) ! Dana and Hales coefficient (/mm) (0.1 if not MODAL_AERO)
-
+
integer, intent(in) :: ncol
real(r8), intent(out) ::&
- scavt(pcols,pver), &! scavenging tend
+ scavt(pcols,pver), &! scavenging tend
iscavt(pcols,pver), &! incloud scavenging tends
fracis(pcols,pver) ! fraction of species not scavenged
@@ -738,8 +738,8 @@ subroutine wetdepa_v1( t, p, q, pdel, &
real(r8) adjfac ! factor stolen from cmfmca
real(r8) aqfrac ! fraction of tracer in aqueous phase
- real(r8) cwatc ! local convective total water amount
- real(r8) cwats ! local stratiform total water amount
+ real(r8) cwatc ! local convective total water amount
+ real(r8) cwats ! local stratiform total water amount
real(r8) cwatp ! local water amount falling from above precip
real(r8) fracev(pcols) ! fraction of precip from above that is evaporating
real(r8) fracp ! fraction of cloud water converted to precip
@@ -779,7 +779,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
real(r8) sol_factii, sol_factbi ! in cloud and below cloud fraction of aerosol scavenged by ice
real(r8) sol_factic(pcols,pver) ! sol_facti for convective clouds
real(r8) sol_factiic ! sol_factii for convective clouds
- ! sol_factic & solfact_iic added for MODAL_AERO.
+ ! sol_factic & solfact_iic added for MODAL_AERO.
! For stratiform cloud, cloudborne aerosol is treated explicitly,
! and sol_facti is 1.0 for cloudborne, 0.0 for interstitial.
! For convective cloud, cloudborne aerosol is not treated explicitly,
@@ -809,7 +809,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
! the assumption is that within the cloud that
! all the tracer is in the cloud water
!
- ! for both convective and stratiform clouds,
+ ! for both convective and stratiform clouds,
! the fraction of cloud water converted to precip defines
! the amount of tracer which is pulled out.
!
@@ -833,7 +833,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
pdog = pdel(i,k)/gravit
! ****************** Evaporation **************************
- ! calculate the fraction of strat precip from above
+ ! calculate the fraction of strat precip from above
! which evaporates within this layer
fracev(i) = evaps(i,k)*pdel(i,k)/gravit &
/max(1.e-12_r8,precabs(i))
@@ -844,7 +844,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
! ****************** Convection ***************************
! now do the convective scavenging
- ! set odds proportional to fraction of the grid box that is swept by the
+ ! set odds proportional to fraction of the grid box that is swept by the
! precipitation =precabc/rhoh20*(area of sphere projected on plane
! /volume of sphere)*deltat
! assume the radius of a raindrop is 1 e-3 m from Rogers and Yau,
@@ -860,7 +860,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
! srcs1 = cldt(i,k)*odds*tracer(i,k)*(1.-weight) &
! srcs1 = cldv(i,k)*odds*tracer(i,k)*(1.-weight) &
!srcs1 = cldc(i,k)*odds*tracer(i,k)*(1.-weight) &
- ! /deltat
+ ! /deltat
! fraction of convective cloud water converted to rain
fracp = cmfdqr(i,k)*deltat/max(1.e-8_r8,conicw(i,k))
@@ -905,7 +905,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
! fracp = 0. ! for debug
! assume the corresponding amnt of tracer is removed
- !++mcb -- remove cldc; change cldt to cldv
+ !++mcb -- remove cldc; change cldt to cldv
! srcs1 = (cldt(i,k)-cldc(i,k))*fracp*tracer(i,k)/deltat
! srcs1 = cldv(i,k)*fracp*tracer(i,k)/deltat &
! srcs1 = cldt(i,k)*fracp*tracer(i,k)/deltat ! all condensate
@@ -938,7 +938,7 @@ subroutine wetdepa_v1( t, p, q, pdel, &
endif
srct(i) = (srcc+srcs)*omsm
-
+
! fraction that is not removed within the cloud
! (assumed to be interstitial, and subject to convective transport)
fracp = deltat*srct(i)/max(cldmabs(i)*tracer(i,k),1.e-36_r8) ! amount removed
@@ -966,22 +966,22 @@ subroutine wetdepa_v1( t, p, q, pdel, &
end do
+#ifdef DEBUG
+ ! only check in debug mode which aborts when larger negative values are found
found = .false.
do i = 1,ncol
- if ( dblchek(i) < 0._r8 ) then
+ ! catch the larger negative values, ignore insignificant small negaive values
+ if (dblchek(i) < -1.e-10_r8) then
found = .true.
- exit
- end if
+ write(iulog,*) ' wetdapa_v1: negative value ', i, k, tracer(i,k), &
+ dblchek(i), scavt(i,k), srct(i), rat(i), fracev(i)
+ endif
end do
- if ( found ) then
- do i = 1,ncol
- if (dblchek(i) < 0._r8) then
- write(iulog,*) ' wetdapa: negative value ', i, k, tracer(i,k), &
- dblchek(i), scavt(i,k), srct(i), rat(i), fracev(i)
- endif
- end do
- endif
+ if (found) then
+ call endrun('wetdapa_v1: negative values found')
+ end if
+#endif
end do
@@ -998,10 +998,10 @@ subroutine wetdepg( t, p, q, pdel, &
solconst, scavt, iscavt, cldv, icwmr1, &
icwmr2, fracis, ncol )
- !-----------------------------------------------------------------------
- ! Purpose:
+ !-----------------------------------------------------------------------
+ ! Purpose:
! scavenging of gas phase constituents by henry's law
- !
+ !
! Author: P. Rasch
!-----------------------------------------------------------------------
@@ -1014,12 +1014,12 @@ subroutine wetdepg( t, p, q, pdel, &
cldc(pcols,pver), &! convective cloud fraction
cmfdqr(pcols,pver), &! rate of production of convective precip
rain (pcols,pver), &! total rainwater mixing ratio
- cwat(pcols,pver), &! cloud water amount
+ cwat(pcols,pver), &! cloud water amount
precs(pcols,pver), &! rate of production of stratiform precip
evaps(pcols,pver), &! rate of evaporation of precip
! Sungsu
evapc(pcols,pver), &! Rate of evaporation of convective precipitation
-! Sungsu
+! Sungsu
cldv(pcols,pver), &! estimate of local volume occupied by clouds
icwmr1 (pcols,pver), &! in cloud water mixing ration for zhang scheme
icwmr2 (pcols,pver), &! in cloud water mixing ration for hack scheme
@@ -1033,7 +1033,7 @@ subroutine wetdepg( t, p, q, pdel, &
solconst(pcols,pver) ! Henry's law coefficient
real(r8), intent(out) ::&
- scavt(pcols,pver), &! scavenging tend
+ scavt(pcols,pver), &! scavenging tend
iscavt(pcols,pver), &! incloud scavenging tends
fracis(pcols, pver) ! fraction of constituent that is insoluble
@@ -1044,12 +1044,12 @@ subroutine wetdepg( t, p, q, pdel, &
real(r8) adjfac ! factor stolen from cmfmca
real(r8) aqfrac ! fraction of tracer in aqueous phase
- real(r8) cwatc ! local convective total water amount
- real(r8) cwats ! local stratiform total water amount
- real(r8) cwatl ! local cloud liq water amount
+ real(r8) cwatc ! local convective total water amount
+ real(r8) cwats ! local stratiform total water amount
+ real(r8) cwatl ! local cloud liq water amount
real(r8) cwatp ! local water amount falling from above precip
real(r8) cwatpl ! local water amount falling from above precip (liq)
- real(r8) cwatt ! local sum of strat + conv total water amount
+ real(r8) cwatt ! local sum of strat + conv total water amount
real(r8) cwatti ! cwatt/cldv = cloudy grid volume mixing ratio
real(r8) fracev ! fraction of precip from above that is evaporating
real(r8) fracp ! fraction of cloud water converted to precip
@@ -1105,17 +1105,17 @@ subroutine wetdepg( t, p, q, pdel, &
! partitioning coefs for gas and aqueous phase
! take as a cloud water amount, the sum of the stratiform amount
- ! plus the convective rain water amount
+ ! plus the convective rain water amount
! convective amnt is just the local precip rate from the hack scheme
! since there is no storage of water, this ignores that falling from above
! cwatc = cmfdqr(i,k)*deltat/adjfac
!++mcb -- test cwatc
cwatc = (icwmr1(i,k) + icwmr2(i,k)) * (1._r8-weight)
- !--mcb
+ !--mcb
! strat cloud water amount and also ignore the part falling from above
- cwats = cwat(i,k)
+ cwats = cwat(i,k)
! cloud water as liq
!++mcb -- add cwatc later (in cwatti)
@@ -1127,7 +1127,7 @@ subroutine wetdepg( t, p, q, pdel, &
! total suspended condensate as liquid
cwatt = cwatl + rain(i,k)
- ! incloud version
+ ! incloud version
!++mcb -- add cwatc here
cwatti = cwatt/max(cldv(i,k), 0.00001_r8) + cwatc
@@ -1143,14 +1143,14 @@ subroutine wetdepg( t, p, q, pdel, &
part = patm*gafrac*tracer(i,k)*molwta/molwt
! use henrys law to give moles tracer /liter of water
- ! in this volume
+ ! in this volume
! then convert to kg tracer /liter of water (kg tracer / kg water)
mplb = solconst(i,k)*part*molwt/1000._r8
pdog = pdel(i,k)/gravit
- ! this part of precip will be carried downward but at a new molarity of mpl
+ ! this part of precip will be carried downward but at a new molarity of mpl
precic = pdog*(precs(i,k) + cmfdqr(i,k))
! we cant take out more than entered, plus that available in the cloud
@@ -1203,7 +1203,7 @@ subroutine wetdepg( t, p, q, pdel, &
!--mcb
! now update the amount leaving the layer
- scavbl = scavab(i) - scavt(i,k)*pdog
+ scavbl = scavab(i) - scavt(i,k)*pdog
! in cloud amount is that formed locally over the total flux out bottom
fins = scavin/(scavin + scavbc + 1.e-36_r8)
@@ -1212,11 +1212,11 @@ subroutine wetdepg( t, p, q, pdel, &
scavab(i) = scavbl
precab(i) = max(precxx + precic,1.e-36_r8)
-
-
+
+
end do
end do
-
+
end subroutine wetdepg
!##############################################################################
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/dynamics/se/dp_coupling.F90 b/src/dynamics/se/dp_coupling.F90
index 919b7f3510..41e24f18f0 100644
--- a/src/dynamics/se/dp_coupling.F90
+++ b/src/dynamics/se/dp_coupling.F90
@@ -49,9 +49,9 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
! Note that all pressures and tracer mixing ratios coming from the dycore are based on
! dry air mass.
- use gravity_waves_sources, only: gws_src_fnct
- use dyn_comp, only: frontgf_idx, frontga_idx
- use phys_control, only: use_gw_front, use_gw_front_igw
+ use gravity_waves_sources, only: gws_src_fnct,gws_src_vort
+ use dyn_comp, only: frontgf_idx, frontga_idx, vort4gw_idx
+ use phys_control, only: use_gw_front, use_gw_front_igw, use_gw_movmtn_pbl
use hycoef, only: hyai, ps0
use fvm_mapping, only: dyn2phys_vector, dyn2phys_all_vars
use se_dyn_time_mod, only: timelevel_qdp
@@ -84,9 +84,16 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
real (kind=r8), allocatable :: frontga(:,:,:) ! function (frontgf) and angle (frontga)
real (kind=r8), allocatable :: frontgf_phys(:,:,:)
real (kind=r8), allocatable :: frontga_phys(:,:,:)
+
+ ! Vorticity
+ real (kind=r8), allocatable :: vort4gw(:,:,:) ! temp arrays to hold vorticity
+ real (kind=r8), allocatable :: vort4gw_phys(:,:,:)
+
+
! Pointers to pbuf
real (kind=r8), pointer :: pbuf_frontgf(:,:)
real (kind=r8), pointer :: pbuf_frontga(:,:)
+ real (kind=r8), pointer :: pbuf_vort4gw(:,:)
integer :: ncols, ierr
integer :: col_ind, blk_ind(1), m
@@ -110,6 +117,9 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
nullify(pbuf_chnk)
nullify(pbuf_frontgf)
nullify(pbuf_frontga)
+ nullify(pbuf_vort4gw)
+
+
if (fv_nphys > 0) then
nphys = fv_nphys
@@ -136,11 +146,18 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
allocate(frontga(nphys_pts,pver,nelemd), stat=ierr)
if (ierr /= 0) call endrun("dp_coupling: Allocate of frontga failed.")
end if
+ if (use_gw_movmtn_pbl) then
+ allocate(vort4gw(nphys_pts,pver,nelemd), stat=ierr)
+ if (ierr /= 0) call endrun("dp_coupling: Allocate of vort4gw failed.")
+ end if
if (iam < par%nprocs) then
- if (use_gw_front .or. use_gw_front_igw) then
+ if (use_gw_front .or. use_gw_front_igw ) then
call gws_src_fnct(elem, tl_f, tl_qdp_np0, frontgf, frontga, nphys)
end if
+ if (use_gw_movmtn_pbl ) then
+ call gws_src_vort(elem, tl_f, tl_qdp_np0, vort4gw, nphys)
+ end if
if (fv_nphys > 0) then
call test_mapping_overwrite_dyn_state(elem,dyn_out%fvm)
@@ -205,6 +222,9 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
frontgf(:,:,:) = 0._r8
frontga(:,:,:) = 0._r8
end if
+ if (use_gw_movmtn_pbl) then
+ vort4gw(:,:,:) = 0._r8
+ end if
endif ! iam < par%nprocs
@@ -223,6 +243,9 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
allocate(frontgf_phys(pcols, pver, begchunk:endchunk))
allocate(frontga_phys(pcols, pver, begchunk:endchunk))
end if
+ if (use_gw_movmtn_pbl) then
+ allocate(vort4gw_phys(pcols, pver, begchunk:endchunk))
+ end if
!$omp parallel do num_threads(max_num_threads) private (col_ind, lchnk, icol, ie, blk_ind, ilyr, m)
do col_ind = 1, phys_columns_on_task
call get_dyn_col_p(col_ind, ie, blk_ind)
@@ -240,6 +263,9 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
frontgf_phys(icol, ilyr, lchnk) = frontgf(blk_ind(1), ilyr, ie)
frontga_phys(icol, ilyr, lchnk) = frontga(blk_ind(1), ilyr, ie)
end if
+ if (use_gw_movmtn_pbl) then
+ vort4gw_phys(icol, ilyr, lchnk) = vort4gw(blk_ind(1), ilyr, ie)
+ end if
end do
do m = 1, pcnst
@@ -265,6 +291,20 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
deallocate(frontgf_phys)
deallocate(frontga_phys)
end if
+ if (use_gw_movmtn_pbl) then
+ !$omp parallel do num_threads(max_num_threads) private (lchnk, ncols, icol, ilyr, pbuf_chnk, pbuf_vort4gw)
+ do lchnk = begchunk, endchunk
+ ncols = get_ncols_p(lchnk)
+ pbuf_chnk => pbuf_get_chunk(pbuf2d, lchnk)
+ call pbuf_get_field(pbuf_chnk, vort4gw_idx, pbuf_vort4gw)
+ do icol = 1, ncols
+ do ilyr = 1, pver
+ pbuf_vort4gw(icol, ilyr) = vort4gw_phys(icol, ilyr, lchnk)
+ end do
+ end do
+ end do
+ deallocate(vort4gw_phys)
+ end if
call t_stopf('dpcopy')
diff --git a/src/dynamics/se/dyn_comp.F90 b/src/dynamics/se/dyn_comp.F90
index 586ee06b1f..37aab5931a 100644
--- a/src/dynamics/se/dyn_comp.F90
+++ b/src/dynamics/se/dyn_comp.F90
@@ -10,7 +10,7 @@ module dyn_comp
cnst_is_a_water_species
use cam_control_mod, only: initial_run
use cam_initfiles, only: initial_file_get_id, topo_file_get_id, pertlim
-use phys_control, only: use_gw_front, use_gw_front_igw
+use phys_control, only: use_gw_front, use_gw_front_igw, use_gw_movmtn_pbl
use dyn_grid, only: ini_grid_name, timelevel, hvcoord, edgebuf, &
ini_grid_hdim_name
@@ -79,6 +79,7 @@ module dyn_comp
! Frontogenesis indices
integer, public :: frontgf_idx = -1
integer, public :: frontga_idx = -1
+integer, public :: vort4gw_idx = -1
interface read_dyn_var
module procedure read_dyn_field_2d
@@ -572,6 +573,10 @@ subroutine dyn_register()
call pbuf_add_field("FRONTGA", "global", dtype_r8, (/pcols,pver/), &
frontga_idx)
end if
+ if (use_gw_movmtn_pbl) then
+ call pbuf_add_field("VORT4GW", "global", dtype_r8, (/pcols,pver/), &
+ vort4gw_idx)
+ end if
end subroutine dyn_register
@@ -875,8 +880,7 @@ subroutine dyn_init(dyn_in, dyn_out)
call get_loop_ranges(hybrid, ibeg=nets, iend=nete)
call prim_init2(elem, fvm, hybrid, nets, nete, TimeLevel, hvcoord)
!$OMP END PARALLEL
-
- if (use_gw_front .or. use_gw_front_igw) call gws_init(elem)
+ if (use_gw_front .or. use_gw_front_igw .or. use_gw_movmtn_pbl) call gws_init(elem)
end if ! iam < par%nprocs
call addfld ('nu_kmvis', (/ 'lev' /), 'A', '', 'Molecular viscosity Laplacian coefficient' , gridname='GLL')
diff --git a/src/dynamics/se/gravity_waves_sources.F90 b/src/dynamics/se/gravity_waves_sources.F90
index a929dfeaf1..abdbaf1315 100644
--- a/src/dynamics/se/gravity_waves_sources.F90
+++ b/src/dynamics/se/gravity_waves_sources.F90
@@ -16,10 +16,12 @@ module gravity_waves_sources
!! for use by WACCM (via dp_coupling)
public :: gws_src_fnct
+ public :: gws_src_vort
public :: gws_init
private :: compute_frontogenesis
+ private :: compute_vorticity_4gw
- type (EdgeBuffer_t) :: edge3
+ type (EdgeBuffer_t) :: edge3,edge1
type (derivative_t) :: deriv
real(r8) :: psurf_ref
@@ -40,42 +42,52 @@ subroutine gws_init(elem)
! Set up variables similar to dyn_comp and prim_driver_mod initializations
call initEdgeBuffer(par, edge3, elem, 3*nlev,nthreads=1)
+ call initEdgeBuffer(par, edge1, elem, nlev,nthreads=1)
psurf_ref = hypi(plev+1)
end subroutine gws_init
- subroutine gws_src_fnct(elem, tl, tlq, frontgf, frontga,nphys)
+ subroutine gws_src_fnct(elem, tl, tlq, frontgf, frontga, nphys)
use derivative_mod, only : derivinit
- use dimensions_mod, only : npsq, nelemd
+ use dimensions_mod, only : nelemd
use dof_mod, only : UniquePoints
use hybrid_mod, only : config_thread_region, get_loop_ranges
use parallel_mod, only : par
use ppgrid, only : pver
use thread_mod, only : horz_num_threads
use dimensions_mod, only : fv_nphys
+ use cam_abortutils, only : handle_allocate_error
+
implicit none
type (element_t), intent(inout), dimension(:) :: elem
integer, intent(in) :: tl, nphys, tlq
real (kind=r8), intent(out) :: frontgf(nphys*nphys,pver,nelemd)
real (kind=r8), intent(out) :: frontga(nphys*nphys,pver,nelemd)
+
! Local variables
type (hybrid_t) :: hybrid
- integer :: nets, nete, ithr, ncols, ie
+ integer :: nets, nete, ithr, ncols, ie, ierr
real(kind=r8), allocatable :: frontgf_thr(:,:,:,:)
real(kind=r8), allocatable :: frontga_thr(:,:,:,:)
+
! This does not need to be a thread private data-structure
call derivinit(deriv)
!!$OMP PARALLEL NUM_THREADS(horz_num_threads), DEFAULT(SHARED), PRIVATE(nets,nete,hybrid,ie,ncols,frontgf_thr,frontga_thr)
-! hybrid = config_thread_region(par,'horizontal')
hybrid = config_thread_region(par,'serial')
call get_loop_ranges(hybrid,ibeg=nets,iend=nete)
- allocate(frontgf_thr(nphys,nphys,nlev,nets:nete))
- allocate(frontga_thr(nphys,nphys,nlev,nets:nete))
+ allocate(frontgf_thr(nphys,nphys,nlev,nets:nete), stat=ierr)
+ call handle_allocate_error(ierr, 'gws_src_fnct', 'frontgf_thr')
+
+ allocate(frontga_thr(nphys,nphys,nlev,nets:nete), stat=ierr)
+ call handle_allocate_error(ierr, 'gws_src_fnct', 'frontga_thr')
+
+
call compute_frontogenesis(frontgf_thr,frontga_thr,tl,tlq,elem,deriv,hybrid,nets,nete,nphys)
+
if (fv_nphys>0) then
do ie=nets,nete
frontgf(:,:,ie) = RESHAPE(frontgf_thr(:,:,:,ie),(/nphys*nphys,nlev/))
@@ -90,10 +102,137 @@ subroutine gws_src_fnct(elem, tl, tlq, frontgf, frontga,nphys)
end if
deallocate(frontga_thr)
deallocate(frontgf_thr)
+
!!$OMP END PARALLEL
end subroutine gws_src_fnct
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ subroutine gws_src_vort(elem, tl, tlq, vort4gw, nphys)
+ use derivative_mod, only : derivinit
+ use dimensions_mod, only : nelemd
+ use dof_mod, only : UniquePoints
+ use hybrid_mod, only : config_thread_region, get_loop_ranges
+ use parallel_mod, only : par
+ use ppgrid, only : pver
+ use thread_mod, only : horz_num_threads
+ use dimensions_mod, only : fv_nphys
+ use cam_abortutils, only : handle_allocate_error
+
+ implicit none
+ type (element_t), intent(in), dimension(:) :: elem
+ integer, intent(in) :: tl, nphys, tlq
+
+ !
+ real (kind=r8), intent(out) :: vort4gw(nphys*nphys,pver,nelemd)
+
+ ! Local variables
+ type (hybrid_t) :: hybrid
+ integer :: nets, nete, ithr, ncols, ie, ierr
+
+ !
+ real(kind=r8), allocatable :: vort4gw_thr(:,:,:,:)
+
+ ! This does not need to be a thread private data-structure
+ call derivinit(deriv)
+ !!$OMP PARALLEL NUM_THREADS(horz_num_threads), DEFAULT(SHARED), PRIVATE(nets,nete,hybrid,ie,ncols,vort4gw_thr)
+ hybrid = config_thread_region(par,'serial')
+ call get_loop_ranges(hybrid,ibeg=nets,iend=nete)
+
+ allocate(vort4gw_thr(nphys,nphys,nlev,nets:nete), stat=ierr)
+ call handle_allocate_error(ierr, 'gws_src_vort', 'vort4gw_thr')
+
+ call compute_vorticity_4gw(vort4gw_thr,tl,tlq,elem,deriv,hybrid,nets,nete,nphys)
+
+ if (fv_nphys>0) then
+ do ie=nets,nete
+ vort4gw(:,:,ie) = RESHAPE(vort4gw_thr(:,:,:,ie),(/nphys*nphys,nlev/))
+ end do
+ else
+ do ie=nets,nete
+ ncols = elem(ie)%idxP%NumUniquePts
+ call UniquePoints(elem(ie)%idxP, nlev, vort4gw_thr(:,:,:,ie), vort4gw(1:ncols,:,ie))
+ end do
+ end if
+ deallocate(vort4gw_thr)
+
+ !!$OMP END PARALLEL
+
+ end subroutine gws_src_vort
+
+ subroutine compute_vorticity_4gw(vort4gw,tl,tlq,elem,ederiv,hybrid,nets,nete,nphys)
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ! compute vorticity for use in gw params
+ ! F = ( curl ) [U,V]
+ !
+ ! Original by Peter Lauritzen, Julio Bacmeister*, Dec 2024
+ ! Patterned on 'compute_frontogenesis'
+ !
+ ! * corresponding/blame-able
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ use derivative_mod, only: vorticity_sphere
+ use edge_mod, only: edgevpack, edgevunpack
+ use bndry_mod, only: bndry_exchange
+ use dimensions_mod, only: fv_nphys
+ use fvm_mapping, only: dyn2phys
+
+ type(hybrid_t), intent(in) :: hybrid
+ type(element_t), intent(in) :: elem(:)
+ type(derivative_t), intent(in) :: ederiv
+ integer, intent(in) :: nets,nete,nphys
+ integer, intent(in) :: tl,tlq
+ real(r8), intent(out) :: vort4gw(nphys,nphys,nlev,nets:nete)
+
+ ! local
+ real(r8) :: area_inv(fv_nphys,fv_nphys), tmp(np,np)
+ real(r8) :: vort_gll(np,np,nlev,nets:nete)
+ integer :: k,kptr,i,j,ie,component,h,nq,m_cnst,n0
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ! First calculate vorticity on GLL grid
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ! set timelevel=1 fro velocities
+ n0=tl
+ do ie=nets,nete
+ do k=1,nlev
+ call vorticity_sphere(elem(ie)%state%v(:,:,:,k,n0),ederiv,elem(ie),vort_gll(:,:,k,ie))
+ end do
+ do k=1,nlev
+ vort_gll(:,:,k,ie) = vort_gll(:,:,k,ie)*elem(ie)%spheremp(:,:)
+ end do
+ ! pack
+ call edgeVpack(edge1, vort_gll(:,:,:,ie),nlev,0,ie)
+ enddo
+ call bndry_exchange(hybrid,edge1,location='compute_vorticity_4gw')
+ do ie=nets,nete
+ call edgeVunpack(edge1, vort_gll(:,:,:,ie),nlev,0,ie)
+ ! apply inverse mass matrix,
+ do k=1,nlev
+ vort_gll(:,:,k,ie) = vort_gll(:,:,k,ie)*elem(ie)%rspheremp(:,:)
+ end do
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ! Now regrid from GLL to PhysGrid if necessary
+ ! otherwise just return vorticity on GLL grid
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if (fv_nphys>0) then
+ tmp = 1.0_r8
+ area_inv = dyn2phys(tmp,elem(ie)%metdet)
+ area_inv = 1.0_r8/area_inv
+ do k=1,nlev
+ vort4gw(:,:,k,ie) = dyn2phys( vort_gll(:,:,k,ie) , elem(ie)%metdet , area_inv )
+ end do
+ else
+ do k=1,nlev
+ vort4gw(:,:,k,ie) = vort_gll(:,:,k,ie)
+ end do
+ end if
+ enddo
+
+
+ end subroutine compute_vorticity_4gw
+
+
subroutine compute_frontogenesis(frontgf,frontga,tl,tlq,elem,ederiv,hybrid,nets,nete,nphys)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! compute frontogenesis function F
diff --git a/src/physics/cam/cam_snapshot.F90 b/src/physics/cam/cam_snapshot.F90
index 7e7d83e9ef..da79aeb517 100644
--- a/src/physics/cam/cam_snapshot.F90
+++ b/src/physics/cam/cam_snapshot.F90
@@ -115,18 +115,53 @@ subroutine cam_snapshot_all_outfld_tphysbc(file_num, state, tend, cam_in, cam_ou
lchnk = state%lchnk
+ call cam_history_snapshot_activate('tphysbc_flx_heat', file_num)
call outfld('tphysbc_flx_heat', flx_heat, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_flx_heat')
+
+ call cam_history_snapshot_activate('tphysbc_cmfmc', file_num)
call outfld('tphysbc_cmfmc', cmfmc, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_cmfmc')
+
+ call cam_history_snapshot_activate('tphysbc_cmfcme', file_num)
call outfld('tphysbc_cmfcme', cmfcme, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_cmfcme')
+
+ call cam_history_snapshot_activate('tphysbc_zdu', file_num)
call outfld('tphysbc_zdu', zdu, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_zdu')
+
+ call cam_history_snapshot_activate('tphysbc_rliq', file_num)
call outfld('tphysbc_rliq', rliq, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_rliq')
+
+ call cam_history_snapshot_activate('tphysbc_rice', file_num)
call outfld('tphysbc_rice', rice, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_rice')
+
+ call cam_history_snapshot_activate('tphysbc_dlf', file_num)
call outfld('tphysbc_dlf', dlf, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_dlf')
+
+ call cam_history_snapshot_activate('tphysbc_dlf2', file_num)
call outfld('tphysbc_dlf2', dlf2, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_dlf2')
+
+ call cam_history_snapshot_activate('tphysbc_rliq2', file_num)
call outfld('tphysbc_rliq2', rliq2, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_rliq2')
+
+ call cam_history_snapshot_activate('tphysbc_det_s', file_num)
call outfld('tphysbc_det_s', det_s, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_det_s')
+
+ call cam_history_snapshot_activate('tphysbc_det_ice', file_num)
call outfld('tphysbc_det_ice', det_ice, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_det_ice')
+
+ call cam_history_snapshot_activate('tphysbc_net_flx', file_num)
call outfld('tphysbc_net_flx', net_flx, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysbc_net_flx')
call cam_snapshot_all_outfld(file_num, state, tend, cam_in, cam_out, pbuf)
@@ -163,10 +198,22 @@ subroutine cam_snapshot_all_outfld_tphysac(file_num, state, tend, cam_in, cam_ou
lchnk = state%lchnk
+ call cam_history_snapshot_activate('tphysac_fh2o', file_num)
call outfld('tphysac_fh2o', fh2o, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysac_fh2o')
+
+ call cam_history_snapshot_activate('tphysac_surfric', file_num)
call outfld('tphysac_surfric', surfric, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysac_surfric')
+
+ call cam_history_snapshot_activate('tphysac_obklen', file_num)
call outfld('tphysac_obklen', obklen, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysac_obklen')
+
+ call cam_history_snapshot_activate('tphysac_flx_heat', file_num)
call outfld('tphysac_flx_heat', flx_heat, pcols, lchnk)
+ call cam_history_snapshot_deactivate('tphysac_flx_heat')
+
call cam_snapshot_all_outfld(file_num, state, tend, cam_in, cam_out, pbuf)
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 3285862fae..7b3297b67d 100644
--- a/src/physics/cam/cloud_fraction.F90
+++ b/src/physics/cam/cloud_fraction.F90
@@ -5,7 +5,7 @@ module cloud_fraction
use shr_kind_mod, only: r8 => shr_kind_r8
use ppgrid, only: pcols, pver, pverp
- use ref_pres, only: pref_mid
+ use ref_pres, only: pref_mid
use spmd_utils, only: masterproc
use cam_logfile, only: iulog
use cam_abortutils, only: endrun
@@ -22,7 +22,6 @@ module cloud_fraction
cldfrc_init, &! Inititialization of cloud_fraction run-time parameters
cldfrc_getparams, &! public access of tuning parameters
cldfrc, &! Computation of cloud fraction
- cldfrc_fice, &! Calculate fraction of condensate in ice phase (radiation partitioning)
dp1, &! parameter for deep convection cloud fraction needed in clubb_intr
dp2 ! parameter for deep convection cloud fraction needed in clubb_intr
@@ -32,9 +31,9 @@ module cloud_fraction
! Top level
integer :: top_lev = 1
- ! Physics buffer indices
- integer :: sh_frac_idx = 0
- integer :: dp_frac_idx = 0
+ ! Physics buffer indices
+ integer :: sh_frac_idx = 0
+ integer :: dp_frac_idx = 0
! Namelist variables
logical :: cldfrc_freeze_dry ! switch for Vavrus correction
@@ -154,8 +153,8 @@ subroutine cldfrc_register
!-----------------------------------------------------------------------
- call pbuf_add_field('SH_FRAC', 'physpkg', dtype_r8, (/pcols,pver/), sh_frac_idx)
- call pbuf_add_field('DP_FRAC', 'physpkg', dtype_r8, (/pcols,pver/), dp_frac_idx)
+ call pbuf_add_field('SH_FRAC', 'physpkg', dtype_r8, (/pcols,pver/), sh_frac_idx)
+ call pbuf_add_field('DP_FRAC', 'physpkg', dtype_r8, (/pcols,pver/), dp_frac_idx)
end subroutine cldfrc_register
@@ -205,20 +204,19 @@ 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.
endif
- if ( masterproc ) then
+ 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
@@ -249,38 +247,38 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
cmfmc ,cmfmc2 ,landfrac,snowh ,concld ,cldst , &
ts ,sst ,ps ,zdu ,ocnfrac ,&
rhu00 ,cldice ,icecldf ,liqcldf ,relhum ,dindex )
- !-----------------------------------------------------------------------
- !
- ! Purpose:
- ! Compute cloud fraction
- !
- !
- ! Method:
+ !-----------------------------------------------------------------------
+ !
+ ! Purpose:
+ ! Compute cloud fraction
+ !
+ !
+ ! Method:
! This calculate cloud fraction using a relative humidity threshold
- ! The threshold depends upon pressure, and upon the presence or absence
- ! of convection as defined by a reasonably large vertical mass flux
+ ! The threshold depends upon pressure, and upon the presence or absence
+ ! of convection as defined by a reasonably large vertical mass flux
! entering that layer from below.
- !
+ !
! Author: Many. Last modified by Jim McCaa
- !
+ !
!-----------------------------------------------------------------------
use cam_history, only: outfld
use physconst, only: cappa, gravit, rair, tmelt
use wv_saturation, only: qsat, qsat_water, svp_ice_vect
use phys_grid, only: get_rlat_all_p, get_rlon_all_p
-
+
!RBN - Need this to write shallow,deep fraction to phys buffer.
!PJR - we should probably make seperate modules for determining convective
! clouds and make this one just responsible for relative humidity clouds
-
+
use physics_buffer, only: physics_buffer_desc, pbuf_get_field
! Arguments
integer, intent(in) :: lchnk ! chunk identifier
integer, intent(in) :: ncol ! number of atmospheric columns
integer, intent(in) :: dindex ! 0 or 1 to perturb rh
-
+
type(physics_buffer_desc), pointer :: pbuf(:)
real(r8), intent(in) :: pmid(pcols,pver) ! midpoint pressures
real(r8), intent(in) :: temp(pcols,pver) ! temperature
@@ -307,7 +305,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
real(r8), intent(out) :: clc(pcols) ! column convective cloud amount
real(r8), intent(out) :: cldst(pcols,pver) ! cloud fraction
real(r8), intent(out) :: rhu00(pcols,pver) ! RH threshold for cloud
- real(r8), intent(out) :: relhum(pcols,pver) ! RH
+ real(r8), intent(out) :: relhum(pcols,pver) ! RH
real(r8), intent(out) :: icecldf(pcols,pver) ! ice cloud fraction
real(r8), intent(out) :: liqcldf(pcols,pver) ! liquid cloud fraction (combined into cloud)
@@ -376,7 +374,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! The idea is that the RH limits for condensation are strict only for
! water saturation
!
- ! Ice clouds are formed by explicit parameterization of ice nucleation.
+ ! Ice clouds are formed by explicit parameterization of ice nucleation.
! Closure for ice cloud fraction is done on available cloud ice, such that
! the in-cloud ice content matches an empirical fit
! thus, icecldf = min(cldice/icicval,1) where icicval = f(temp,cldice,numice)
@@ -385,17 +383,17 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! No dA/dt term for ice?
!
! There are three co-existing cloud types: convective, inversion related low-level
- ! stratocumulus, and layered cloud (based on relative humidity). Layered and
- ! stratocumulus clouds do not compete with convective cloud for which one creates
- ! the most cloud. They contribute collectively to the total grid-box average cloud
- ! amount. This is reflected in the way in which the total cloud amount is evaluated
+ ! stratocumulus, and layered cloud (based on relative humidity). Layered and
+ ! stratocumulus clouds do not compete with convective cloud for which one creates
+ ! the most cloud. They contribute collectively to the total grid-box average cloud
+ ! amount. This is reflected in the way in which the total cloud amount is evaluated
! (a sum as opposed to a logical "or" operation)
!
!==================================================================================
! set defaults for rhu00
rhu00(:,:) = 2.0_r8
! define rh perturbation in order to estimate rhdfda
- rhpert = 0.01_r8
+ rhpert = 0.01_r8
!set Wang and Sassen IWC paramters
a=26.87_r8
@@ -460,7 +458,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
!
! Estimate of local convective cloud cover based on convective mass flux
- ! Modify local large-scale relative humidity to account for presence of
+ ! Modify local large-scale relative humidity to account for presence of
! convective cloud when evaluating relative humidity based layered cloud amount
!
concld(:ncol,top_lev:pver) = 0.0_r8
@@ -468,7 +466,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! cloud mass flux in SI units of kg/m2/s; should produce typical numbers of 20%
! shallow and deep convective cloudiness are evaluated separately (since processes
! are evaluated separately) and summed
- !
+ !
#ifndef PERGRO
do k=top_lev,pver
do i=1,ncol
@@ -488,7 +486,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! ****** Compute layer cloudiness ******
!
!====================================================================
- ! Begin the evaluation of layered cloud amount based on (modified) RH
+ ! Begin the evaluation of layered cloud amount based on (modified) RH
!====================================================================
!
numkcld = pver
@@ -517,7 +515,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! SJV: decrease cloud amount if very low water vapor content
! (thus very cold): "freeze dry"
if (cldfrc_freeze_dry) then
- rhcloud(i,k) = rhcloud(i,k)*max(0.15_r8,min(1.0_r8,q(i,k)/0.0030_r8))
+ rhcloud(i,k) = rhcloud(i,k)*max(0.15_r8,min(1.0_r8,q(i,k)/0.0030_r8))
endif
else if ( pmid(i,k).lt.premit ) then
@@ -537,7 +535,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
! linear rh threshold transition between thresholds for low & high cloud
!
rhwght = (premib-(max(pmid(i,k),premit)))/(premib-premit)
-
+
if (land(i) .and. (snowh(i) <= 0.000001_r8)) then
rhlim = rhminh*rhwght + (rhminl - rhminl_adj_land)*(1.0_r8-rhwght)
else
@@ -591,7 +589,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
!--------ICE CLOUD OPTION 3--------Wood & Field 2000 (JAS)
! eq 6: cloud fraction = 1 - exp (-K * qc/qsati)
-
+
icecldf(i,k)=1._r8 - exp(-Kc*cldice(i,k)/(qs(i,k)*(esi(i,k)/esl(i,k))))
icecldf(i,k)=max(0._r8,min(icecldf(i,k),1._r8))
else
@@ -634,7 +632,7 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
cloud(i,k) = rhcloud(i,k)
end if
end do
- end do
+ end do
!
! Add in the marine strat
! MARINE STRATUS SHOULD BE A SPECIAL CASE OF LAYERED CLOUD
@@ -644,20 +642,20 @@ subroutine cldfrc(lchnk ,ncol , pbuf, &
!===================================================================================
!
! SOME OBSERVATIONS ABOUT THE FOLLOWING SECTION OF CODE (missed in earlier look)
- ! K700 IS SET AS A CONSTANT BASED ON HYBRID COORDINATE: IT DOES NOT DEPEND ON
- ! LOCAL PRESSURE; THERE IS NO PRESSURE RAMP => LOOKS LEVEL DEPENDENT AND
+ ! K700 IS SET AS A CONSTANT BASED ON HYBRID COORDINATE: IT DOES NOT DEPEND ON
+ ! LOCAL PRESSURE; THERE IS NO PRESSURE RAMP => LOOKS LEVEL DEPENDENT AND
! DISCONTINUOUS IN SPACE (I.E., STRATUS WILL END SUDDENLY WITH NO TRANSITION)
!
! IT APPEARS THAT STRAT IS EVALUATED ACCORDING TO KLEIN AND HARTMANN; HOWEVER,
! THE ACTUAL STRATUS AMOUNT (CLDST) APPEARS TO DEPEND DIRECTLY ON THE RH BELOW
- ! THE STRONGEST PART OF THE LOW LEVEL INVERSION.
+ ! THE STRONGEST PART OF THE LOW LEVEL INVERSION.
!PJR answers: 1) the rh limitation is a physical/mathematical limitation
! cant have more cloud than there is RH
! allowed the cloud to exist two layers below the inversion
! because the numerics frequently make 50% relative humidity
! in level below the inversion which would allow no cloud
! 2) since the cloud is only allowed over ocean, it should
- ! be very insensitive to surface pressure (except due to
+ ! be very insensitive to surface pressure (except due to
! spectral ringing, which also causes so many other problems
! I didnt worry about it.
!
@@ -738,77 +736,4 @@ end subroutine cldfrc
!================================================================================================
- subroutine cldfrc_fice(ncol, t, fice, fsnow)
-!
-! Compute the fraction of the total cloud water which is in ice phase.
-! The fraction depends on temperature only.
-! This is the form that was used for radiation, the code came from cldefr originally
-!
-! Author: B. A. Boville Sept 10, 2002
-! modified: PJR 3/13/03 (added fsnow to ascribe snow production for convection )
-!-----------------------------------------------------------------------
- use physconst, only: tmelt
-
-! Arguments
- integer, intent(in) :: ncol ! number of active columns
- real(r8), intent(in) :: t(:,:) ! temperature
-
- real(r8), intent(out) :: fice(:,:) ! Fractional ice content within cloud
- real(r8), intent(out) :: fsnow(:,:) ! Fractional snow content for convection
-
-! Local variables
- real(r8) :: tmax_fice ! max temperature for cloud ice formation
- real(r8) :: tmin_fice ! min temperature for cloud ice formation
- real(r8) :: tmax_fsnow ! max temperature for transition to convective snow
- real(r8) :: tmin_fsnow ! min temperature for transition to convective snow
-
- integer :: i,k ! loop indexes
-
-!-----------------------------------------------------------------------
-
- tmax_fice = tmelt - 10._r8 ! max temperature for cloud ice formation
- tmin_fice = tmax_fice - 30._r8 ! min temperature for cloud ice formation
- tmax_fsnow = tmelt ! max temperature for transition to convective snow
- tmin_fsnow = tmelt - 5._r8 ! min temperature for transition to convective snow
-
- fice(:,:top_lev-1) = 0._r8
- fsnow(:,:top_lev-1) = 0._r8
-
-! Define fractional amount of cloud that is ice
- do k=top_lev,pver
- do i=1,ncol
-
-! If warmer than tmax then water phase
- if (t(i,k) > tmax_fice) then
- fice(i,k) = 0.0_r8
-
-! If colder than tmin then ice phase
- else if (t(i,k) < tmin_fice) then
- fice(i,k) = 1.0_r8
-
-! Otherwise mixed phase, with ice fraction decreasing linearly from tmin to tmax
- else
- fice(i,k) =(tmax_fice - t(i,k)) / (tmax_fice - tmin_fice)
- end if
-
-! snow fraction partitioning
-
-! If warmer than tmax then water phase
- if (t(i,k) > tmax_fsnow) then
- fsnow(i,k) = 0.0_r8
-
-! If colder than tmin then ice phase
- else if (t(i,k) < tmin_fsnow) then
- fsnow(i,k) = 1.0_r8
-
-! Otherwise mixed phase, with ice fraction decreasing linearly from tmin to tmax
- else
- fsnow(i,k) =(tmax_fsnow - t(i,k)) / (tmax_fsnow - tmin_fsnow)
- end if
-
- end do
- end do
-
- end subroutine cldfrc_fice
-
end module cloud_fraction
diff --git a/src/physics/cam/clubb_intr.F90 b/src/physics/cam/clubb_intr.F90
index a9f25f0256..9bbed56277 100644
--- a/src/physics/cam/clubb_intr.F90
+++ b/src/physics/cam/clubb_intr.F90
@@ -478,7 +478,6 @@ module clubb_intr
integer :: &
dlfzm_idx = -1, & ! ZM detrained convective cloud water mixing ratio.
- difzm_idx = -1, & ! ZM detrained convective cloud ice mixing ratio.
dnlfzm_idx = -1, & ! ZM detrained convective cloud water num concen.
dnifzm_idx = -1 ! ZM detrained convective cloud ice num concen.
@@ -2525,7 +2524,6 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
! ZM microphysics
real(r8), pointer :: dlfzm(:,:) ! ZM detrained convective cloud water mixing ratio.
- real(r8), pointer :: difzm(:,:) ! ZM detrained convective cloud ice mixing ratio.
real(r8), pointer :: dnlfzm(:,:) ! ZM detrained convective cloud water num concen.
real(r8), pointer :: dnifzm(:,:) ! ZM detrained convective cloud ice num concen.
@@ -2869,10 +2867,14 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
!$acc rrho, prer_evap, rtp2_mc_zt, thlp2_mc_zt, wprtp_mc_zt, wpthlp_mc_zt, rtpthlp_mc_zt ) &
!$acc copy( um, vm, upwp, vpwp, wpthvp, wp2thvp, rtpthvp, thlpthvp, up2, vp2, up3, vp3, &
!$acc wp2, wp3, rtp2, thlp2, rtp3, thlp3, thlm, rtm, rvm, wprtp, wpthlp, rtpthlp, &
+ !$acc pdf_zm_w_1, pdf_zm_w_2, pdf_zm_varnce_w_1, pdf_zm_varnce_w_2, pdf_zm_mixt_frac, &
!$acc cloud_frac, wp2rtp, wp2thlp, uprcp, vprcp, rc_coef, wp4, wpup2, wpvp2, &
!$acc ttend_clubb_mc, upwp_clubb_gw_mc, vpwp_clubb_gw_mc, thlp2_clubb_gw_mc, wpthlp_clubb_gw_mc, &
!$acc ttend_clubb, upwp_clubb_gw, vpwp_clubb_gw, thlp2_clubb_gw, wpthlp_clubb_gw, &
- !$acc wp2up2, wp2vp2, ice_supersat_frac ) &
+ !$acc wp2up2, wp2vp2, ice_supersat_frac, &
+ !$acc pdf_params_zm_chnk(lchnk)%w_1, pdf_params_zm_chnk(lchnk)%w_2, &
+ !$acc pdf_params_zm_chnk(lchnk)%varnce_w_1, pdf_params_zm_chnk(lchnk)%varnce_w_2, &
+ !$acc pdf_params_zm_chnk(lchnk)%mixt_frac ) &
!$acc copyout( temp2d, temp2dp, rtp2_zt_out, thl2_zt_out, wp2_zt_out, pdfp_rtp2, wm_zt_out, inv_exner_clubb, &
!$acc rcm, wprcp, rcm_in_layer, cloud_cover, zt_out, zi_out, khzm, qclvar, thv, dz_g, &
!$acc clubbtop, se_dis, eleak, clubb_s, wpthvp_clubb, wprcp_clubb ) &
@@ -2893,7 +2895,6 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
!$acc radf, wpthlp_sfc, clubb_params, sfc_elevation, wprtp_sfc, upwp_sfc, vpwp_sfc, &
!$acc rtm_ref, thlm_ref, um_ref, vm_ref, ug, vg, p_in_Pa, exner, um_pert_inout, &
!$acc inv_exner_clubb_surf, thlprcp_out, zi_g, zt_g, qrl_clubb, &
- !$acc pdf_zm_w_1, pdf_zm_w_2, pdf_zm_varnce_w_1, pdf_zm_varnce_w_2, pdf_zm_mixt_frac, &
!$acc pdf_params_chnk(lchnk)%w_1, pdf_params_chnk(lchnk)%w_2, &
!$acc pdf_params_chnk(lchnk)%varnce_w_1, pdf_params_chnk(lchnk)%varnce_w_2, &
!$acc pdf_params_chnk(lchnk)%rt_1, pdf_params_chnk(lchnk)%rt_2, &
@@ -2917,8 +2918,6 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
!$acc pdf_params_chnk(lchnk)%cloud_frac_1, pdf_params_chnk(lchnk)%cloud_frac_2, &
!$acc pdf_params_chnk(lchnk)%mixt_frac, pdf_params_chnk(lchnk)%ice_supersat_frac_1, &
!$acc pdf_params_chnk(lchnk)%ice_supersat_frac_2, &
- !$acc pdf_params_zm_chnk(lchnk)%w_1, pdf_params_zm_chnk(lchnk)%w_2, &
- !$acc pdf_params_zm_chnk(lchnk)%varnce_w_1, pdf_params_zm_chnk(lchnk)%varnce_w_2, &
!$acc pdf_params_zm_chnk(lchnk)%rt_1, pdf_params_zm_chnk(lchnk)%rt_2, &
!$acc pdf_params_zm_chnk(lchnk)%varnce_rt_1, pdf_params_zm_chnk(lchnk)%varnce_rt_2, &
!$acc pdf_params_zm_chnk(lchnk)%thl_1, pdf_params_zm_chnk(lchnk)%thl_2, &
@@ -2938,8 +2937,7 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
!$acc pdf_params_zm_chnk(lchnk)%corr_chi_eta_2, pdf_params_zm_chnk(lchnk)%rsatl_1, &
!$acc pdf_params_zm_chnk(lchnk)%rsatl_2, pdf_params_zm_chnk(lchnk)%rc_1, pdf_params_zm_chnk(lchnk)%rc_2, &
!$acc pdf_params_zm_chnk(lchnk)%cloud_frac_1, pdf_params_zm_chnk(lchnk)%cloud_frac_2, &
- !$acc pdf_params_zm_chnk(lchnk)%mixt_frac, pdf_params_zm_chnk(lchnk)%ice_supersat_frac_1, &
- !$acc pdf_params_zm_chnk(lchnk)%ice_supersat_frac_2 )
+ !$acc pdf_params_zm_chnk(lchnk)%ice_supersat_frac_1, pdf_params_zm_chnk(lchnk)%ice_supersat_frac_2 )
!$acc data if( sclr_dim > 0 ) &
!$acc create( wpsclrp_sfc, sclrm_forcing, sclrm, wpsclrp, sclrp2, sclrp3, sclrprtp, sclrpthlp, sclrpthvp_inout) &
@@ -4700,8 +4698,8 @@ subroutine clubb_tend_cam( state, ptend_all, pbuf, hdtime, &
! --------------------------------------------------------------------------------- !
do i=1,ncol
do k=1,pver
- !use local exner since state%exner is not a proper exner
- th(i,k) = state1%t(i,k)*inv_exner_clubb(i,k)
+ !subroutine pblind expects "Stull" definition of Exner
+ th(i,k) = state1%t(i,k)*state1%exner(i,k)
!thv should have condensate loading to be consistent with earlier def's in this module
thv(i,k) = th(i,k)*(1.0_r8+zvir*state1%q(i,k,ixq) - state1%q(i,k,ixcldliq))
enddo
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 902187eb24..9edd28c696 100644
--- a/src/physics/cam/convect_shallow.F90
+++ b/src/physics/cam/convect_shallow.F90
@@ -14,10 +14,12 @@ module convect_shallow
use physconst, only : cpair, zvir
use ppgrid, only : pver, pcols, pverp
use zm_conv_evap, only : zm_conv_evap_run
- use zm_conv_intr, only : zmconv_ke, zmconv_ke_lnd, zmconv_org
+ use zm_conv_intr, only : zmconv_ke, zmconv_ke_lnd
use cam_history, only : outfld, addfld, horiz_only
use cam_logfile, only : iulog
use phys_control, only : phys_getopts
+ use cloud_fraction_fice, only: cloud_fraction_fice_run
+ use ref_pres, only: trop_cloud_top_lev
implicit none
private
@@ -88,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 )
@@ -165,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 !
! ------------------------------------------------- !
@@ -473,10 +469,21 @@ subroutine convect_shallow_tend( ztodt , cmfmc , &
real(r8), pointer, dimension(:,:) :: cmfmc2 ! (pcols,pverp) Updraft mass flux by shallow convection [ kg/s/m2 ]
real(r8), pointer, dimension(:,:) :: sh_e_ed_ratio ! (pcols,pver) fer/(fer+fdr) from uwschu
+ real(r8), dimension(pcols,pver) :: fsnow_conv
+ real(r8), dimension(pcols,pver) :: fice
+
logical :: lq(pcnst)
type(unicon_out_t) :: unicon_out
+ character(len=40) :: scheme_name
+ character(len=16) :: macrop_scheme
+ character(len=512):: errmsg
+ integer :: errflg
+ integer :: top_lev
+
+
+
! ----------------------- !
! Main Computation Begins !
! ----------------------- !
@@ -872,16 +879,25 @@ subroutine convect_shallow_tend( ztodt , cmfmc , &
tend_s_snwprd(:,:) = 0._r8
tend_s_snwevmlt(:,:) = 0._r8
snow(:) = 0._r8
+ fice(:,:) = 0._r8
+ fsnow_conv(:,:) = 0._r8
!REMOVECAM_END
+ top_lev = 1
+ call phys_getopts (macrop_scheme_out = macrop_scheme)
+ if ( .not. (macrop_scheme == "rk" .or. macrop_scheme == "SPCAM_sam1mom")) top_lev = trop_cloud_top_lev
+
+ call cloud_fraction_fice_run(ncol, state1%t(1:ncol,:), tmelt, top_lev, pver, fice(1:ncol,:), fsnow_conv(1:ncol,:))
+
call zm_conv_evap_run(state1%ncol, pver, pverp, &
gravit, latice, latvap, tmelt, &
- cpair, zmconv_ke, zmconv_ke_lnd, zmconv_org, &
+ cpair, zmconv_ke, zmconv_ke_lnd, &
state1%t(:ncol,:),state1%pmid(:ncol,:),state1%pdel(:ncol,:),state1%q(:ncol,:pver,1), &
landfracdum(:ncol), &
ptend_loc%s(:ncol,:), tend_s_snwprd(:ncol,:), tend_s_snwevmlt(:ncol,:), ptend_loc%q(:ncol,:pver,1), &
rprdsh(:ncol,:), cld(:ncol,:), ztodt, &
- precc(:ncol), snow(:ncol), ntprprd(:ncol,:), ntsnprd(:ncol,:), flxprec(:ncol,:), flxsnow(:ncol,:) )
+ precc(:ncol), snow(:ncol), ntprprd(:ncol,:), ntsnprd(:ncol,:), fsnow_conv(:ncol,:), flxprec(:ncol,:), flxsnow(:ncol,:),&
+ scheme_name, errmsg, errflg)
! ---------------------------------------------- !
! record history variables from zm_conv_evap_run !
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/gw_common.F90 b/src/physics/cam/gw_common.F90
index 04014c8c97..a9897cb140 100644
--- a/src/physics/cam/gw_common.F90
+++ b/src/physics/cam/gw_common.F90
@@ -132,7 +132,9 @@ function new_GWBand(ngwv, dc, fcrit2, wavelength) result(band)
! Simple assignments.
band%ngwv = ngwv
band%dc = dc
- band%fcrit2 = fcrit2
+
+ ! For now just ensure fcrit is always set to 1
+ band%fcrit2 = 1.0_r8 ! fcrit2
! Uniform phase speed reference grid.
allocate(band%cref(-ngwv:ngwv))
@@ -147,7 +149,7 @@ end function new_GWBand
!==========================================================================
subroutine gw_common_init(pver_in, &
- tau_0_ubc_in, ktop_in, gravit_in, rair_in, alpha_in, &
+ tau_0_ubc_in, ktop_in, gravit_in, rair_in, alpha_in, &
prndl_in, qbo_hdepth_scaling_in, errstring)
integer, intent(in) :: pver_in
@@ -356,8 +358,8 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
real(r8), intent(in), optional :: &
kwvrdg(ncol)
- ! Factor for saturation calculation. Here backwards
- ! compatibility. I believe it should be 1.0 (jtb).
+ ! Factor for saturation calculation. Here backwards
+ ! compatibility. I believe it should be 1.0 (jtb).
! Looks like it has been 2.0 for a while in CAM.
real(r8), intent(in), optional :: &
satfac_in
@@ -425,7 +427,7 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
lapply_effgw = .TRUE.
endif
-
+
! Lowest levels that loops need to iterate over.
kbot_tend = maxval(tend_level)
kbot_src = maxval(src_level)
@@ -457,9 +459,9 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
!------------------------------------------------------------------------
! Loop from bottom to top to get stress profiles.
- ! do k = kbot_src-1, ktop, -1 !++jtb I think this is right
- do k = kbot_src, ktop, -1 !++ but this is in model now
-
+ ! do k = kbot_src-1, ktop, -1 !++jtb I think this is right
+ do k = kbot_src, ktop, -1 !++ but this is in model now
+
! Determine the diffusivity for each column.
d = dback + kvtt(:,k)
@@ -552,8 +554,8 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
! Write out pre-adjustment tau profile for diagnostc purposes.
! Current implementation only makes sense for orographic waves.
- ! Fix later.
- if (PRESENT(tau_diag)) then
+ ! Fix later.
+ if (PRESENT(tau_diag)) then
tau_diag(:,:) = tau(:,0,:)
end if
@@ -592,11 +594,11 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
ubtl = min(ubtl, umcfac * abs(c(:,l)-ubm(:,k)) / dt)
if (.not. lapply_effgw) ubtl = min(ubtl, tndmax)
-
+
where (k <= tend_level)
! Save tendency for each wave (for later computation of kzz).
- ! sign function returns magnitude of ubtl with sign of c-ubm
+ ! sign function returns magnitude of ubtl with sign of c-ubm
! Renders ubt/ubm check for mountain waves unecessary
gwut(:,k,l) = sign(ubtl, c(:,l)-ubm(:,k))
ubt(:,k) = ubt(:,k) + gwut(:,k,l)
@@ -620,7 +622,7 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
else
ubt_lim_ratio = 1._r8
end if
-
+
do l = -band%ngwv, band%ngwv
gwut(:,k,l) = ubt_lim_ratio*gwut(:,k,l)
! Redetermine the effective stress on the interface below from the
@@ -634,11 +636,11 @@ subroutine gw_drag_prof(ncol, band, p, src_level, tend_level, dt, &
!--------------------------------------------------
where( abs(gwut(:,k,l)) < 1.e-15_r8 )
gwut(:,k,l) = 0._r8
- endwhere
+ endwhere
where (k <= tend_level)
- tau(:,l,k+1) = tau(:,l,k) + &
- abs(gwut(:,k,l)) * p%del(:,k) / gravit
+ tau(:,l,k+1) = tau(:,l,k) + &
+ abs(gwut(:,k,l)) * p%del(:,k) / gravit
end where
end do
@@ -866,7 +868,7 @@ subroutine momentum_fixer(tend_level, p, um_flux, vm_flux, utgw, vtgw)
vtgw(:,k) = vtgw(:,k) + dv
end where
end do
-
+
end subroutine momentum_fixer
!==========================================================================
diff --git a/src/physics/cam/gw_convect.F90 b/src/physics/cam/gw_convect.F90
index 09ca64a016..311865b499 100644
--- a/src/physics/cam/gw_convect.F90
+++ b/src/physics/cam/gw_convect.F90
@@ -161,7 +161,7 @@ subroutine gw_beres_src(ncol, band, desc, u, v, &
do k = pver, 1, -1
do i = 1, ncol
if (boti(i) == 0) then
- ! Detect if we are outside the maximum range (where z = 20 km).
+ ! Detect if we are outside the top of range (where z = 20 km).
if (zm(i,k) >= 20000._r8) then
boti(i) = k
topi(i) = k
@@ -169,17 +169,20 @@ subroutine gw_beres_src(ncol, band, desc, u, v, &
! First spot where heating rate is positive.
if (netdt(i,k) > 0.0_r8) boti(i) = k
end if
- else if (topi(i) == 0) then
- ! Detect if we are outside the maximum range (z = 20 km).
- if (zm(i,k) >= 20000._r8) then
- topi(i) = k
- else
- ! First spot where heating rate is no longer positive.
- if (.not. (netdt(i,k) > 0.0_r8)) topi(i) = k
- end if
end if
end do
- ! When all done, exit.
+ ! When all done, exit
+ if (all(boti /= 0)) exit
+ end do
+
+ do k = 1, pver
+ do i = 1, ncol
+ if (topi(i) == 0) then
+ ! First spot where heating rate is positive.
+ if ((netdt(i,k) > 0.0_r8) .AND. (zm(i,k) <= 20000._r8)) topi(i) = k-1
+ end if
+ end do
+ ! When all done, exit
if (all(topi /= 0)) exit
end do
@@ -283,7 +286,7 @@ subroutine gw_beres_src(ncol, band, desc, u, v, &
! Adjust for critical level filtering.
tau0(Umini(i):Umaxi(i)) = 0.0_r8
-
+
tau(i,:,topi(i)+1) = tau0
end if ! heating depth above min and not at the pole
diff --git a/src/physics/cam/gw_drag.F90 b/src/physics/cam/gw_drag.F90
index 798ad63059..6f2b66f886 100644
--- a/src/physics/cam/gw_drag.F90
+++ b/src/physics/cam/gw_drag.F90
@@ -109,6 +109,18 @@ module gw_drag
real(r8) :: effgw_beres_dp = unset_r8
! Beres (shallow convection).
real(r8) :: effgw_beres_sh = unset_r8
+ ! PBL moving mtn
+ real(r8) :: effgw_movmtn_pbl = unset_r8
+ integer :: movmtn_source = -1
+ integer :: movmtn_ksteer = -1
+ integer :: movmtn_klaunch = -1
+ real(r8) :: movmtn_psteer = unset_r8
+ real(r8) :: movmtn_plaunch = unset_r8
+
+ ! Parameters controlling isotropic residual
+ ! orographic GW.
+ logical :: use_gw_rdg_resid = .false.
+ real(r8) :: effgw_rdg_resid = unset_r8
! Horzontal wavelengths [m].
real(r8), parameter :: wavelength_mid = 1.e5_r8
@@ -155,6 +167,9 @@ module gw_drag
integer :: ttend_sh_idx = -1
integer :: frontgf_idx = -1
integer :: frontga_idx = -1
+
+ integer :: vort4gw_idx = -1
+
integer :: sgh_idx = -1
! From CLUBB
@@ -168,7 +183,9 @@ module gw_drag
integer, parameter :: prdg = 16
real(r8), allocatable, dimension(:,:), target :: &
- rdg_gbxar
+ rdg_gbxar, &
+ rdg_isovar, &
+ rdg_isowgt
! Meso Beta
real(r8), allocatable, dimension(:,:,:), target :: &
@@ -245,7 +262,10 @@ subroutine gw_drag_readnl(nlfile)
rdg_gamma_cd_llb, trpd_leewv_rdg_gamma, bnd_rdggm, &
gw_oro_south_fac, gw_limit_tau_without_eff, &
gw_lndscl_sgh, gw_prndl, gw_apply_tndmax, gw_qbo_hdepth_scaling, &
- gw_top_taper, front_gaussian_width, alpha_gw_movmtn
+ gw_top_taper, front_gaussian_width, alpha_gw_movmtn, use_gw_rdg_resid, &
+ effgw_rdg_resid, effgw_movmtn_pbl, movmtn_source, movmtn_psteer, &
+ movmtn_plaunch
+
!----------------------------------------------------------------------
if (use_simple_phys) return
@@ -351,6 +371,20 @@ subroutine gw_drag_readnl(nlfile)
call mpi_bcast(alpha_gw_movmtn, 1, mpi_real8, mstrid, mpicom, ierr)
if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: alpha_gw_movmtn")
+ call mpi_bcast(effgw_movmtn_pbl, 1, mpi_real8, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: effgw_movmtn_pbl")
+ call mpi_bcast(movmtn_source, 1, mpi_integer, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: movmtn_source")
+ call mpi_bcast(movmtn_psteer, 1, mpi_real8, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: movmtn_psteer")
+ call mpi_bcast(movmtn_plaunch, 1, mpi_real8, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: movmtn_plaunch")
+
+ call mpi_bcast(use_gw_rdg_resid, 1, mpi_logical, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: use_gw_rdg_resid")
+ call mpi_bcast(effgw_rdg_resid, 1, mpi_real8, mstrid, mpicom, ierr)
+ if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: effgw_rdg_resid")
+
! Check if pgwv was set.
call shr_assert(pgwv >= 0, &
@@ -636,6 +670,8 @@ subroutine gw_init()
! Get beta ridge data
allocate( &
rdg_gbxar(pcols,begchunk:endchunk), &
+ rdg_isovar(pcols,begchunk:endchunk), &
+ rdg_isowgt(pcols,begchunk:endchunk), &
rdg_hwdth(pcols,prdg,begchunk:endchunk), &
rdg_clngt(pcols,prdg,begchunk:endchunk), &
rdg_mxdis(pcols,prdg,begchunk:endchunk), &
@@ -647,6 +683,18 @@ subroutine gw_init()
if (.not. found) call endrun(sub//': ERROR: GBXAR not found on topo file')
rdg_gbxar = rdg_gbxar * (rearth/1000._r8)*(rearth/1000._r8) ! transform to km^2
+ call infld('ISOVAR', fh_topo, dim1name, dim2name, 1, pcols, &
+ begchunk, endchunk, rdg_isovar, found, gridname='physgrid')
+! if (.not. found) call endrun(sub//': ERROR: ISOVAR not found on topo file')
+ ! ++jtb - Temporary fix until topo files contain this variable
+ if (.not. found) rdg_isovar(:,:) = 0._r8
+
+ call infld('ISOWGT', fh_topo, dim1name, dim2name, 1, pcols, &
+ begchunk, endchunk, rdg_isowgt, found, gridname='physgrid')
+! if (.not. found) call endrun(sub//': ERROR: ISOWGT not found on topo file')
+ ! ++jtb - Temporary fix until topo files contain this variable
+ if (.not. found) rdg_isowgt(:,:) = 0._r8
+
call infld('HWDTH', fh_topo, dim1name, 'nrdg', dim2name, 1, pcols, &
1, prdg, begchunk, endchunk, rdg_hwdth, found, gridname='physgrid')
if (.not. found) call endrun(sub//': ERROR: HWDTH not found on topo file')
@@ -722,15 +770,39 @@ subroutine gw_init()
call addfld('ZMGW', (/ 'lev' /) , 'A' ,'m' , &
'midlayer geopotential heights in GW code ' )
+
+ call addfld('NIEGW', (/ 'ilev' /) , 'I' ,'1/s' , &
+ 'interface BV freq in GW code ' )
+ call addfld('NMEGW', (/ 'lev' /) , 'I' ,'1/s' , &
+ 'midlayer BV freq in GW code ' )
+ call addfld('RHOIEGW', (/ 'ilev' /) , 'I' ,'kg/m^3' , &
+ 'interface density in GW code ' )
+ call addfld('PINTEGW', (/ 'ilev' /) , 'I' ,'Pa' , &
+ 'interface air pressure in GW code ' )
+
call addfld('TAUM1_DIAG' , (/ 'ilev' /) , 'I' ,'N m-2' , &
'Ridge based momentum flux profile')
call addfld('TAU1RDGBETAM' , (/ 'ilev' /) , 'I' ,'N m-2' , &
'Ridge based momentum flux profile')
- call addfld('UBM1BETA', (/ 'lev' /) , 'A' ,'s-1' , &
+ call addfld('UBM1BETA', (/ 'lev' /) , 'A' ,'m s-1' , &
'On-ridge wind profile ' )
- call addfld('UBT1RDGBETA' , (/ 'lev' /) , 'I' ,'m s-1' , &
+ call addfld('UBT1RDGBETA' , (/ 'lev' /) , 'I' ,'m s-2' , &
'On-ridge wind tendency from ridge 1 ')
+ call addfld('TAURESIDBETAM' , (/ 'ilev' /) , 'I' ,'N m-2' , &
+ 'Ridge based momentum flux profile')
+ call addfld('UBMRESIDBETA', (/ 'lev' /) , 'I' ,'m s-1' , &
+ 'On-ridge wind profile ' )
+ call addfld('UBIRESIDBETA', (/ 'ilev' /) , 'I' ,'m s-1' , &
+ 'On-ridge wind profile (interface) ' )
+ call addfld('SRC_LEVEL_RESIDBETA', horiz_only , 'I' ,'1' , &
+ 'src level index for ridge residual ' )
+ call addfld('TAUORO_RESID', horiz_only , 'I' ,'N m-2' , &
+ 'Surface momentum flux from ridge residual ' )
+ call addfld('TAUDIAG_RESID' , (/ 'ilev' /) , 'I' ,'N m-2' , &
+ 'Ridge based momentum flux profile')
+
+
do i = 1, 6
write(cn, '(i1)') i
call addfld('TAU'//cn//'RDGBETAY' , (/ 'ilev' /), 'I', 'N m-2', &
@@ -751,6 +823,12 @@ subroutine gw_init()
'Ridge based momentum flux profile')
call register_vector_field('TAUARDGBETAX','TAUARDGBETAY')
+ call addfld('TAURESIDBETAY' , (/ 'ilev' /) , 'I' ,'N m-2' , &
+ 'Ridge based momentum flux profile')
+ call addfld('TAURESIDBETAX' , (/ 'ilev' /) , 'I' ,'N m-2' , &
+ 'Ridge based momentum flux profile')
+ call register_vector_field('TAURESIDBETAX','TAURESIDBETAY')
+
if (history_waccm) then
call add_default('TAUARDGBETAX', 1, ' ')
call add_default('TAUARDGBETAY ', 1, ' ')
@@ -883,6 +961,29 @@ subroutine gw_init()
end if
+ if (use_gw_movmtn_pbl) then
+ do k = 1, pver
+ ! Find steering level
+ if ( (pref_edge(k+1) >= movmtn_psteer).and.(pref_edge(k) < movmtn_psteer) ) then
+ movmtn_ksteer = k
+ end if
+ end do
+ do k = 1, pver
+ ! Find launch level
+ if ( (pref_edge(k+1) >= movmtn_plaunch).and.(pref_edge(k) < movmtn_plaunch ) ) then
+ movmtn_klaunch = k
+ end if
+ end do
+
+ end if
+ if (use_gw_movmtn_pbl) then
+
+ vort4gw_idx = pbuf_get_index('VORT4GW')
+
+ call addfld ('VORT4GW', (/ 'lev' /), 'A', 's-1', &
+ 'Vorticity')
+ end if
+
if (use_gw_front) then
call shr_assert(all(unset_r8 /= [ effgw_cm, taubgnd ]), &
@@ -1451,7 +1552,7 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
! Interface for multiple gravity wave drag parameterization.
!-----------------------------------------------------------------------
- use physics_types, only: physics_state_copy, set_dry_to_wet
+ use physics_types, only: physics_state_copy
use constituents, only: cnst_type
use physics_buffer, only: physics_buffer_desc, pbuf_get_field
use camsrfexch, only: cam_in_t
@@ -1466,7 +1567,7 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
use gw_front, only: gw_cm_src
use gw_convect, only: gw_beres_src
use gw_movmtn, only: gw_movmtn_src
-
+ use dycore, only: dycore_is
!------------------------------Arguments--------------------------------
type(physics_state), intent(in) :: state ! physics state structure
type(physics_buffer_desc), pointer :: pbuf(:) ! Physics buffer
@@ -1536,6 +1637,8 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
! Frontogenesis
real(r8), pointer :: frontgf(:,:)
real(r8), pointer :: frontga(:,:)
+ ! Vorticity source
+ real(r8), pointer :: vort4gw(:,:)
! Temperature change due to deep convection.
real(r8), pointer :: ttend_dp(:,:)
@@ -1568,6 +1671,12 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
real(r8), pointer :: angll(:,:)
! anisotropy of ridges.
real(r8), pointer :: anixy(:,:)
+ ! sqrt(residual variance) not repr by ridges (assumed isotropic).
+ real(r8), pointer :: isovar(:)
+ ! area fraction of res variance
+ real(r8), pointer :: isowgt(:)
+
+
! Gamma ridges
! width of ridges.
@@ -1628,9 +1737,6 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
! Make local copy of input state.
call physics_state_copy(state, state1)
- ! constituents are all treated as wet mmr
- call set_dry_to_wet(state1, convert_cnst_type='dry')
-
lchnk = state1%lchnk
ncol = state1%ncol
@@ -1718,13 +1824,14 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
call pbuf_get_field(pbuf, wpthlp_clubb_gw_idx, wpthlp_clubb_gw)
call pbuf_get_field(pbuf, upwp_clubb_gw_idx, upwp_clubb_gw)
call pbuf_get_field(pbuf, vpwp_clubb_gw_idx, vpwp_clubb_gw)
+ call pbuf_get_field(pbuf, vort4gw_idx, vort4gw)
xpwp_clubb(:ncol,:) = sqrt( upwp_clubb_gw(:ncol,:)**2 + vpwp_clubb_gw(:ncol,:)**2 )
- effgw = 1._r8
+ effgw = effgw_movmtn_pbl
call gw_movmtn_src(ncol, lchnk, band_movmtn , movmtn_desc, &
- u, v, ttend_dp(:ncol,:), ttend_clubb(:ncol,:), xpwp_clubb(:ncol,:) , &
- zm, alpha_gw_movmtn, src_level, tend_level, &
+ u, v, ttend_dp(:ncol,:), ttend_clubb(:ncol,:), xpwp_clubb(:ncol,:), vort4gw(:ncol,:), &
+ zm, alpha_gw_movmtn, movmtn_source, movmtn_ksteer, movmtn_klaunch, src_level, tend_level, &
tau, ubm, ubi, xv, yv, &
phase_speeds, hdepth)
!-------------------------------------------------------------
@@ -1782,6 +1889,7 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
call outfld('WPTHLP_CLUBB_GW', wpthlp_clubb_gw, pcols, lchnk)
call outfld('UPWP_CLUBB_GW', upwp_clubb_gw, pcols, lchnk)
call outfld('VPWP_CLUBB_GW', vpwp_clubb_gw, pcols, lchnk)
+ call outfld ('VORT4GW', vort4gw, pcols, lchnk)
!Deallocate variables that are no longer used:
deallocate(tau, gwut, phase_speeds)
@@ -2259,6 +2367,8 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
mxdis => rdg_mxdis(:ncol,:,lchnk)
angll => rdg_angll(:ncol,:,lchnk)
anixy => rdg_anixy(:ncol,:,lchnk)
+ isovar => rdg_isovar(:ncol,lchnk)
+ isowgt => rdg_isowgt(:ncol,lchnk)
where(mxdis < 0._r8)
mxdis = 0._r8
@@ -2277,7 +2387,9 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
u, v, t, p, piln, zm, zi, &
nm, ni, rhoi, kvtt, q, dse, &
effgw_rdg_beta, effgw_rdg_beta_max, &
+ effgw_rdg_resid, use_gw_rdg_resid, &
hwdth, clngt, gbxar, mxdis, angll, anixy, &
+ isovar, isowgt, &
rdg_beta_cd_llb, trpd_leewv_rdg_beta, &
ptend, flx_heat)
@@ -2307,7 +2419,9 @@ subroutine gw_tend(state, pbuf, dt, ptend, cam_in, flx_heat)
u, v, t, p, piln, zm, zi, &
nm, ni, rhoi, kvtt, q, dse, &
effgw_rdg_gamma, effgw_rdg_gamma_max, &
+ effgw_rdg_resid, use_gw_rdg_resid, &
hwdthg, clngtg, gbxar, mxdisg, angllg, anixyg, &
+ isovar, isowgt, &
rdg_gamma_cd_llb, trpd_leewv_rdg_gamma, &
ptend, flx_heat)
@@ -2347,13 +2461,15 @@ subroutine gw_rdg_calc( &
u, v, t, p, piln, zm, zi, &
nm, ni, rhoi, kvtt, q, dse, &
effgw_rdg, effgw_rdg_max, &
+ effgw_rdg_resid, luse_gw_rdg_resid, &
hwdth, clngt, gbxar, &
mxdis, angll, anixy, &
+ isovar, isowgt, &
rdg_cd_llb, trpd_leewv, &
ptend, flx_heat)
use coords_1d, only: Coords1D
- use gw_rdg, only: gw_rdg_src, gw_rdg_belowpeak, gw_rdg_break_trap, gw_rdg_do_vdiff
+ use gw_rdg, only: gw_rdg_src, gw_rdg_resid_src, gw_rdg_belowpeak, gw_rdg_break_trap, gw_rdg_do_vdiff
use gw_common, only: gw_drag_prof, energy_change
character(len=5), intent(in) :: type ! BETA or GAMMA
@@ -2379,6 +2495,8 @@ subroutine gw_rdg_calc( &
real(r8), intent(in) :: effgw_rdg ! Tendency efficiency.
real(r8), intent(in) :: effgw_rdg_max
+ real(r8), intent(in) :: effgw_rdg_resid ! Tendency efficiency.
+ logical, intent(in) :: luse_gw_rdg_resid ! On-Off switch
real(r8), intent(in) :: hwdth(ncol,prdg) ! width of ridges.
real(r8), intent(in) :: clngt(ncol,prdg) ! length of ridges.
real(r8), intent(in) :: gbxar(ncol) ! gridbox area
@@ -2387,6 +2505,9 @@ subroutine gw_rdg_calc( &
real(r8), intent(in) :: angll(ncol,prdg) ! orientation of ridges.
real(r8), intent(in) :: anixy(ncol,prdg) ! Anisotropy parameter.
+ real(r8), intent(in) :: isovar(ncol) ! sqrt of residual variance
+ real(r8), intent(in) :: isowgt(ncol) ! area frac of residual variance
+
real(r8), intent(in) :: rdg_cd_llb ! Drag coefficient for low-level flow
logical, intent(in) :: trpd_leewv
@@ -2606,13 +2727,70 @@ subroutine gw_rdg_calc( &
end do ! end of loop over multiple ridges
+ call outfld('TAUARDG'//trim(type)//'X', taurx, ncol, lchnk)
+ call outfld('TAUARDG'//trim(type)//'Y', taury, ncol, lchnk)
+
+ if (luse_gw_rdg_resid) then
+ ! Add additional GW from residual variance. Assumed isotropic
+ kwvrdg = 0.001_r8 / ( 100._r8 )
+ effgw = effgw_rdg_resid * isowgt
+ tauoro = 0._r8
+
+ call gw_rdg_resid_src(ncol, band_oro, p, &
+ u, v, t, isovar, kwvrdg, zi, nm, &
+ src_level, tend_level, tau, ubm, ubi, xv, yv, &
+ ubmsrc, usrc, vsrc, nsrc, rsrc, m2src, phase_speeds, tauoro )
+
+ call gw_drag_prof(ncol, band_oro, p, src_level, tend_level, dt, &
+ t, vramp, &
+ piln, rhoi, nm, ni, ubm, ubi, xv, yv, &
+ effgw, phase_speeds, kvtt, q, dse, tau, utgw, vtgw, &
+ ttgw, qtgw, egwdffi, gwut, dttdf, dttke, &
+ kwvrdg=kwvrdg, &
+ satfac_in = 1._r8, lapply_vdiff=gw_rdg_do_vdiff , tau_diag=tau_diag )
+
+ ! Add the tendencies from isotropic residual to the totals.
+ do k = 1, pver
+ ! diagnostics
+ utrdg(:,k) = utrdg(:,k) + utgw(:,k)
+ vtrdg(:,k) = vtrdg(:,k) + vtgw(:,k)
+ ttrdg(:,k) = ttrdg(:,k) + ttgw(:,k)
+ ! physics tendencies
+ ptend%u(:ncol,k) = ptend%u(:ncol,k) + utgw(:,k)
+ ptend%v(:ncol,k) = ptend%v(:ncol,k) + vtgw(:,k)
+ ptend%s(:ncol,k) = ptend%s(:ncol,k) + ttgw(:,k)
+ end do
+
+ do m = 1, pcnst
+ do k = 1, pver
+ ptend%q(:ncol,k,m) = ptend%q(:ncol,k,m) + qtgw(:,k,m)
+ end do
+ end do
+
+ do k = 1, pver+1
+ taurx0(:,k) = tau(:,0,k)*xv
+ taury0(:,k) = tau(:,0,k)*yv
+ taurx(:,k) = taurx(:,k) + taurx0(:,k)
+ taury(:,k) = taury(:,k) + taury0(:,k)
+ end do
+
+ call outfld('TAUDIAG_RESID', tau_diag, ncol, lchnk)
+ call outfld('TAUORO_RESID', tauoro , ncol, lchnk)
+ call outfld('TAURESID'//trim(type)//'M', tau(:,0,:), ncol, lchnk)
+ call outfld('TAURESID'//trim(type)//'X', taurx, ncol, lchnk)
+ call outfld('TAURESID'//trim(type)//'Y', taury, ncol, lchnk)
+
+ call outfld('UBMRESID'//trim(type), ubm, ncol, lchnk)
+ call outfld('UBIRESID'//trim(type), ubi, ncol, lchnk)
+ call outfld('SRC_LEVEL_RESID'//trim(type), real(src_level, r8) , ncol, lchnk)
+ ! end of residual variance calc
+ end if
+
! Calculate energy change for output to CAM's energy checker.
call energy_change(dt, p, u, v, ptend%u(:ncol,:), &
ptend%v(:ncol,:), ptend%s(:ncol,:), de)
flx_heat(:ncol) = de
- call outfld('TAUARDG'//trim(type)//'X', taurx, ncol, lchnk)
- call outfld('TAUARDG'//trim(type)//'Y', taury, ncol, lchnk)
if (trim(type) == 'BETA') then
fname(1) = 'TAUGWX'
diff --git a/src/physics/cam/gw_movmtn.F90 b/src/physics/cam/gw_movmtn.F90
index 0408928932..142b833eaa 100644
--- a/src/physics/cam/gw_movmtn.F90
+++ b/src/physics/cam/gw_movmtn.F90
@@ -2,7 +2,7 @@ module gw_movmtn
!
! This module parameterizes gravity waves generated by the obstacle effect produced by
-! boundary layer turbulence for convection.
+! internal circulations in the atmosphere.
!
use gw_utils, only: r8
@@ -35,12 +35,13 @@ module gw_movmtn
!==========================================================================
subroutine gw_movmtn_src(ncol,lchnk, band, desc, u, v, &
- netdt, netdt_shcu, xpwp_shcu, &
- zm, alpha_gw_movmtn, src_level, tend_level, tau, ubm, ubi, xv, yv, &
+ netdt, netdt_shcu, xpwp_shcu, vorticity, &
+ zm, alpha_gw_movmtn, movmtn_source, ksteer_in, klaunch_in, &
+ src_level, tend_level, tau, ubm, ubi, xv, yv, &
c, hdepth)
!-----------------------------------------------------------------------
! Flexible driver for gravity wave source from obstacle effects produced
-! by boundary layer turbulence or deep convection
+! by internal circulations
!-----------------------------------------------------------------------
use gw_utils, only: get_unit_vector, dot_2d, midpoint_interp
use gw_common, only: GWBand, pver, qbo_hdepth_scaling
@@ -65,10 +66,16 @@ subroutine gw_movmtn_src(ncol,lchnk, band, desc, u, v, &
real(r8), intent(in) :: netdt_shcu(:,:)
! Higher order flux from ShCu/PBL.
real(r8), intent(in) :: xpwp_shcu(ncol,pver+1)
+ ! Relative vorticity
+ real(r8), intent(in) :: vorticity(ncol,pver)
! Midpoint altitudes.
real(r8), intent(in) :: zm(ncol,pver)
! tunable parameter controlling proportion of PBL momentum flux emitted as GW
real(r8), intent(in) :: alpha_gw_movmtn
+ ! code for source of gw: 1=vorticity, 2=upwp
+ integer, intent(in) :: movmtn_source
+ ! Steering level and launch level inputs
+ integer, intent(in) :: ksteer_in, klaunch_in
! Indices of top gravity wave source level and lowest level where wind
! tendencies are allowed.
@@ -136,10 +143,12 @@ subroutine gw_movmtn_src(ncol,lchnk, band, desc, u, v, &
! Index for ground based phase speed bin
real(r8) :: c0(ncol,-band%ngwv:band%ngwv)
integer :: c_idx(ncol,-band%ngwv:band%ngwv)
- ! Flux source from ShCu/PBL
+ ! GW Flux source
real(r8) :: xpwp_src(ncol)
! Manual steering level set
- integer :: Steer_k
+ integer :: Steer_k(ncol), Launch_k(ncol)
+ ! Set source (1=vorticity, 2=PBL mom fluxes)
+ integer :: source_type
!----------------------------------------------------------------------
! Initialize tau array
@@ -149,22 +158,38 @@ subroutine gw_movmtn_src(ncol,lchnk, band, desc, u, v, &
q0 = 0.0_r8
tau0 = 0.0_r8
- !----------------------------------------------------------------------
- ! Calculate flux source from ShCu/PBL
- !----------------------------------------------------------------------
- xpwp_src = shcu_flux_src( xpwp_shcu, ncol, pver+1, alpha_gw_movmtn )
+ source_type=movmtn_source
+ if ( source_type==1 ) then
+ !----------------------------------------------------------------------
+ ! Calculate flux source from vorticity
+ !----------------------------------------------------------------------
+ call vorticity_flux_src( vorticity, ncol, pver , alpha_gw_movmtn, xpwp_src, Steer_k, Launch_k )
+ else if ( source_type==2 ) then
+ !----------------------------------------------------------------------
+ ! Calculate flux source from ShCu/PBL and set Steering level
+ !----------------------------------------------------------------------
+ call shcu_flux_src( xpwp_shcu, ncol, pver+1, alpha_gw_movmtn, xpwp_src, Steer_k, Launch_k )
+ end if
+
+ !-------------------------------------------------
+ ! Override steering and launch levels if inputs>0
+ !-------------------------------------------------
+ if (klaunch_in > 0) then
+ Launch_k(:ncol) = klaunch_in
+ end if
+ if (ksteer_in > 0) then
+ Steer_k(:ncol) = ksteer_in
+ end if
!------------------------------------------------------------------------
- ! Determine wind and unit vectors approximately at the source (steering level), then
+ ! Determine wind and unit vectors at the steering level) then
! project winds.
!------------------------------------------------------------------------
-
- ! Winds at 'steering level'
- Steer_k = pver-1
- usteer = u(:,Steer_k) !k defined in line21 (at specified altitude)
- vsteer = v(:,Steer_k)
- steer_level = real(Steer_k,r8)
-
+ do i=1,ncol
+ usteer(i) = u(i, Steer_k(i) )
+ vsteer(i) = v(i, Steer_k(i) )
+ steer_level(i) = real(Steer_k(i),r8)
+ end do
! all GW calculations on a plane, which in our case is the wind at source level -> ubi is wind in this plane
! Get the unit vector components and magnitude at the source level.
call get_unit_vector(usteer, vsteer, xv_steer, yv_steer, umag_steer)
@@ -209,7 +234,7 @@ subroutine gw_movmtn_src(ncol,lchnk, band, desc, u, v, &
if (use_gw_movmtn_pbl) then
boti=pver
- topi=Steer_k-10 ! desc%k-5
+ topi=Launch_k ! set in source subr
else
do k = pver, 1, -1 !start at surface
do i = 1, ncol
@@ -419,15 +444,19 @@ pure function index_of_nearest(x, grid) result(idx)
end function index_of_nearest
!!!!!!!!!!!!!!!!!!!!!!!!!!!
-pure function shcu_flux_src (xpwp_shcu , ncol, pverx, alpha_gw_movmtn ) result(xpwp_src)
+subroutine shcu_flux_src (xpwp_shcu , ncol, pverx, alpha_gw_movmtn, xpwp_src, steering_level, launch_level )
integer, intent(in) :: ncol,pverx
real(r8), intent(in) :: xpwp_shcu (ncol,pverx)
real(r8), intent(in) :: alpha_gw_movmtn
- real(r8) :: xpwp_src(ncol)
+ real(r8), intent(out) :: xpwp_src(ncol)
+ integer, intent(out) :: steering_level(ncol), launch_level(ncol)
integer :: k, nlayers
+ steering_level(:ncol) = (pverx-1) - 5 !++ tuning test 12/30/24
+ launch_level(:ncol) = steering_level -10 !++ tuning test 01/05/25
+
!-----------------------------------
! Simple average over layers.
! Probably can do better
@@ -439,6 +468,35 @@ pure function shcu_flux_src (xpwp_shcu , ncol, pverx, alpha_gw_movmtn ) result(x
end do
xpwp_src(:) = alpha_gw_movmtn * xpwp_src(:)/(1.0_r8*nlayers)
-end function shcu_flux_src
+end subroutine shcu_flux_src
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!
+subroutine vorticity_flux_src (vorticity , ncol, pverx, alpha_gw_movmtn, vort_src, steering_level, launch_level )
+ integer, intent(in) :: ncol,pverx
+ real(r8), intent(in) :: vorticity (ncol,pverx)
+ real(r8), intent(in) :: alpha_gw_movmtn
+
+ real(r8), intent(out) :: vort_src(ncol)
+ integer, intent(out) :: steering_level(ncol), launch_level(ncol)
+
+ real(r8) :: scale_factor
+ integer :: k, nlayers
+
+ steering_level(:ncol) = pverx - 20
+ launch_level(:ncol) = steering_level -10
+
+ scale_factor = 1.e4 ! scales vorticity amp to u'w' in CLUBB
+ !-----------------------------------
+ ! Simple average over layers.
+ ! Probably can do better
+ !-----------------------------------
+ nlayers=10
+ vort_src(:) =0._r8
+ do k = 0, nlayers-1
+ vort_src(:) = vort_src(:) + scale_factor * abs( vorticity(:,pverx-k) )
+ end do
+ vort_src(:) = alpha_gw_movmtn * vort_src(:)/nlayers
+
+end subroutine vorticity_flux_src
end module gw_movmtn
diff --git a/src/physics/cam/gw_rdg.F90 b/src/physics/cam/gw_rdg.F90
index b5a2a2137f..4e91db565a 100644
--- a/src/physics/cam/gw_rdg.F90
+++ b/src/physics/cam/gw_rdg.F90
@@ -19,6 +19,7 @@ module gw_rdg
! Public interface
public :: gw_rdg_readnl
public :: gw_rdg_src
+public :: gw_rdg_resid_src
public :: gw_rdg_belowpeak
public :: gw_rdg_break_trap
public :: gw_rdg_do_vdiff
@@ -51,7 +52,7 @@ module gw_rdg
-! NOTE: Critical inverse Froude number Fr_c is
+! NOTE: Critical inverse Froude number Fr_c is
! 1./(SQRT(2.)~0.707 in SM2000
! (should be <= 1)
real(r8), protected :: Fr_c
@@ -92,10 +93,10 @@ subroutine gw_rdg_readnl(nlfile)
logical :: gw_rdg_do_divstream, gw_rdg_do_smooth_regimes, gw_rdg_do_adjust_tauoro, &
gw_rdg_do_backward_compat
-
+
real(r8) :: gw_rdg_C_BetaMax_DS, gw_rdg_C_GammaMax, &
gw_rdg_Frx0, gw_rdg_Frx1, gw_rdg_C_BetaMax_SM, gw_rdg_Fr_c, &
- gw_rdg_orohmin, gw_rdg_orovmin, gw_rdg_orostratmin, gw_rdg_orom2min
+ gw_rdg_orohmin, gw_rdg_orovmin, gw_rdg_orostratmin, gw_rdg_orom2min
namelist /gw_rdg_nl/ gw_rdg_do_divstream, gw_rdg_C_BetaMax_DS, gw_rdg_C_GammaMax, &
gw_rdg_Frx0, gw_rdg_Frx1, gw_rdg_C_BetaMax_SM, gw_rdg_Fr_c, &
@@ -119,7 +120,7 @@ subroutine gw_rdg_readnl(nlfile)
call freeunit(unitn)
! Set the local variables
- do_divstream = gw_rdg_do_divstream
+ do_divstream = gw_rdg_do_divstream
C_BetaMax_DS = gw_rdg_C_BetaMax_DS
C_GammaMax = gw_rdg_C_GammaMax
Frx0 = gw_rdg_Frx0
@@ -175,9 +176,213 @@ subroutine gw_rdg_readnl(nlfile)
end subroutine gw_rdg_readnl
+!==========================================================================
+subroutine gw_rdg_resid_src(ncol, band, p, &
+ u, v, t, mxdis, kwvrdg, zi, nm, &
+ src_level, tend_level, tau, ubm, ubi, xv, yv, &
+ ubmsrc, usrc, vsrc, nsrc, rsrc, m2src, c, tauoro )
+ use gw_common, only: rair, GWBand
+ use gw_utils, only: dot_2d, midpoint_interp, get_unit_vector
+ !-----------------------------------------------------------------------
+ ! Orographic source for multiple gravity wave drag parameterization.
+ !
+ ! The stress is returned for a single wave with c=0, over orography.
+ ! For points where the orographic variance is small (including ocean),
+ ! the returned stress is zero.
+ !------------------------------Arguments--------------------------------
+ ! Column dimension.
+ integer, intent(in) :: ncol
+
+ ! Band to emit orographic waves in.
+ ! Regardless, we will only ever emit into l = 0.
+ type(GWBand), intent(in) :: band
+ ! Pressure coordinates.
+ type(Coords1D), intent(in) :: p
+
+
+ ! Midpoint zonal/meridional winds. ( m s-1)
+ real(r8), intent(in) :: u(ncol,pver), v(ncol,pver)
+ ! Midpoint temperatures. (K)
+ real(r8), intent(in) :: t(ncol,pver)
+ ! Height estimate for ridge (m) [anisotropic orography].
+ real(r8), intent(in) :: mxdis(ncol)
+ ! horiz wavenumber [anisotropic orography].
+ real(r8), intent(in) :: kwvrdg(ncol)
+ ! Interface altitudes above ground (m).
+ real(r8), intent(in) :: zi(ncol,pver+1)
+ ! Midpoint Brunt-Vaisalla frequencies (s-1).
+ real(r8), intent(in) :: nm(ncol,pver)
+
+ ! Indices of top gravity wave source level and lowest level where wind
+ ! tendencies are allowed.
+ integer, intent(out) :: src_level(ncol)
+ integer, intent(out) :: tend_level(ncol)
+
+ ! Averages over source region.
+ real(r8), intent(out) :: nsrc(ncol) ! B-V frequency.
+ real(r8), intent(out) :: rsrc(ncol) ! Density.
+ real(r8), intent(out) :: usrc(ncol) ! Zonal wind.
+ real(r8), intent(out) :: vsrc(ncol) ! Meridional wind.
+ real(r8), intent(out) :: ubmsrc(ncol) ! On-obstacle wind.
+ ! normalized wavenumber
+ real(r8), intent(out) :: m2src(ncol)
+
+
+ ! Wave Reynolds stress.
+ real(r8), intent(out) :: tau(ncol,-band%ngwv:band%ngwv,pver+1)
+ ! Projection of wind at midpoints and interfaces.
+ real(r8), intent(out) :: ubm(ncol,pver), ubi(ncol,pver+1)
+ ! Unit vectors of source wind (zonal and meridional components).
+ real(r8), intent(out) :: xv(ncol), yv(ncol)
+ ! Phase speeds.
+ real(r8), intent(out) :: c(ncol,-band%ngwv:band%ngwv)
+ ! source level mom. flux
+ real(r8), intent(out) :: tauoro(ncol)
+
+ !---------------------------Local Storage-------------------------------
+ ! Column and level indices.
+ integer :: i, k
+
+ ! Surface streamline displacement height (2*sgh).
+ real(r8) :: hdsp(ncol)
+
+ ! Difference in interface pressure across source region.
+ real(r8) :: dpsrc(ncol)
+ ! Thickness of downslope wind region.
+ real(r8) :: ddw(ncol)
+ ! Thickness of linear wave region.
+ real(r8) :: dwv(ncol)
+ ! Wind speed in source region.
+ real(r8) :: wmsrc(ncol)
+
+ real(r8) :: ragl(ncol)
+ real(r8) :: Fcrit_res,sghmax
+
+!--------------------------------------------------------------------------
+! Check that ngwav is equal to zero, otherwise end the job
+!--------------------------------------------------------------------------
+ if (band%ngwv /= 0) call endrun(' gw_rdg_src :: ERROR - band%ngwv must be zero and it is not')
+
+!--------------------------------------------------------------------------
+! Average the basic state variables for the wave source over the depth of
+! the orographic standard deviation. Here we assume that the appropiate
+! values of wind, stability, etc. for determining the wave source are
+! averages over the depth of the atmosphere penterated by the typical
+! mountain.
+! Reduces to the bottom midpoint values when mxdis=0, such as over ocean.
+!--------------------------------------------------------------------------
+
+ Fcrit_res = 1.0_r8
+ hdsp = mxdis ! no longer multipied by 2
+ where(hdsp < 10._r8)
+ hdsp = 0._r8
+ end where
+
+ src_level = pver+1
+
+ tau(:,0,:) = 0.0_r8
+
+ ! Find depth of "source layer" for mountain waves
+ ! i.e., between ground and mountain top
+ do k = pver, 1, -1
+ do i = 1, ncol
+ ! Need to have h >= z(k+1) here or code will bomb when h=0.
+ if ( (hdsp(i) >= zi(i,k+1)) .and. (hdsp(i) < zi(i,k)) ) then
+ src_level(i) = k
+ end if
+ end do
+ end do
+
+ rsrc = 0._r8
+ usrc = 0._r8
+ vsrc = 0._r8
+ nsrc = 0._r8
+ do i = 1, ncol
+ do k = pver, src_level(i), -1
+ rsrc(i) = rsrc(i) + p%mid(i,k) / (rair*t(i,k))* p%del(i,k)
+ usrc(i) = usrc(i) + u(i,k) * p%del(i,k)
+ vsrc(i) = vsrc(i) + v(i,k) * p%del(i,k)
+ nsrc(i) = nsrc(i) + nm(i,k)* p%del(i,k)
+ end do
+ end do
+
+
+ do i = 1, ncol
+ dpsrc(i) = p%ifc(i,pver+1) - p%ifc(i,src_level(i))
+ end do
+
+ rsrc = rsrc / dpsrc
+ usrc = usrc / dpsrc
+ vsrc = vsrc / dpsrc
+ nsrc = nsrc / dpsrc
+
+ ! Get the unit vector components and magnitude at the surface.
+ call get_unit_vector(usrc, vsrc, xv, yv, wmsrc )
+
+ ubmsrc = wmsrc
+
+ ! Project the local wind at midpoints onto the source wind.
+ do k = 1, pver
+ ubm(:,k) = dot_2d(u(:,k), v(:,k), xv, yv)
+ end do
+
+ ! Compute the interface wind projection by averaging the midpoint winds.
+ ! Use the top level wind at the top interface.
+ ubi(:,1) = ubm(:,1)
+
+ ubi(:,2:pver) = midpoint_interp(ubm)
+
+ ! The minimum stratification allowing GW behavior
+ ! should really depend on horizontal scale since
+ !
+ ! m^2 ~ (N/U)^2 - k^2
+ !
+
+ m2src = ( (nsrc/(ubmsrc+0.01_r8))**2 - kwvrdg**2 ) /((nsrc/(ubmsrc+0.01_r8))**2)
+
+ ! Compute the interface wind projection by averaging the midpoint winds.
+ ! Use the top level wind at the top interface.
+ ubi(:,1) = ubm(:,1)
+ ubi(:,2:pver) = midpoint_interp(ubm)
+ ubi(:,pver+1) = ubm(:,pver)
+
+
+
+ ! Determine the orographic c=0 source term following McFarlane (1987).
+ ! (DOI: https://doi.org/10.1175/1520-0469(1987)044<1775:TEOOEG>2.0.CO;2)
+ ! Set the source top interface index to pver, if the orographic term is
+ ! zero.
+ do i = 1, ncol
+ if ( ( src_level(i) > 0 ) .and. ( m2src(i) > orom2min ) ) then
+ sghmax = Fcrit_res * (ubmsrc(i) / nsrc(i))**2
+ tauoro(i) = 0.5_r8 * kwvrdg(i) * min(hdsp(i)**2, sghmax) * &
+ rsrc(i) * nsrc(i) * ubmsrc(i)
+ else
+ tauoro(i) = 0._r8
+ end if
+ end do
+
+ do i = 1, ncol
+ do k=src_level(i),pver+1
+ tau(i,0,k) = tauoro(i)
+ end do
+ end do
+
+
+ ! Allow wind tendencies all the way to the model bottom.
+ tend_level = pver
+
+ ! No spectrum; phase speed is just 0.
+ c = 0._r8
+
+end subroutine gw_rdg_resid_src
+
+
+!==========================================================================
+
subroutine gw_rdg_src(ncol, band, p, &
u, v, t, mxdis, angxy, anixy, kwvrdg, iso, zi, nm, &
- src_level, tend_level, bwv_level ,tlb_level , tau, ubm, ubi, xv, yv, &
+ src_level, tend_level, bwv_level ,tlb_level , tau, ubm, ubi, xv, yv, &
ubmsrc, usrc, vsrc, nsrc, rsrc, m2src, tlb, bwv, Fr1, Fr2, Frx, c)
use gw_common, only: rair, GWBand
use gw_utils, only: dot_2d, midpoint_interp
@@ -264,8 +469,8 @@ subroutine gw_rdg_src(ncol, band, p, &
! Wind speed in source region.
real(r8) :: wmsrc(ncol)
- real(r8) :: ragl(ncol)
-
+ real(r8) :: ragl(ncol)
+
!--------------------------------------------------------------------------
! Check that ngwav is equal to zero, otherwise end the job
!--------------------------------------------------------------------------
@@ -293,13 +498,13 @@ subroutine gw_rdg_src(ncol, band, p, &
do i = 1, ncol
! Need to have h >= z(k+1) here or code will bomb when h=0.
if ( (hdsp(i) >= zi(i,k+1)) .and. (hdsp(i) < zi(i,k)) ) then
- src_level(i) = k
+ src_level(i) = k
end if
end do
end do
rsrc = 0._r8
- usrc = 0._r8
+ usrc = 0._r8
vsrc = 0._r8
nsrc = 0._r8
do i = 1, ncol
@@ -329,7 +534,7 @@ subroutine gw_rdg_src(ncol, band, p, &
ragl = angxy * pii/180._r8
- ! protect from wierd "bad" angles
+ ! protect from wierd "bad" angles
! that may occur if hdsp is zero
where( hdsp <= orohmin )
ragl = 0._r8
@@ -341,7 +546,7 @@ subroutine gw_rdg_src(ncol, band, p, &
! Kluge in possible "isotropic" obstacle.
where( ( iso == 1 ) .and. (wmsrc > orovmin) )
- xv = usrc/wmsrc
+ xv = usrc/wmsrc
yv = vsrc/wmsrc
end where
@@ -357,7 +562,7 @@ subroutine gw_rdg_src(ncol, band, p, &
ubm(:,k) = sign( ubmsrc*0._r8+1._r8 , ubmsrc ) * ubm(:,k)
end do
- ! Sean says just use 1._r8 as
+ ! Sean says just use 1._r8 as
! first argument
xv = sign( ubmsrc*0._r8+1._r8 , ubmsrc ) * xv
yv = sign( ubmsrc*0._r8+1._r8 , ubmsrc ) * yv
@@ -366,7 +571,7 @@ subroutine gw_rdg_src(ncol, band, p, &
! against zero
ubmsrc = abs(ubmsrc)
ubmsrc = max( 0.01_r8 , ubmsrc )
-
+
! The minimum stratification allowing GW behavior
! should really depend on horizontal scale since
@@ -374,9 +579,9 @@ subroutine gw_rdg_src(ncol, band, p, &
! m^2 ~ (N/U)^2 - k^2
!
! Should also think about parameterizing
- ! trapped lee-waves.
+ ! trapped lee-waves.
+
-
! This needs to be made constistent with later
! treatment of nonhydrostatic effects.
m2src = ( (nsrc/(ubmsrc+0.01_r8))**2 - kwvrdg**2 ) /((nsrc/(ubmsrc+0.01_r8))**2)
@@ -387,9 +592,9 @@ subroutine gw_rdg_src(ncol, band, p, &
! will modified later if wave breaking or trapping are
! diagnosed
!
- ! ^
+ ! ^
! | *** linear propagation ***
- ! (H) -------- mountain top ------------- | *** or wave breaking ****
+ ! (H) -------- mountain top ------------- | *** or wave breaking ****
! | *** regimes *************
! (BWV)------ bottom of linear waves ---- |
! : |
@@ -397,7 +602,7 @@ subroutine gw_rdg_src(ncol, band, p, &
! : |
! (TLB)--- top of flow diversion layer--- '
! :
- ! **** flow diversion *****
+ ! **** flow diversion *****
! :
!============================================
@@ -406,17 +611,17 @@ subroutine gw_rdg_src(ncol, band, p, &
!--------------------------------------------
! High-drag downslope wind regime exists
! between bottom of linear waves and top of
- ! flow diversion. Linear waves can only
+ ! flow diversion. Linear waves can only
! attain vertical displacment of f1*U/N. So,
! bottom of linear waves is given by
!
- ! BWV = H - Fr1*U/N
+ ! BWV = H - Fr1*U/N
!
- ! Downslope wind layer begins at BWV and
+ ! Downslope wind layer begins at BWV and
! extends below it until some maximum high
! drag obstacle height Fr2*U/N is attained
! (where Fr2 >= f1). Below downslope wind
- ! there is flow diversion, so top of
+ ! there is flow diversion, so top of
! diversion layer (TLB) is equivalent to
! bottom of downslope wind layer and is;
!
@@ -431,27 +636,27 @@ subroutine gw_rdg_src(ncol, band, p, &
if ( do_divstream ) then
!------------------------------------------------
- ! Calculate Fr2(Frx) for DS2017
+ ! Calculate Fr2(Frx) for DS2017
!------------------------------------------------
where(Frx <= Frx0)
Fr2(:) = Fr1(:) + Fr1(:)* C_GammaMax * anixy(:)
elsewhere((Frx > Frx0).and.(Frx <= Frx1) )
Fr2(:) = Fr1(:) + Fr1(:)* C_GammaMax * anixy(:) &
- * (Frx1 - Frx(:))/(Frx1-Frx0)
- elsewhere(Frx > Frx1)
+ * (Frx1 - Frx(:))/(Frx1-Frx0)
+ elsewhere(Frx > Frx1)
Fr2(:)=Fr1(:)
endwhere
else
- !------------------------------------------
+ !------------------------------------------
! Regime distinctions entirely carried by
! amplification of taudsw (next subr)
!------------------------------------------
Fr2(:)=Fr1(:)
- end if
+ end if
-
- where( m2src > orom2min )
+
+ where( m2src > orom2min )
ddw = Fr2 * ( abs(ubmsrc) )/nsrc
elsewhere
ddw = 0._r8
@@ -475,7 +680,7 @@ subroutine gw_rdg_src(ncol, band, p, &
! Find *BOTTOM* of linear wave layer (BWV)
!where ( nsrc > orostratmin )
- where( m2src > orom2min )
+ where( m2src > orom2min )
dwv = Fr1 * ( abs(ubmsrc) )/nsrc
elsewhere
dwv = -9.999e9_r8 ! if weak strat - no waves
@@ -507,7 +712,7 @@ subroutine gw_rdg_src(ncol, band, p, &
! No spectrum; phase speed is just 0.
c = 0._r8
- where( m2src < orom2min )
+ where( m2src < orom2min )
tlb = mxdis
tlb_level = src_level
endwhere
@@ -520,8 +725,8 @@ end subroutine gw_rdg_src
subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
t, mxdis, anixy, kwvrdg, zi, nm, ni, rhoi, &
- src_level , tau, &
- ubmsrc, nsrc, rsrc, m2src,tlb,bwv,Fr1,Fr2,Frx, &
+ src_level , tau, &
+ ubmsrc, nsrc, rsrc, m2src,tlb,bwv,Fr1,Fr2,Frx, &
tauoro,taudsw, hdspwv,hdspdw )
use gw_common, only: GWBand
@@ -604,16 +809,16 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
end do
do i = 1, ncol
- if ( m2src(i) > orom2min ) then
+ if ( m2src(i) > orom2min ) then
hdspwv(i) = min( mxdis(i) , Fr1(i) * ubsrcx(i) / nsrc(i) )
else
hdspwv(i) = 0._r8
end if
end do
-
+
if (do_divstream) then
do i = 1, ncol
- if ( m2src(i) > orom2min ) then
+ if ( m2src(i) > orom2min ) then
hdspdw(i) = min( mxdis(i) , Fr2(i) * ubsrcx(i) / nsrc(i) )
else
hdspdw(i) = 0._r8
@@ -622,8 +827,8 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
else
do i = 1, ncol
! Needed only to mark where a DSW occurs
- if ( m2src(i) > orom2min ) then
- hdspdw(i) = mxdis(i)
+ if ( m2src(i) > orom2min ) then
+ hdspdw(i) = mxdis(i)
else
hdspdw(i) = 0._r8
end if
@@ -637,14 +842,14 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
! Determine the orographic c=0 source term following McFarlane (1987).
! Set the source top interface index to pver, if the orographic term is
! zero.
- !
+ !
! This formula is basically from
!
! tau(src) = rho * u' * w'
- ! where
+ ! where
! u' ~ N*h' and w' ~ U*h'/b (b="breite")
!
- ! and 1/b has been replaced with k (kwvrdg)
+ ! and 1/b has been replaced with k (kwvrdg)
!
do i = 1, ncol
if ( ( src_level(i) > 0 ) .and. ( m2src(i) > orom2min ) ) then
@@ -680,7 +885,7 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
! Amplify DSW between Frx=1. and Frx=Frx1
do i = 1,ncol
dswamp=0._r8
- BetaMax = C_BetaMax_DS * anixy(i)
+ BetaMax = C_BetaMax_DS * anixy(i)
if ( (Frx(i)>1._r8).and.(Frx(i)<=Frx1)) then
dswamp = (Frx(i)-1._r8)*(Frx1-Frx(i))/(0.25_r8*(Frx1-1._r8)**2)
end if
@@ -691,30 +896,30 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
! Scinocca&McFarlane
!--------------------
do i = 1, ncol
- BetaMax = C_BetaMax_SM * anixy(i)
+ BetaMax = C_BetaMax_SM * anixy(i)
if ( (Frx(i) >=1._r8) .and. (Frx(i) < 1.5_r8) ) then
dswamp = 2._r8 * BetaMax * (Frx(i) -1._r8)
else if ( ( Frx(i) >= 1.5_r8 ) .and. (Frx(i) < 3._r8 ) ) then
- dswamp = ( 1._r8 + BetaMax - (0.666_r8**2) ) * ( 0.666_r8*(3._r8 - Frx(i) ))**2 &
+ dswamp = ( 1._r8 + BetaMax - (0.666_r8**2) ) * ( 0.666_r8*(3._r8 - Frx(i) ))**2 &
+ ( 1._r8 / Frx(i) )**2 -1._r8
else
- dswamp = 0._r8
+ dswamp = 0._r8
end if
if ( (Frx(i) >=1._r8) .and. (Frx(i) < 3._r8) ) then
taudsw(i) = (1._r8 + dswamp )*taulin(i) - tauoro(i)
else
- taudsw(i) = 0._r8
+ taudsw(i) = 0._r8
endif
! This code defines "taudsw" as SUM of freely-propagating
! DSW enhancement. Different than in SM2000
- taudsw(i) = taudsw(i) + tauoro(i)
+ taudsw(i) = taudsw(i) + tauoro(i)
end do
!----------------------------------------------------
end if
-
+
do i = 1, ncol
- if ( m2src(i) > orom2min ) then
+ if ( m2src(i) > orom2min ) then
where ( ( zi(i,:) < mxdis(i) ) .and. ( zi(i,:) >= bwv(i) ) )
tau(i,0,:) = tauoro(i)
else where ( ( zi(i,:) < bwv(i) ) .and. ( zi(i,:) >= tlb(i) ) )
@@ -728,7 +933,7 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
tau(i,0,:) = taudsw(i) + &
Coeff_LB(i) * kwvrdg(i) * rsrc(i) * 0.5_r8 * (ubsrcx(i)**2) * ( tlb(i) - zi(i,:) )
endwhere
-
+
if (do_smooth_regimes) then
! This blocks accounts for case where both mxdis and tlb fall
! between adjacent edges
@@ -739,7 +944,7 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
tau(i,0,k) = tauoro(i)
end if
end do
- end if
+ end if
else !----------------------------------------------
! This block allows low-level dynamics to occur
@@ -758,11 +963,11 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
k=src_level(i)
if ( ni(i,k) > orostratmin ) then
tausat = (Fr_c**2) * kwvrdg(i) * rhoi(i,k) * ubsrcx(i)**3 / &
- (1._r8*ni(i,k))
+ (1._r8*ni(i,k))
else
tausat = 0._r8
- endif
- tau(i,0,src_level(i)) = min( tauoro(i), tausat )
+ endif
+ tau(i,0,src_level(i)) = min( tauoro(i), tausat )
end do
@@ -770,18 +975,18 @@ subroutine gw_rdg_belowpeak(ncol, band, rdg_cd_llb, &
! Final clean-up. Do nothing if obstacle less than orohmin
do i = 1, ncol
if ( mxdis(i) < orohmin ) then
- tau(i,0,:) = 0._r8
+ tau(i,0,:) = 0._r8
tauoro(i) = 0._r8
taudsw(i) = 0._r8
- endif
+ endif
end do
- ! Disable vertical propagation if Scorer param is
+ ! Disable vertical propagation if Scorer param is
! too small.
do i = 1, ncol
if ( m2src(i) <= orom2min ) then
src_level(i)=1
- endif
+ endif
end do
@@ -790,10 +995,10 @@ end subroutine gw_rdg_belowpeak
!==========================================================================
subroutine gw_rdg_break_trap(ncol, band, &
- zi, nm, ni, ubm, ubi, rhoi, kwvrdg, bwv, tlb, wbr, &
- src_level, tlb_level, &
+ zi, nm, ni, ubm, ubi, rhoi, kwvrdg, bwv, tlb, wbr, &
+ src_level, tlb_level, &
hdspwv, hdspdw, mxdis, &
- tauoro, taudsw, tau, &
+ tauoro, taudsw, tau, &
ldo_trapped_waves, wdth_kwv_scale_in )
use gw_common, only: GWBand
!-----------------------------------------------------------------------
@@ -893,7 +1098,7 @@ subroutine gw_rdg_break_trap(ncol, band, &
endwhere
end do
- ! Take square root of m**2 and
+ ! Take square root of m**2 and
! do vertical integral to find
! WKB phase.
!-----------------------------
@@ -901,8 +1106,8 @@ subroutine gw_rdg_break_trap(ncol, band, &
phswkb(:,:)=0
do k=pver,1,-1
where( zi(:,k) > tlb(:) )
- delz(:) = min( zi(:,k)-zi(:,k+1) , zi(:,k)-tlb(:) )
- phswkb(:,k) = phswkb(:,k+1) + m2(:,k)*delz(:)
+ delz(:) = min( zi(:,k)-zi(:,k+1) , zi(:,k)-tlb(:) )
+ phswkb(:,k) = phswkb(:,k+1) + m2(:,k)*delz(:)
endwhere
end do
@@ -913,9 +1118,9 @@ subroutine gw_rdg_break_trap(ncol, band, &
wbrx(:)=0._r8
if (do_smooth_regimes) then
do k=pver,1,-1
- where( (phswkb(:,k+1)<1.5_r8*pii).and.(phswkb(:,k)>=1.5_r8*pii) &
+ where( (phswkb(:,k+1)<1.5_r8*pii).and.(phswkb(:,k)>=1.5_r8*pii) &
.and.(hdspdw(:)>hdspwv(:)) )
- wbr(:) = zi(:,k)
+ wbr(:) = zi(:,k)
! Extrapolation to make regime
! transitions smoother
wbrx(:) = zi(:,k) - ( phswkb(:,k) - 1.5_r8*pii ) &
@@ -925,7 +1130,7 @@ subroutine gw_rdg_break_trap(ncol, band, &
end do
else
do k=pver,1,-1
- where( (phswkb(:,k+1)<1.5_r8*pii).and.(phswkb(:,k)>=1.5_r8*pii) &
+ where( (phswkb(:,k+1)<1.5_r8*pii).and.(phswkb(:,k)>=1.5_r8*pii) &
.and.(hdspdw(:)>hdspwv(:)) )
wbr(:) = zi(:,k)
src_level(:) = k
@@ -936,12 +1141,12 @@ subroutine gw_rdg_break_trap(ncol, band, &
! Adjust tauoro at new source levels if needed.
! This is problematic if Fr_c<1.0. Not sure why.
!----------------------------------------------------------
- if (do_adjust_tauoro) then
+ if (do_adjust_tauoro) then
do i = 1,ncol
if (wbr(i) > 0._r8 ) then
- tausat(i) = (Fr_c**2) * kwvrdg(i) * rhoi( i, src_level(i) ) &
+ tausat(i) = (Fr_c**2) * kwvrdg(i) * rhoi( i, src_level(i) ) &
* abs(ubi(i , src_level(i) ))**3 &
- / ni( i , src_level(i) )
+ / ni( i , src_level(i) )
tauoro(i) = min( tauoro(i), tausat(i) )
end if
end do
@@ -954,9 +1159,9 @@ subroutine gw_rdg_break_trap(ncol, band, &
tau(i,0,k) = tauoro(i) + (taudsw(i)-tauoro(i)) * &
( wbrx(i) - zi(i,k) ) / &
( wbrx(i) - tlb(i) )
- tau(i,0,k) = max( tau(i,0,k), tauoro(i) )
+ tau(i,0,k) = max( tau(i,0,k), tauoro(i) )
endif
- end do
+ end do
end do
else
! Following is for backwards B4B compatibility with earlier versions
@@ -969,7 +1174,7 @@ subroutine gw_rdg_break_trap(ncol, band, &
( wbr(i) - zi(i,k) ) / &
( wbr(i) - tlb(i) )
endif
- end do
+ end do
end do
else
do i = 1, ncol
@@ -979,13 +1184,13 @@ subroutine gw_rdg_break_trap(ncol, band, &
( wbr(i) - zi(i,k) ) / &
( wbr(i) - tlb(i) )
endif
- end do
+ end do
end do
end if
end if
-
- if (lldo_trapped_waves) then
-
+
+ if (lldo_trapped_waves) then
+
! Identify top edge of layer in which Scorer param drops below 0
! - approximately the "turning level"
!----------------------------------------------------------
diff --git a/src/physics/cam/macrop_driver.F90 b/src/physics/cam/macrop_driver.F90
index d381387bfc..26217c2a8c 100644
--- a/src/physics/cam/macrop_driver.F90
+++ b/src/physics/cam/macrop_driver.F90
@@ -87,7 +87,6 @@ module macrop_driver
integer :: &
dlfzm_idx = -1, & ! ZM detrained convective cloud water mixing ratio.
- difzm_idx = -1, & ! ZM detrained convective cloud ice mixing ratio.
dnlfzm_idx = -1, & ! ZM detrained convective cloud water num concen.
dnifzm_idx = -1 ! ZM detrained convective cloud ice num concen.
@@ -395,7 +394,8 @@ subroutine macrop_driver_tend( &
! !
!-------------------------------------------------------- !
- use cloud_fraction, only: cldfrc, cldfrc_fice
+ use cloud_fraction, only: cldfrc
+ use cloud_fraction_fice, only: cloud_fraction_fice_run
use physics_types, only: physics_state, physics_ptend
use physics_types, only: physics_ptend_init, physics_update
use physics_types, only: physics_ptend_sum, physics_state_copy
@@ -486,7 +486,6 @@ subroutine macrop_driver_tend( &
! ZM microphysics
real(r8), pointer :: dlfzm(:,:) ! ZM detrained convective cloud water mixing ratio.
- real(r8), pointer :: difzm(:,:) ! ZM detrained convective cloud ice mixing ratio.
real(r8), pointer :: dnlfzm(:,:) ! ZM detrained convective cloud water num concen.
real(r8), pointer :: dnifzm(:,:) ! ZM detrained convective cloud ice num concen.
@@ -872,8 +871,8 @@ subroutine macrop_driver_tend( &
fice(:,:) = 0._r8
fsnow(:,:) = 0._r8
!REMOVECAM_END
- call cldfrc_fice( ncol, state_loc%t(:ncol,:), fice(:ncol,:), fsnow(:ncol,:) )
+ call cloud_fraction_fice_run(ncol, state_loc%t(:ncol,:), tmelt, top_lev, pver, fice(:ncol,:), fsnow(:ncol,:))
lq(:) = .FALSE.
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/nucleate_ice_cam.F90 b/src/physics/cam/nucleate_ice_cam.F90
index bd0a8b6636..3edd3f616a 100644
--- a/src/physics/cam/nucleate_ice_cam.F90
+++ b/src/physics/cam/nucleate_ice_cam.F90
@@ -69,7 +69,7 @@ module nucleate_ice_cam
naai_hom_idx = -1
integer :: &
- ast_idx = -1
+ aist_idx = -1
integer :: &
qsatfac_idx = -1
@@ -360,7 +360,7 @@ subroutine nucleate_ice_cam_init(mincld_in, bulk_scale_in, pbuf2d, aero_props)
mincld)
! get indices for fields in the physics buffer
- ast_idx = pbuf_get_index('AST')
+ aist_idx = pbuf_get_index('AIST')
end subroutine nucleate_ice_cam_init
@@ -400,8 +400,7 @@ subroutine nucleate_ice_cam_calc( &
real(r8), pointer :: pmid(:,:) ! pressure at layer midpoints (pa)
real(r8), pointer :: aer_mmr(:,:) ! aerosol mass mixing ratio
-
- real(r8), pointer :: ast(:,:)
+ real(r8), pointer :: aist(:,:)
real(r8) :: icecldf(pcols,pver) ! ice cloud fraction
real(r8), pointer :: qsatfac(:,:) ! Subgrid cloud water saturation scaling factor.
@@ -509,9 +508,8 @@ subroutine nucleate_ice_cam_calc( &
end if
itim_old = pbuf_old_tim_idx()
- call pbuf_get_field(pbuf, ast_idx, ast, start=(/1,1,itim_old/), kount=(/pcols,pver,1/))
-
- icecldf(:ncol,:pver) = ast(:ncol,:pver)
+ call pbuf_get_field(pbuf, aist_idx, aist, start=(/1,1,itim_old/), kount=(/pcols,pver,1/))
+ icecldf(:ncol,:pver) = aist(:ncol,:pver)
! naai and naai_hom are the outputs from this parameterization
call pbuf_get_field(pbuf, naai_idx, naai)
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 3a39635457..0e83ad2707 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
@@ -731,6 +727,7 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
use co2_cycle, only: co2_init, co2_transport
use convect_deep, only: convect_deep_init
use convect_shallow, only: convect_shallow_init
+ use constituents, only: cnst_get_ind
use cam_diagnostics, only: diag_init
use gw_drag, only: gw_init
use radheat, only: radheat_init
@@ -742,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
@@ -787,7 +783,7 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
! local variables
integer :: lchnk
- integer :: ierr
+ integer :: ierr, ixq
logical :: history_budget ! output tendencies and state variables for
! temperature, water vapor, cloud
@@ -910,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()
@@ -966,7 +957,8 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
! Initialize CAM CCPP constituent properties array
! for use in CCPP-ized physics schemes:
- call ccpp_const_props_init()
+ call cnst_get_ind('Q', ixq)
+ call ccpp_const_props_init(ixq)
! Initialize qneg3 and qneg4
call qneg_init()
@@ -1067,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
@@ -1094,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')
@@ -1148,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
!
@@ -1161,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/rk_stratiform.F90 b/src/physics/cam/rk_stratiform.F90
index 002300dbfd..148ea3fd28 100644
--- a/src/physics/cam/rk_stratiform.F90
+++ b/src/physics/cam/rk_stratiform.F90
@@ -427,7 +427,8 @@ subroutine rk_stratiform_tend( &
! !
!-------------------------------------------------------- !
- use cloud_fraction, only: cldfrc, cldfrc_fice
+ use cloud_fraction, only: cldfrc
+ use cloud_fraction_fice, only: cloud_fraction_fice_run
use physics_types, only: physics_state, physics_ptend
use physics_types, only: physics_ptend_init, physics_update
use physics_types, only: physics_ptend_sum, physics_state_copy
@@ -440,7 +441,7 @@ subroutine rk_stratiform_tend( &
use phys_control, only: cam_physpkg_is
use tropopause, only: tropopause_find_cam
use phys_grid, only: get_rlat_all_p
- use physconst, only: pi
+ use physconst, only: pi, tmelt
! Arguments
type(physics_state), intent(in) :: state ! State variables
@@ -577,6 +578,9 @@ subroutine rk_stratiform_tend( &
real(r8) :: dlat(pcols)
real(r8), parameter :: rad2deg = 180._r8/pi
+ integer :: top_lev
+
+
! ======================================================================
lchnk = state%lchnk
@@ -812,7 +816,9 @@ subroutine rk_stratiform_tend( &
fice(:,:) = 0._r8
fsnow(:,:) = 0._r8
!REMOVECAM_END
- call cldfrc_fice(ncol, state1%t(1:ncol,:), fice(1:ncol,:), fsnow(1:ncol,:))
+ top_lev = 1
+ call cloud_fraction_fice_run(ncol, state1%t(:ncol,:), tmelt, top_lev, pver, fice(:ncol,:), fsnow(:ncol,:))
+
! Perform repartitioning of stratiform condensate.
! Corresponding heating tendency will be added later.
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 e2d571a4de..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)
@@ -709,7 +709,6 @@ subroutine vertical_diffusion_tend( &
!---------------------------------------------------- !
use physics_buffer, only : physics_buffer_desc, pbuf_get_field, pbuf_set_field
use physics_types, only : physics_state, physics_ptend, physics_ptend_init
- use physics_types, only : set_dry_to_wet, set_wet_to_dry
use camsrfexch, only : cam_in_t
use cam_history, only : outfld
@@ -904,9 +903,6 @@ subroutine vertical_diffusion_tend( &
! Main Computation Begins !
! ----------------------- !
- ! Assume 'wet' mixing ratios in diffusion code.
- call set_dry_to_wet(state, convert_cnst_type='dry')
-
rztodt = 1._r8 / ztodt
lchnk = state%lchnk
ncol = state%ncol
@@ -1014,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, &
@@ -1030,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.
@@ -1375,8 +1371,6 @@ subroutine vertical_diffusion_tend( &
ptend%q(:ncol,:pver,m) = ptend%q(:ncol,:pver,m)*state%pdel(:ncol,:pver)/state%pdeldry(:ncol,:pver)
endif
end do
- ! convert wet mmr back to dry before conservation check
- call set_wet_to_dry(state, convert_cnst_type='dry')
if (.not. do_pbl_diags) then
slten(:ncol,:) = ( sl(:ncol,:) - sl_prePBL(:ncol,:) ) * rztodt
@@ -1395,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/cam/wv_sat_methods.F90 b/src/physics/cam/wv_sat_methods.F90
deleted file mode 100644
index bb2ffeb45b..0000000000
--- a/src/physics/cam/wv_sat_methods.F90
+++ /dev/null
@@ -1,759 +0,0 @@
-module wv_sat_methods
-
-! This portable module contains all CAM methods for estimating
-! the saturation vapor pressure of water.
-!
-! wv_saturation provides CAM-specific interfaces and utilities
-! based on these formulae.
-!
-! Typical usage of this module:
-!
-! Init:
-! call wv_sat_methods_init(r8, , errstring)
-!
-! Get scheme index from a name string:
-! scheme_idx = wv_sat_get_scheme_idx(scheme_name)
-! if (.not. wv_sat_valid_idx(scheme_idx))
-!
-! Get pressures:
-! es = wv_sat_svp_water(t, scheme_idx)
-! es = wv_sat_svp_ice(t, scheme_idx)
-!
-! Use ice/water transition range:
-! es = wv_sat_svp_trice(t, ttrice, scheme_idx)
-!
-! Note that elemental functions cannot be pointed to, nor passed
-! as arguments. If you need to do either, it is recommended to
-! wrap the function so that it can be given an explicit (non-
-! elemental) interface.
-
-implicit none
-private
-save
-
-integer, parameter :: r8 = selected_real_kind(12) ! 8 byte real
-
-integer, parameter :: VLENS = 128 ! vector length for a GPU kernel
-
-real(r8) :: tmelt ! Melting point of water at 1 atm (K)
-real(r8) :: h2otrip ! Triple point temperature of water (K)
-real(r8) :: tboil ! Boiling point of water at 1 atm (K)
-
-real(r8) :: ttrice ! Ice-water transition range
-
-real(r8) :: epsilo ! Ice-water transition range
-real(r8) :: omeps ! 1._r8 - epsilo
-
-! Indices representing individual schemes
-integer, parameter :: Invalid_idx = -1
-integer, parameter :: GoffGratch_idx = 1
-integer, parameter :: MurphyKoop_idx = 2
-integer, parameter :: Bolton_idx = 3
-
-! Index representing the current default scheme.
-integer, parameter :: initial_default_idx = GoffGratch_idx
-integer :: default_idx = initial_default_idx
-
-!$acc declare create (epsilo, tmelt, tboil, omeps, h2otrip, ttrice)
-
-public wv_sat_methods_init
-public wv_sat_get_scheme_idx
-public wv_sat_valid_idx
-
-public wv_sat_set_default
-public wv_sat_reset_default
-
-public wv_sat_qsat_water, wv_sat_qsat_water_vect
-public wv_sat_qsat_ice, wv_sat_qsat_ice_vect
-
-public wv_sat_svp_trans, wv_sat_svp_trans_vect
-
-! pressure -> humidity conversion
-public wv_sat_svp_to_qsat, wv_sat_svp_to_qsat_vect
-
-! Combined qsat operations
-public wv_sat_qsat_trans
-
-public wv_sat_svp_water, wv_sat_svp_water_vect
-public wv_sat_svp_ice, wv_sat_svp_ice_vect
-
-contains
-
-!---------------------------------------------------------------------
-! ADMINISTRATIVE FUNCTIONS
-!---------------------------------------------------------------------
-
-! Get physical constants
-subroutine wv_sat_methods_init(kind, tmelt_in, h2otrip_in, tboil_in, &
- ttrice_in, epsilo_in, errstring)
- integer, intent(in) :: kind
- real(r8), intent(in) :: tmelt_in
- real(r8), intent(in) :: h2otrip_in
- real(r8), intent(in) :: tboil_in
- real(r8), intent(in) :: ttrice_in
- real(r8), intent(in) :: epsilo_in
- character(len=*), intent(out) :: errstring
-
- errstring = ' '
-
- if (kind /= r8) then
- write(errstring,*) 'wv_sat_methods_init: ERROR: ', &
- kind,' was input kind but ',r8,' is internal kind.'
- return
- end if
-
- if (ttrice_in < 0._r8) then
- write(errstring,*) 'wv_sat_methods_init: ERROR: ', &
- ttrice_in,' was input for ttrice, but negative range is invalid.'
- return
- end if
-
- tmelt = tmelt_in
- h2otrip = h2otrip_in
- tboil = tboil_in
- ttrice = ttrice_in
- epsilo = epsilo_in
-
- omeps = 1._r8 - epsilo
-
- !$acc update device (tmelt,h2otrip,tboil,ttrice,epsilo,omeps)
-
-end subroutine wv_sat_methods_init
-
-! Look up index by name.
-pure function wv_sat_get_scheme_idx(name) result(idx)
- character(len=*), intent(in) :: name
- integer :: idx
-
- select case (name)
- case("GoffGratch")
- idx = GoffGratch_idx
- case("MurphyKoop")
- idx = MurphyKoop_idx
- case("Bolton")
- idx = Bolton_idx
- case default
- idx = Invalid_idx
- end select
-
-end function wv_sat_get_scheme_idx
-
-! Check validity of an index from the above routine.
-pure function wv_sat_valid_idx(idx) result(status)
- integer, intent(in) :: idx
- logical :: status
-
- status = (idx /= Invalid_idx)
-
-end function wv_sat_valid_idx
-
-! Set default scheme (otherwise, Goff & Gratch is default)
-! Returns a logical representing success (.true.) or
-! failure (.false.).
-function wv_sat_set_default(name) result(status)
- character(len=*), intent(in) :: name
- logical :: status
-
- ! Don't want to overwrite valid default with invalid,
- ! so assign to temporary and check it first.
- integer :: tmp_idx
-
- tmp_idx = wv_sat_get_scheme_idx(name)
-
- status = wv_sat_valid_idx(tmp_idx)
-
- if (status) default_idx = tmp_idx
-
-end function wv_sat_set_default
-
-! Reset default scheme to initial value.
-! The same thing can be accomplished with wv_sat_set_default;
-! the real reason to provide this routine is to reset the
-! module for testing purposes.
-subroutine wv_sat_reset_default()
-
- default_idx = initial_default_idx
-
-end subroutine wv_sat_reset_default
-
-!---------------------------------------------------------------------
-! UTILITIES
-!---------------------------------------------------------------------
-
-! Get saturation specific humidity given pressure and SVP.
-! Specific humidity is limited to range 0-1.
-function wv_sat_svp_to_qsat(es, p) result(qs)
- real(r8), intent(in) :: es ! SVP
- real(r8), intent(in) :: p ! Current pressure.
- real(r8) :: qs
-
- ! If pressure is less than SVP, set qs to maximum of 1.
- if ( (p - es) <= 0._r8 ) then
- qs = 1.0_r8
- else
- qs = epsilo*es / (p - omeps*es)
- end if
-
-end function wv_sat_svp_to_qsat
-
-! Get saturation specific humidity given pressure and SVP.
-! Specific humidity is limited to range 0-1.
-subroutine wv_sat_svp_to_qsat_vect(es, p, qs, vlen)
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: es(vlen) ! SVP
- real(r8), intent(in) :: p(vlen) ! Current pressure.
- real(r8), intent(out) :: qs(vlen)
- integer :: i
-
- ! If pressure is less than SVP, set qs to maximum of 1.
-
- !$acc data present (es,p,qs)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i=1,vlen
- if ( (p(i) - es(i)) <= 0._r8 ) then
- qs(i) = 1.0_r8
- else
- qs(i) = epsilo*es(i) / (p(i) - omeps*es(i))
- end if
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine wv_sat_svp_to_qsat_vect
-
-subroutine wv_sat_qsat_water(t, p, es, qs, idx)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over water at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- integer, intent(in), optional :: idx ! Scheme index
-
- es = wv_sat_svp_water(t, idx)
-
- qs = wv_sat_svp_to_qsat(es, p)
-
- ! Ensures returned es is consistent with limiters on qs.
- es = min(es, p)
-
-end subroutine wv_sat_qsat_water
-
-subroutine wv_sat_qsat_water_vect(t, p, es, qs, vlen, idx)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over water at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- !------------------------------------------------------------------!
- ! Inputs
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature
- real(r8), intent(in) :: p(vlen) ! Pressure
- ! Outputs
- real(r8), intent(out) :: es(vlen) ! Saturation vapor pressure
- real(r8), intent(out) :: qs(vlen) ! Saturation specific humidity
-
- integer, intent(in), optional :: idx ! Scheme index
- integer :: i
-
- !$acc data present (t,p,es,qs)
-
- call wv_sat_svp_water_vect(t, es, vlen, idx)
- call wv_sat_svp_to_qsat_vect(es, p, qs, vlen)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i=1,vlen
- ! Ensures returned es is consistent with limiters on qs.
- es(i) = min(es(i), p(i))
- enddo
- !$acc end parallel
-
- !$acc end data
-end subroutine wv_sat_qsat_water_vect
-
-subroutine wv_sat_qsat_ice(t, p, es, qs, idx)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- integer, intent(in), optional :: idx ! Scheme index
-
- es = wv_sat_svp_ice(t, idx)
-
- qs = wv_sat_svp_to_qsat(es, p)
-
- ! Ensures returned es is consistent with limiters on qs.
- es = min(es, p)
-
-end subroutine wv_sat_qsat_ice
-
-subroutine wv_sat_qsat_ice_vect(t, p, es, qs, vlen, idx)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- !------------------------------------------------------------------!
- ! Inputs
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature
- real(r8), intent(in) :: p(vlen) ! Pressure
- ! Outputs
- real(r8), intent(out) :: es(vlen) ! Saturation vapor pressure
- real(r8), intent(out) :: qs(vlen) ! Saturation specific humidity
-
- integer, intent(in), optional :: idx ! Scheme index
- integer :: i
-
- !$acc data present (t,p,es,qs)
-
- call wv_sat_svp_ice_vect(t, es, vlen, idx)
- call wv_sat_svp_to_qsat_vect(es, p, qs, vlen)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i=1,vlen
- ! Ensures returned es is consistent with limiters on qs.
- es(i) = min(es(i), p(i))
- enddo
- !$acc end parallel
-
- !$acc end data
-end subroutine wv_sat_qsat_ice_vect
-
-subroutine wv_sat_qsat_trans(t, p, es, qs, idx)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- integer, intent(in), optional :: idx ! Scheme index
-
- es = wv_sat_svp_trans(t, idx)
-
- qs = wv_sat_svp_to_qsat(es, p)
-
- ! Ensures returned es is consistent with limiters on qs.
- es = min(es, p)
-
-end subroutine wv_sat_qsat_trans
-
-!---------------------------------------------------------------------
-! SVP INTERFACE FUNCTIONS
-!---------------------------------------------------------------------
-
-function wv_sat_svp_water(t, idx) result(es)
- real(r8), intent(in) :: t
- integer, intent(in), optional :: idx
- real(r8) :: es
-
- integer :: use_idx
-
- if (present(idx)) then
- use_idx = idx
- else
- use_idx = default_idx
- end if
-
- select case (use_idx)
- case(GoffGratch_idx)
- es = GoffGratch_svp_water(t)
- case(MurphyKoop_idx)
- es = MurphyKoop_svp_water(t)
- case(Bolton_idx)
- es = Bolton_svp_water(t)
- end select
-
-end function wv_sat_svp_water
-
-subroutine wv_sat_svp_water_vect(t, es, vlen, idx)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen)
- integer, intent(in), optional :: idx
- real(r8), intent(out) :: es(vlen)
- integer :: i
- integer :: use_idx
-
- !$acc data present (t,es)
-
- if (present(idx)) then
- use_idx = idx
- else
- use_idx = default_idx
- end if
-
- select case (use_idx)
- case(GoffGratch_idx)
- call GoffGratch_svp_water_vect(t,es,vlen)
- case(MurphyKoop_idx)
- call MurphyKoop_svp_water_vect(t,es,vlen)
- case(Bolton_idx)
- call Bolton_svp_water_vect(t,es,vlen)
- end select
-
- !$acc end data
-end subroutine wv_sat_svp_water_vect
-
-function wv_sat_svp_ice(t, idx) result(es)
- real(r8), intent(in) :: t
- integer, intent(in), optional :: idx
- real(r8) :: es
-
- integer :: use_idx
-
- if (present(idx)) then
- use_idx = idx
- else
- use_idx = default_idx
- end if
-
- select case (use_idx)
- case(GoffGratch_idx)
- es = GoffGratch_svp_ice(t)
- case(MurphyKoop_idx)
- es = MurphyKoop_svp_ice(t)
- case(Bolton_idx)
- es = Bolton_svp_water(t)
- end select
-
-end function wv_sat_svp_ice
-
-subroutine wv_sat_svp_ice_vect(t, es, vlen, idx)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen)
- integer, intent(in), optional :: idx
- real(r8), intent(out) :: es(vlen)
- integer :: i
-
- integer :: use_idx
-
- !$acc data present (t,es)
-
- if (present(idx)) then
- use_idx = idx
- else
- use_idx = default_idx
- end if
-
- select case (use_idx)
- case(GoffGratch_idx)
- call GoffGratch_svp_ice_vect(t,es,vlen)
- case(MurphyKoop_idx)
- call MurphyKoop_svp_ice_vect(t,es,vlen)
- case(Bolton_idx)
- call Bolton_svp_water_vect(t,es,vlen)
- end select
-
- !$acc end data
-end subroutine wv_sat_svp_ice_vect
-
-function wv_sat_svp_trans(t, idx) result(es)
-
- real(r8), intent(in) :: t
- integer, intent(in), optional :: idx
- real(r8) :: es
-
- real(r8) :: esice ! Saturation vapor pressure over ice
- real(r8) :: weight ! Intermediate scratch variable for es transition
-
-!
-! Water
-!
- if (t >= (tmelt - ttrice)) then
- es = wv_sat_svp_water(t,idx)
- else
- es = 0.0_r8
- end if
-
-!
-! Ice
-!
- if (t < tmelt) then
-
- esice = wv_sat_svp_ice(t,idx)
-
- if ( (tmelt - t) > ttrice ) then
- weight = 1.0_r8
- else
- weight = (tmelt - t)/ttrice
- end if
-
- es = weight*esice + (1.0_r8 - weight)*es
- end if
-
-end function wv_sat_svp_trans
-
-subroutine wv_sat_svp_trans_vect(t, es, vlen, idx)
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen)
- integer, intent(in), optional :: idx
- real(r8), intent(out) :: es(vlen)
-
- real(r8) :: esice(vlen) ! Saturation vapor pressure over ice
- real(r8) :: weight ! Intermediate scratch variable for es transition
- integer :: i
-
- !$acc data present (t,es) &
- !$acc create (esice)
-
-!
-! Water
-!
- call wv_sat_svp_water_vect(t,es,vlen,idx)
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- if (t(i) < (tmelt - ttrice)) then
- es(i) = 0.0_r8
- end if
- end do
- !$acc end parallel
-!
-! Ice
-!
- call wv_sat_svp_ice_vect(t,esice,vlen,idx)
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- if (t(i) < tmelt) then
- if ( (tmelt - t(i)) > ttrice ) then
- weight = 1.0_r8
- else
- weight = (tmelt - t(i))/ttrice
- end if
-
- es(i) = weight*esice(i) + (1.0_r8 - weight)*es(i)
- end if
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine wv_sat_svp_trans_vect
-
-!---------------------------------------------------------------------
-! SVP METHODS
-!---------------------------------------------------------------------
-
-! Goff & Gratch (1946)
-
-function GoffGratch_svp_water(t) result(es)
- real(r8), intent(in) :: t ! Temperature in Kelvin
- real(r8) :: es ! SVP in Pa
-
- ! uncertain below -70 C
- es = 10._r8**(-7.90298_r8*(tboil/t-1._r8)+ &
- 5.02808_r8*log10(tboil/t)- &
- 1.3816e-7_r8*(10._r8**(11.344_r8*(1._r8-t/tboil))-1._r8)+ &
- 8.1328e-3_r8*(10._r8**(-3.49149_r8*(tboil/t-1._r8))-1._r8)+ &
- log10(1013.246_r8))*100._r8
-
-end function GoffGratch_svp_water
-
-subroutine GoffGratch_svp_water_vect(t, es, vlen)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature in Kelvin
- real(r8), intent(out) :: es(vlen) ! SVP in Pa
- real(r8) :: log_tboil
- integer :: i
-
- !$acc data present (t,es)
-
- ! Goff, J. A., and S. Gratch. “Low-Pressure Properties of Water from -160F
- ! to 212F.” Trans. Am. Soc. Heat. Vent. Eng. 52 (1946): 95–121.
- ! uncertain below -70 C
-
- log_tboil = log10(tboil)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i=1,vlen
- es(i) = 10._r8**(-7.90298_r8*(tboil/t(i)-1._r8)+ &
- 5.02808_r8*(log_tboil-log10(t(i)))- &
- 1.3816e-7_r8*(10._r8**(11.344_r8*(1._r8-t(i)/tboil))-1._r8)+ &
- 8.1328e-3_r8*(10._r8**(-3.49149_r8*(tboil/t(i)-1._r8))-1._r8)+ &
- log10(1013.246_r8))*100._r8
- enddo
- !$acc end parallel
-
- !$acc end data
-end subroutine GoffGratch_svp_water_vect
-
-function GoffGratch_svp_ice(t) result(es)
- real(r8), intent(in) :: t ! Temperature in Kelvin
- real(r8) :: es ! SVP in Pa
-
- ! good down to -100 C
- es = 10._r8**(-9.09718_r8*(h2otrip/t-1._r8)-3.56654_r8* &
- log10(h2otrip/t)+0.876793_r8*(1._r8-t/h2otrip)+ &
- log10(6.1071_r8))*100._r8
-
-end function GoffGratch_svp_ice
-
-subroutine GoffGratch_svp_ice_vect(t, es, vlen)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature in Kelvin
- real(r8), intent(out) :: es(vlen) ! SVP in Pa
- real(r8), parameter :: log_param = log10(6.1071_r8)
- integer :: i
- ! good down to -100 C
-
- !$acc data present (t,es)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i=1,vlen
- es(i) = 10._r8**(-9.09718_r8*(h2otrip/t(i)-1._r8)-3.56654_r8* &
- log10(h2otrip/t(i))+0.876793_r8*(1._r8-t(i)/h2otrip)+ &
- log_param)*100._r8
- enddo
- !$acc end parallel
-
- !$acc end data
-end subroutine GoffGratch_svp_ice_vect
-
-! Murphy & Koop (2005)
-
-function MurphyKoop_svp_water(t) result(es)
- real(r8), intent(in) :: t ! Temperature in Kelvin
- real(r8) :: es ! SVP in Pa
-
- ! (good for 123 < T < 332 K)
- es = exp(54.842763_r8 - (6763.22_r8 / t) - (4.210_r8 * log(t)) + &
- (0.000367_r8 * t) + (tanh(0.0415_r8 * (t - 218.8_r8)) * &
- (53.878_r8 - (1331.22_r8 / t) - (9.44523_r8 * log(t)) + &
- 0.014025_r8 * t)))
-
-end function MurphyKoop_svp_water
-
-subroutine MurphyKoop_svp_water_vect(t, es, vlen)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature in Kelvin
- real(r8), intent(out) :: es(vlen) ! SVP in Pa
-
- integer :: i
- ! Murphy, D. M., and T. Koop. “Review of the Vapour Pressure of Ice and
- ! Supercooled Water for Atmospheric Applications.” Q. J. R. Meteorol.
- ! Soc. 131, no. 608 (2005): 1539–65. 10.1256/qj.04.94
- ! (good for 123 < T < 332 K)
-
- !$acc data present (t,es)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- es(i) = exp(54.842763_r8 - (6763.22_r8 / t(i)) - (4.210_r8 * log(t(i))) + &
- (0.000367_r8 * t(i)) + (tanh(0.0415_r8 * (t(i) - 218.8_r8)) * &
- (53.878_r8 - (1331.22_r8 / t(i)) - (9.44523_r8 * log(t(i))) + &
- 0.014025_r8 * t(i))))
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine MurphyKoop_svp_water_vect
-
-function MurphyKoop_svp_ice(t) result(es)
- real(r8), intent(in) :: t ! Temperature in Kelvin
- real(r8) :: es ! SVP in Pa
-
- ! (good down to 110 K)
- es = exp(9.550426_r8 - (5723.265_r8 / t) + (3.53068_r8 * log(t)) &
- - (0.00728332_r8 * t))
-
-end function MurphyKoop_svp_ice
-
-subroutine MurphyKoop_svp_ice_vect(t, es, vlen)
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature in Kelvin
- real(r8), intent(out) :: es(vlen) ! SVP in Pa
-
- integer :: i
- ! (good down to 110 K)
-
- !$acc data present (t,es)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- es(i) = exp(9.550426_r8 - (5723.265_r8 / t(i)) + (3.53068_r8 * log(t(i))) &
- - (0.00728332_r8 * t(i)))
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine MurphyKoop_svp_ice_vect
-
-! Bolton (1980)
-! zm_conv deep convection scheme contained this SVP calculation.
-! It appears to be from D. Bolton, 1980, Monthly Weather Review.
-! Unlike the other schemes, no distinct ice formula is associated
-! with it. (However, a Bolton ice formula exists in CLUBB.)
-
-! The original formula used degrees C, but this function
-! takes Kelvin and internally converts.
-
-function Bolton_svp_water(t) result(es)
- real(r8),parameter :: c1 = 611.2_r8
- real(r8),parameter :: c2 = 17.67_r8
- real(r8),parameter :: c3 = 243.5_r8
-
- real(r8), intent(in) :: t ! Temperature in Kelvin
- real(r8) :: es ! SVP in Pa
-
- es = c1*exp( (c2*(t - tmelt))/((t - tmelt)+c3) )
-
-end function Bolton_svp_water
-
-subroutine Bolton_svp_water_vect(t, es,vlen)
- real(r8),parameter :: c1 = 611.2_r8
- real(r8),parameter :: c2 = 17.67_r8
- real(r8),parameter :: c3 = 243.5_r8
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature in Kelvin
- real(r8), intent(out) :: es(vlen) ! SVP in Pa
-
- integer :: i
-
- !$acc data present (t,es)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- es(i) = c1*exp( (c2*(t(i) - tmelt))/((t(i) - tmelt)+c3) )
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine Bolton_svp_water_vect
-
-end module wv_sat_methods
diff --git a/src/physics/cam/wv_saturation.F90 b/src/physics/cam/wv_saturation.F90
deleted file mode 100644
index ac94482e20..0000000000
--- a/src/physics/cam/wv_saturation.F90
+++ /dev/null
@@ -1,1484 +0,0 @@
-module wv_saturation
-
-!--------------------------------------------------------------------!
-! Module Overview: !
-! !
-! This module provides an interface to wv_sat_methods, providing !
-! saturation vapor pressure and related calculations to CAM. !
-! !
-! The original wv_saturation codes were introduced by J. J. Hack, !
-! February 1990. The code has been extensively rewritten since then, !
-! including a total refactoring in Summer 2012. !
-! !
-!--------------------------------------------------------------------!
-! Methods: !
-! !
-! Pure water/ice saturation vapor pressures are calculated on the !
-! fly, with the specific method determined by a runtime option. !
-! Mixed phase SVP is interpolated from the internal table, estbl, !
-! which is created during initialization. !
-! !
-! The default method for calculating SVP is determined by a namelist !
-! option, and used whenever svp_water/ice or qsat are called. !
-! !
-!--------------------------------------------------------------------!
-
-use shr_kind_mod, only: r8 => shr_kind_r8
-use physconst, only: epsilo, &
- latvap, &
- latice, &
- rh2o, &
- cpair, &
- tmelt, &
- h2otrip
-
-use wv_sat_methods, only: &
- svp_to_qsat => wv_sat_svp_to_qsat, &
- svp_to_qsat_vect => wv_sat_svp_to_qsat_vect
-
-implicit none
-private
-save
-
-! Public interfaces
-! Namelist, initialization, finalization
-public wv_sat_readnl
-public wv_sat_init
-public wv_sat_final
-
-! Saturation vapor pressure calculations
-public svp_water, svp_water_vect
-public svp_ice, svp_ice_vect
-
-! Mixed phase (water + ice) saturation vapor pressure table lookup
-public estblf
-
-public svp_to_qsat
-
-! Subroutines that return both SVP and humidity
-! Optional arguments do temperature derivatives
-interface qsat
- module procedure qsat_line
- module procedure qsat_vect
- module procedure qsat_2D
-end interface
-public qsat ! Mixed phase
-interface qsat_water
- module procedure qsat_water_line
- module procedure qsat_water_vect
- module procedure qsat_water_2D
-end interface
-public qsat_water ! SVP over water only
-interface qsat_ice
- module procedure qsat_ice_line
- module procedure qsat_ice_vect
- module procedure qsat_ice_2D
-end interface
-public qsat_ice ! SVP over ice only
-
-! Wet bulb temperature solver
-public :: findsp_vc, findsp
-
-! Data
-
-! This value is slightly high, but it seems to be the value for the
-! steam point of water originally (and most frequently) used in the
-! Goff & Gratch scheme.
-real(r8), parameter :: tboil = 373.16_r8
-
-! Table of saturation vapor pressure values (estbl) from tmin to
-! tmax+1 Kelvin, in one degree increments. ttrice defines the
-! transition region, estbl contains a combination of ice & water
-! values.
-! Make these public parameters in case another module wants to see the
-! extent of the table.
- real(r8), public, parameter :: tmin = 127.16_r8
- real(r8), public, parameter :: tmax = 375.16_r8
-
- real(r8), parameter :: ttrice = 20.00_r8 ! transition range from es over H2O to es over ice
-
- integer :: plenest ! length of estbl
- real(r8), allocatable :: estbl(:) ! table values of saturation vapor pressure
-
- real(r8) :: omeps ! 1.0_r8 - epsilo
-
- real(r8) :: c3 ! parameter used by findsp
-
- ! Set coefficients for polynomial approximation of difference
- ! between saturation vapor press over water and saturation pressure
- ! over ice for -ttrice < t < 0 (degrees C). NOTE: polynomial is
- ! valid in the range -40 < t < 0 (degrees C).
- real(r8) :: pcf(5) = (/ &
- 5.04469588506e-01_r8, &
- -5.47288442819e+00_r8, &
- -3.67471858735e-01_r8, &
- -8.95963532403e-03_r8, &
- -7.78053686625e-05_r8 /)
-
-! --- Degree 6 approximation ---
-! real(r8) :: pcf(6) = (/ &
-! 7.63285250063e-02, &
-! 5.86048427932e+00, &
-! 4.38660831780e-01, &
-! 1.37898276415e-02, &
-! 2.14444472424e-04, &
-! 1.36639103771e-06 /)
-
- integer, parameter :: VLENS = 128 ! vector length for a GPU kernel
-
- !$acc declare create (plenest,estbl,omeps,c3,pcf)
-
-contains
-
-!---------------------------------------------------------------------
-! ADMINISTRATIVE FUNCTIONS
-!---------------------------------------------------------------------
-
-subroutine wv_sat_readnl(nlfile)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Get runtime options for wv_saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_get_scheme_idx, &
- wv_sat_valid_idx, &
- wv_sat_set_default
-
- use spmd_utils, only: masterproc
- use namelist_utils, only: find_group_name
- use units, only: getunit, freeunit
- use mpishorthand
- use cam_abortutils, only: endrun
-
- character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
-
- ! Local variables
- integer :: unitn, ierr
-
- character(len=32) :: wv_sat_scheme = "GoffGratch"
-
- character(len=*), parameter :: subname = 'wv_sat_readnl'
-
- namelist /wv_sat_nl/ wv_sat_scheme
- !-----------------------------------------------------------------------------
-
- if (masterproc) then
- unitn = getunit()
- open( unitn, file=trim(nlfile), status='old' )
- call find_group_name(unitn, 'wv_sat_nl', status=ierr)
- if (ierr == 0) then
- read(unitn, wv_sat_nl, iostat=ierr)
- if (ierr /= 0) then
- call endrun(subname // ':: ERROR reading namelist')
- return
- end if
- end if
- close(unitn)
- call freeunit(unitn)
-
- end if
-
-#ifdef SPMD
- call mpibcast(wv_sat_scheme, len(wv_sat_scheme) , mpichar, 0, mpicom)
-#endif
-
- if (.not. wv_sat_set_default(wv_sat_scheme)) then
- call endrun('wv_sat_readnl :: Invalid wv_sat_scheme.')
- return
- end if
-
-end subroutine wv_sat_readnl
-
-subroutine wv_sat_init
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Initialize module (e.g. setting parameters, initializing the !
- ! SVP lookup table). !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_methods_init, &
- wv_sat_get_scheme_idx, &
- wv_sat_valid_idx
- use spmd_utils, only: masterproc
- use cam_logfile, only: iulog
- use cam_abortutils, only: endrun
- use shr_assert_mod, only: shr_assert_in_domain
- use error_messages, only: handle_errmsg
-
- integer :: status
-
- ! For wv_sat_methods error reporting.
- character(len=256) :: errstring
-
- ! For generating internal SVP table.
- real(r8) :: t ! Temperature
- integer :: i ! Increment counter
-
- ! Precalculated because so frequently used.
- omeps = 1.0_r8 - epsilo
-
- ! Transition range method is only valid for transition temperatures at:
- ! -40 deg C < T < 0 deg C
- call shr_assert_in_domain(ttrice, ge=0._r8, le=40._r8, varname="ttrice",&
- msg="wv_sat_init: Invalid transition temperature range.")
-
-! This parameter uses a hardcoded 287.04_r8?
- c3 = 287.04_r8*(7.5_r8*log(10._r8))/cpair
-
-! Init "methods" module containing actual SVP formulae.
-
- call wv_sat_methods_init(r8, tmelt, h2otrip, tboil, ttrice, &
- epsilo, errstring)
-
- call handle_errmsg(errstring, subname="wv_sat_methods_init")
-
- ! Add two to make the table slightly too big, just in case.
- plenest = ceiling(tmax-tmin) + 2
-
- ! Allocate SVP table.
- allocate(estbl(plenest), stat=status)
- if (status /= 0) then
- call endrun('wv_sat_init :: ERROR allocating saturation vapor pressure table')
- return
- end if
-
- do i = 1, plenest
- estbl(i) = svp_trans(tmin + real(i-1,r8))
- end do
-
- !$acc update device (plenest,estbl,omeps,c3,pcf)
-
- if (masterproc) then
- write(iulog,*)' *** SATURATION VAPOR PRESSURE TABLE COMPLETED ***'
- end if
-
-end subroutine wv_sat_init
-
-subroutine wv_sat_final
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Deallocate global variables in module. !
- !------------------------------------------------------------------!
- use cam_abortutils, only: endrun
-
- integer :: status
-
- if (allocated(estbl)) then
-
- deallocate(estbl, stat=status)
-
- if (status /= 0) then
- call endrun('wv_sat_final :: ERROR deallocating table')
- return
- end if
-
- end if
-
-end subroutine wv_sat_final
-
-!---------------------------------------------------------------------
-! DEFAULT SVP FUNCTIONS
-!---------------------------------------------------------------------
-
-! Compute saturation vapor pressure over water
-function svp_water(t) result(es)
-
- use wv_sat_methods, only: &
- wv_sat_svp_water
-
- real(r8), intent(in) :: t ! Temperature (K)
- real(r8) :: es ! SVP (Pa)
-
- es = wv_sat_svp_water(t)
-
-end function svp_water
-
-! Compute saturation vapor pressure over water
-subroutine svp_water_vect(t, es, vlen)
-
- use wv_sat_methods, only: &
- wv_sat_svp_water_vect
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature (K)
- real(r8), intent(out) :: es(vlen) ! SVP (Pa)
-
- !$acc data copyin (t) copyout (es)
-
- call wv_sat_svp_water_vect(t, es, vlen)
-
- !$acc end data
-end subroutine svp_water_vect
-
-! Compute saturation vapor pressure over ice
-function svp_ice(t) result(es)
-
- use wv_sat_methods, only: &
- wv_sat_svp_ice
-
- real(r8), intent(in) :: t ! Temperature (K)
- real(r8) :: es ! SVP (Pa)
-
- es = wv_sat_svp_ice(t)
-
-end function svp_ice
-
-! Compute saturation vapor pressure over ice
-subroutine svp_ice_vect(t, es, vlen)
-
- use wv_sat_methods, only: &
- wv_sat_svp_ice_vect
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature (K)
- real(r8), intent(out) :: es(vlen) ! SVP (Pa)
-
- !$acc data copyin(t) copyout(es)
-
- call wv_sat_svp_ice_vect(t, es, vlen)
-
- !$acc end data
-end subroutine svp_ice_vect
-
-! Compute saturation vapor pressure with an ice-water transition
-function svp_trans(t) result(es)
-
- use wv_sat_methods, only: &
- wv_sat_svp_trans
-
- real(r8), intent(in) :: t ! Temperature (K)
- real(r8) :: es ! SVP (Pa)
-
- es = wv_sat_svp_trans(t)
-
-end function svp_trans
-
-! Compute saturation vapor pressure with an ice-water transition
-subroutine svp_trans_vect(t, es, vlen)
-
- use wv_sat_methods, only: &
- wv_sat_svp_trans_vect
-
- integer, intent(in) :: vlen
- real(r8), intent(in) :: t(vlen) ! Temperature (K)
- real(r8), intent(out) :: es(vlen) ! SVP (Pa)
-
- !$acc data copyin(t) copyout(es)
-
- call wv_sat_svp_trans_vect(t, es, vlen)
-
- !$acc end data
-end subroutine svp_trans_vect
-
-!---------------------------------------------------------------------
-! UTILITIES
-!---------------------------------------------------------------------
-
-! Does linear interpolation from nearest values found
-! in the table (estbl).
-elemental function estblf(t) result(es)
-
- real(r8), intent(in) :: t ! Temperature
- real(r8) :: es ! SVP (Pa)
-
- integer :: i ! Index for t in the table
- real(r8) :: t_tmp ! intermediate temperature for es look-up
-
- real(r8) :: weight ! Weight for interpolation
-
- t_tmp = max(min(t,tmax)-tmin, 0._r8) ! Number of table entries above tmin
- i = int(t_tmp) + 1 ! Corresponding index.
- weight = t_tmp - aint(t_tmp, r8) ! Fractional part of t_tmp (for interpolation).
- es = (1._r8 - weight)*estbl(i) + weight*estbl(i+1)
-
-end function estblf
-
-! Does linear interpolation from nearest values found
-! in the table (estbl).
-subroutine estblf_vect(t, es, vlen)
-
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(out) :: es ! SVP (Pa)
-
- integer :: i ! Index for t in the table
- integer :: j
- real(r8) :: t_tmp ! intermediate temperature for es look-up
-
- real(r8) :: weight ! Weight for interpolation
-
- !$acc data present (t,es)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector private(t_tmp,weight,i)
- do j = 1, vlen
- t_tmp = max(min(t(j),tmax)-tmin, 0._r8) ! Number of table entries above tmin
- i = int(t_tmp) + 1 ! Corresponding index.
- weight = t_tmp - aint(t_tmp, r8) ! Fractional part of t_tmp (for interpolation).
- es(j) = (1._r8 - weight)*estbl(i) + weight*estbl(i+1)
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine estblf_vect
-
-! Get enthalpy based only on temperature
-! and specific humidity.
-elemental function tq_enthalpy(t, q, hltalt) result(enthalpy)
-
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: q ! Specific humidity
- real(r8), intent(in) :: hltalt ! Modified hlat for T derivatives
-
- real(r8) :: enthalpy
-
- enthalpy = cpair * t + hltalt * q
-
-end function tq_enthalpy
-
-! Get enthalpy based only on temperature
-! and specific humidity.
-subroutine tq_enthalpy_vect(t, q, hltalt, enthalpy, vlen)
-
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(in) :: q ! Specific humidity
- real(r8), dimension(vlen), intent(in) :: hltalt ! Modified hlat for T derivatives
-
- real(r8), dimension(vlen), intent(out) :: enthalpy
-
- integer :: i
-
- !$acc data present(t,q,hltalt,enthalpy)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- enthalpy(i) = cpair * t(i) + hltalt(i) * q(i)
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine tq_enthalpy_vect
-
-!---------------------------------------------------------------------
-! LATENT HEAT OF VAPORIZATION CORRECTIONS
-!---------------------------------------------------------------------
-
-elemental subroutine no_ip_hltalt(t, hltalt)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate latent heat of vaporization of pure liquid water at !
- ! a given temperature. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- ! Outputs
- real(r8), intent(out) :: hltalt ! Appropriately modified hlat
-
- hltalt = latvap
-
- ! Account for change of latvap with t above freezing where
- ! constant slope is given by -2369 j/(kg c) = cpv - cw
- if (t >= tmelt) then
- hltalt = hltalt - 2369.0_r8*(t-tmelt)
- end if
-
-end subroutine no_ip_hltalt
-
-subroutine no_ip_hltalt_vect(t, hltalt, vlen)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate latent heat of vaporization of pure liquid water at !
- ! a given temperature. !
- !------------------------------------------------------------------!
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- ! Outputs
- real(r8), dimension(vlen), intent(out) :: hltalt ! Appropriately modified hlat
-
- integer :: i
-
- !$acc data present(t,hltalt)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- hltalt(i) = latvap
- ! Account for change of latvap with t above freezing where
- ! constant slope is given by -2369 j/(kg c) = cpv - cw
- if (t(i) >= tmelt) then
- hltalt(i) = hltalt(i) - 2369.0_r8*(t(i)-tmelt)
- end if
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine no_ip_hltalt_vect
-
-elemental subroutine calc_hltalt(t, hltalt, tterm)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate latent heat of vaporization of water at a given !
- ! temperature, taking into account the ice phase if temperature !
- ! is below freezing. !
- ! Optional argument also calculates a term used to calculate !
- ! d(es)/dT within the water-ice transition range. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- ! Outputs
- real(r8), intent(out) :: hltalt ! Appropriately modified hlat
- ! Term to account for d(es)/dT in transition region.
- real(r8), intent(out), optional :: tterm
-
- ! Local variables
- real(r8) :: tc ! Temperature in degrees C
- real(r8) :: weight ! Weight for es transition from water to ice
- ! Loop iterator
- integer :: i
-
- if (present(tterm)) tterm = 0.0_r8
-
- call no_ip_hltalt(t,hltalt)
- if (t < tmelt) then
- ! Weighting of hlat accounts for transition from water to ice.
- tc = t - tmelt
-
- if (tc >= -ttrice) then
- weight = -tc/ttrice
-
- ! polynomial expression approximates difference between es
- ! over water and es over ice from 0 to -ttrice (C) (max of
- ! ttrice is 40): required for accurate estimate of es
- ! derivative in transition range from ice to water
- if (present(tterm)) then
- do i = size(pcf), 1, -1
- tterm = pcf(i) + tc*tterm
- end do
- tterm = tterm/ttrice
- end if
-
- else
- weight = 1.0_r8
- end if
-
- hltalt = hltalt + weight*latice
-
- end if
-
-end subroutine calc_hltalt
-
-subroutine calc_hltalt_vect(t, hltalt, vlen, tterm)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate latent heat of vaporization of water at a given !
- ! temperature, taking into account the ice phase if temperature !
- ! is below freezing. !
- ! Optional argument also calculates a term used to calculate !
- ! d(es)/dT within the water-ice transition range. !
- !------------------------------------------------------------------!
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- ! Outputs
- real(r8), dimension(vlen), intent(out) :: hltalt ! Appropriately modified hlat
- ! Term to account for d(es)/dT in transition region.
- real(r8), dimension(vlen), intent(out), optional :: tterm
-
- ! Local variables
- real(r8) :: tc ! Temperature in degrees C
- real(r8) :: weight ! Weight for es transition from water to ice
- logical :: present_tterm
- ! Loop iterator
- integer :: i, j, size_pcf
-
- present_tterm = present(tterm)
- size_pcf = size(pcf)
-
- !$acc data present(t,hltalt,tterm)
-
- if (present_tterm) then
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- tterm(i) = 0.0_r8
- end do
- !$acc end parallel
- end if
-
- call no_ip_hltalt_vect(t,hltalt,vlen)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector private(tc,weight)
- do j = 1, vlen
- if (t(j) < tmelt) then
- ! Weighting of hlat accounts for transition from water to ice.
- tc = t(j) - tmelt
-
- if (tc >= -ttrice) then
- weight = -tc/ttrice
-
- ! polynomial expression approximates difference between es
- ! over water and es over ice from 0 to -ttrice (C) (max of
- ! ttrice is 40): required for accurate estimate of es
- ! derivative in transition range from ice to water
- if (present_tterm) then
- !$acc loop seq
- do i = size_pcf, 1, -1
- tterm(j) = pcf(i) + tc*tterm(j)
- end do
- tterm(j) = tterm(j)/ttrice
- end if
-
- else
- weight = 1.0_r8
- end if
-
- hltalt(j) = hltalt(j) + weight*latice
-
- end if
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine calc_hltalt_vect
-
-!---------------------------------------------------------------------
-! OPTIONAL OUTPUTS
-!---------------------------------------------------------------------
-
-! Temperature derivative outputs, for qsat_*
-subroutine deriv_outputs_line(t, p, es, qs, hltalt, tterm, &
- gam, dqsdt)
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- real(r8), intent(in) :: es ! Saturation vapor pressure
- real(r8), intent(in) :: qs ! Saturation specific humidity
- real(r8), intent(in) :: hltalt ! Modified latent heat
- real(r8), intent(in) :: tterm ! Extra term for d(es)/dT in
- ! transition region.
-
- ! Outputs
- real(r8), intent(out), optional :: gam ! (hltalt/cpair)*(d(qs)/dt)
- real(r8), intent(out), optional :: dqsdt ! (d(qs)/dt)
-
- ! Local variables
- real(r8) :: desdt ! d(es)/dt
- real(r8) :: dqsdt_loc ! local copy of dqsdt
-
- if (qs == 1.0_r8) then
- dqsdt_loc = 0._r8
- else
- desdt = hltalt*es/(rh2o*t*t) + tterm
- dqsdt_loc = qs*p*desdt/(es*(p-omeps*es))
- end if
-
- if (present(dqsdt)) dqsdt = dqsdt_loc
- if (present(gam)) gam = dqsdt_loc * (hltalt/cpair)
-
-end subroutine deriv_outputs_line
-
-! Temperature derivative outputs, for qsat_*
-subroutine deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam, dqsdt)
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(in) :: p ! Pressure
- real(r8), dimension(vlen), intent(in) :: es ! Saturation vapor pressure
- real(r8), dimension(vlen), intent(in) :: qs ! Saturation specific humidity
- real(r8), dimension(vlen), intent(in) :: hltalt ! Modified latent heat
- real(r8), dimension(vlen), intent(in) :: tterm ! Extra term for d(es)/dT in
- ! transition region.
-
- ! Outputs
- real(r8), dimension(vlen), intent(out), optional :: gam ! (hltalt/cpair)*(d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: dqsdt ! (d(qs)/dt)
-
- ! Local variables
- real(r8) :: desdt ! d(es)/dt
- real(r8) :: dqsdt_loc ! local copy of dqsdt
- logical :: present_dqsdt, present_gam
- integer :: i
-
- present_dqsdt = present(dqsdt)
- present_gam = present(gam)
-
- !$acc data present(t,p,es,qs,hltalt,tterm,gam,dqsdt)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector private(dqsdt_loc,desdt)
- do i = 1, vlen
- if (qs(i) == 1.0_r8) then
- dqsdt_loc = 0._r8
- else
- desdt = hltalt(i)*es(i)/(rh2o*t(i)*t(i)) + tterm(i)
- dqsdt_loc = qs(i)*p(i)*desdt/(es(i)*(p(i)-omeps*es(i)))
- end if
-
- if (present_dqsdt) dqsdt(i) = dqsdt_loc
- if (present_gam) gam(i) = dqsdt_loc * (hltalt(i)/cpair)
- end do
- !$acc end parallel
-
- !$acc end data
-end subroutine deriv_outputs_vect
-
-!---------------------------------------------------------------------
-! QSAT (SPECIFIC HUMIDITY) PROCEDURES
-!---------------------------------------------------------------------
-
-subroutine qsat_line(t, p, es, qs, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Look up and return saturation vapor pressure from precomputed !
- ! table, then calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8) :: hltalt ! Modified latent heat for T derivatives
- real(r8) :: tterm ! Account for d(es)/dT in transition region
-
- es = estblf(t)
-
- qs = svp_to_qsat(es, p)
-
- ! Ensures returned es is consistent with limiters on qs.
- es = min(es, p)
-
- ! Calculate optional arguments.
- if (present(gam) .or. present(dqsdt) .or. present(enthalpy)) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call calc_hltalt(t, hltalt, tterm)
-
- if (present(enthalpy)) enthalpy = tq_enthalpy(t, qs, hltalt)
-
- call deriv_outputs_line(t, p, es, qs, hltalt, tterm, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
-end subroutine qsat_line
-
-subroutine qsat_vect(t, p, es, qs, vlen, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Look up and return saturation vapor pressure from precomputed !
- ! table, then calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(vlen), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(vlen), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(vlen), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(vlen) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(vlen) :: tterm ! Account for d(es)/dT in transition region
- integer :: i
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (hltalt,tterm)
-
- call estblf_vect(t, es, vlen)
-
- call svp_to_qsat_vect(es, p, qs, vlen)
-
- ! Ensures returned es is consistent with limiters on qs.
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- es(i) = min(es(i), p(i))
- end do
- !$acc end parallel
-
- ! Calculate optional arguments.
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call calc_hltalt_vect(t, hltalt, vlen, tterm)
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_vect
-
-subroutine qsat_2D(t, p, es, qs, dim1, dim2, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Look up and return saturation vapor pressure from precomputed !
- ! table, then calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- ! Inputs
- integer, intent(in) :: dim1, dim2
- real(r8), dimension(dim1,dim2), intent(in) :: t ! Temperature
- real(r8), dimension(dim1,dim2), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(dim1,dim2), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(dim1,dim2), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(dim1,dim2), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(dim1,dim2) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(dim1,dim2) :: tterm ! Account for d(es)/dT in transition region
- integer :: i, k, vlen
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- vlen = dim1 * dim2
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (hltalt,tterm)
-
- call estblf_vect(t, es, vlen)
-
- call svp_to_qsat_vect(es, p, qs, vlen)
-
- ! Ensures returned es is consistent with limiters on qs.
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector collapse(2)
- do k = 1, dim2
- do i = 1, dim1
- es(i,k) = min(es(i,k), p(i,k))
- end do
- end do
- !$acc end parallel
-
- ! Calculate optional arguments.
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call calc_hltalt_vect(t, hltalt, vlen, tterm)
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_2D
-
-subroutine qsat_water_line(t, p, es, qs, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over water at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_water
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8) :: hltalt ! Modified latent heat for T derivatives
-
- call wv_sat_qsat_water(t, p, es, qs)
-
- if (present(gam) .or. present(dqsdt) .or. present(enthalpy)) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call no_ip_hltalt(t, hltalt)
-
- if (present(enthalpy)) enthalpy = tq_enthalpy(t, qs, hltalt)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_line(t, p, es, qs, hltalt, 0._r8, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
-end subroutine qsat_water_line
-
-subroutine qsat_water_vect(t, p, es, qs, vlen, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over water at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_water_vect
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(vlen), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(vlen), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(vlen), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(vlen) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(vlen) :: tterm
- integer :: i
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (tterm,hltalt)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- tterm(i) = 0._r8
- end do
- !$acc end parallel
-
- call wv_sat_qsat_water_vect(t, p, es, qs, vlen)
-
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call no_ip_hltalt_vect(t, hltalt, vlen)
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_water_vect
-
-subroutine qsat_water_2D(t, p, es, qs, dim1, dim2, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over water at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_water_vect
-
- ! Inputs
- integer, intent(in) :: dim1, dim2
- real(r8), dimension(dim1,dim2), intent(in) :: t ! Temperature
- real(r8), dimension(dim1,dim2), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(dim1,dim2), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(dim1,dim2), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(dim1,dim2), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(dim1,dim2) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(dim1,dim2) :: tterm
- integer :: i, k, vlen
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- vlen = dim1 * dim2
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (hltalt,tterm)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector collapse(2)
- do k = 1, dim2
- do i = 1, dim1
- tterm(i,k) = 0._r8
- end do
- end do
- !$acc end parallel
-
- call wv_sat_qsat_water_vect(t, p, es, qs, vlen)
-
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- ! "generalized" analytic expression for t derivative of es
- ! accurate to within 1 percent for 173.16 < t < 373.16
- call no_ip_hltalt_vect(t, hltalt, vlen)
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_water_2D
-
-subroutine qsat_ice_line(t, p, es, qs, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_ice
-
- ! Inputs
- real(r8), intent(in) :: t ! Temperature
- real(r8), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), intent(out) :: es ! Saturation vapor pressure
- real(r8), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8) :: hltalt ! Modified latent heat for T derivatives
-
- call wv_sat_qsat_ice(t, p, es, qs)
-
- if (present(gam) .or. present(dqsdt) .or. present(enthalpy)) then
-
- ! For pure ice, just add latent heats.
- hltalt = latvap + latice
-
- if (present(enthalpy)) enthalpy = tq_enthalpy(t, qs, hltalt)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_line(t, p, es, qs, hltalt, 0._r8, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
-end subroutine qsat_ice_line
-
-subroutine qsat_ice_vect(t, p, es, qs, vlen, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_ice_vect
-
- ! Inputs
- integer, intent(in) :: vlen
- real(r8), dimension(vlen), intent(in) :: t ! Temperature
- real(r8), dimension(vlen), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(vlen), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(vlen), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(vlen), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(vlen), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(vlen) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(vlen) :: tterm
- integer :: i
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (hltalt,tterm)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- tterm(i) = 0._r8
- end do
- !$acc end parallel
-
- call wv_sat_qsat_ice_vect(t, p, es, qs, vlen)
-
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector
- do i = 1, vlen
- ! For pure ice, just add latent heats.
- hltalt(i) = latvap + latice
- end do
- !$acc end parallel
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_ice_vect
-
-subroutine qsat_ice_2D(t, p, es, qs, dim1, dim2, gam, dqsdt, enthalpy)
- !------------------------------------------------------------------!
- ! Purpose: !
- ! Calculate SVP over ice at a given temperature, and then !
- ! calculate and return saturation specific humidity. !
- ! Optionally return various temperature derivatives or enthalpy !
- ! at saturation. !
- !------------------------------------------------------------------!
-
- use wv_sat_methods, only: wv_sat_qsat_ice_vect
-
- ! Inputs
- integer, intent(in) :: dim1, dim2
- real(r8), dimension(dim1,dim2), intent(in) :: t ! Temperature
- real(r8), dimension(dim1,dim2), intent(in) :: p ! Pressure
- ! Outputs
- real(r8), dimension(dim1,dim2), intent(out) :: es ! Saturation vapor pressure
- real(r8), dimension(dim1,dim2), intent(out) :: qs ! Saturation specific humidity
-
- real(r8), dimension(dim1,dim2), intent(out), optional :: gam ! (l/cpair)*(d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: dqsdt ! (d(qs)/dt)
- real(r8), dimension(dim1,dim2), intent(out), optional :: enthalpy ! cpair*t + hltalt*q
-
- ! Local variables
- real(r8), dimension(dim1,dim2) :: hltalt ! Modified latent heat for T derivatives
- real(r8), dimension(dim1,dim2) :: tterm
- integer :: i, k, vlen
- logical :: present_gam, present_dqsdt, present_enthalpy
-
- vlen = dim1 * dim2
- present_gam = present(gam)
- present_dqsdt = present(dqsdt)
- present_enthalpy = present(enthalpy)
-
- !$acc data copyin (t,p) &
- !$acc copyout (es,qs,gam,dqsdt,enthalpy) &
- !$acc create (hltalt,tterm)
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector collapse(2)
- do k = 1, dim2
- do i = 1, dim1
- tterm(i,k) = 0._r8
- end do
- end do
- !$acc end parallel
-
- call wv_sat_qsat_ice_vect(t, p, es, qs, vlen)
-
- if (present_gam .or. present_dqsdt .or. present_enthalpy) then
-
- !$acc parallel vector_length(VLENS) default(present)
- !$acc loop gang vector collapse(2)
- do k = 1, dim2
- do i = 1, dim1
- ! For pure ice, just add latent heats.
- hltalt(i,k) = latvap + latice
- end do
- end do
- !$acc end parallel
-
- if (present_enthalpy) call tq_enthalpy_vect(t, qs, hltalt, enthalpy, vlen)
-
- ! For pure water/ice transition term is 0.
- call deriv_outputs_vect(t, p, es, qs, hltalt, tterm, vlen, &
- gam=gam, dqsdt=dqsdt)
-
- end if
-
- !$acc end data
-end subroutine qsat_ice_2D
-
-!---------------------------------------------------------------------
-! FINDSP (WET BULB TEMPERATURE) PROCEDURES
-!---------------------------------------------------------------------
-
-subroutine findsp_vc(q, t, p, use_ice, tsp, qsp)
-
- use cam_logfile, only: iulog
- use cam_abortutils, only: endrun
-
- ! Wrapper for findsp which is 1D and handles the output status.
- ! Changing findsp to elemental restricted debugging output.
- ! If that output is needed again, it's preferable *not* to copy findsp,
- ! but to change the existing version.
-
- ! input arguments
- real(r8), intent(in) :: q(:) ! water vapor (kg/kg)
- real(r8), intent(in) :: t(:) ! temperature (K)
- real(r8), intent(in) :: p(:) ! pressure (Pa)
- logical, intent(in) :: use_ice ! flag to include ice phase in calculations
-
- ! output arguments
- real(r8), intent(out) :: tsp(:) ! saturation temp (K)
- real(r8), intent(out) :: qsp(:) ! saturation mixing ratio (kg/kg)
-
- integer :: status(size(q)) ! flag representing state of output
- ! 0 => Successful convergence
- ! 1 => No calculation done: pressure or specific
- ! humidity not within usable range
- ! 2 => Run failed to converge
- ! 4 => Temperature fell below minimum
- ! 8 => Enthalpy not conserved
-
- integer :: n, i
-
- n = size(q)
-
- ! Currently, only 2 and 8 seem to be treated as fatal errors.
- do i = 1,n
- call findsp(q(i), t(i), p(i), use_ice, tsp(i), qsp(i), status(i))
- if (status(i) == 2) then
- write(iulog,*) ' findsp not converging at i = ', i
- write(iulog,*) ' t, q, p ', t(i), q(i), p(i)
- write(iulog,*) ' tsp, qsp ', tsp(i), qsp(i)
- call endrun ('wv_saturation::FINDSP -- not converging')
- else if (status(i) == 8) then
- write(iulog,*) ' the enthalpy is not conserved at i = ', i
- write(iulog,*) ' t, q, p ', t(i), q(i), p(i)
- write(iulog,*) ' tsp, qsp ', tsp(i), qsp(i)
- call endrun ('wv_saturation::FINDSP -- enthalpy is not conserved')
- endif
- end do
-
-end subroutine findsp_vc
-
-subroutine findsp (q, t, p, use_ice, tsp, qsp, status)
-!-----------------------------------------------------------------------
-!
-! Purpose:
-! find the wet bulb temperature for a given t and q
-! in a longitude height section
-! wet bulb temp is the temperature and spec humidity that is
-! just saturated and has the same enthalpy
-! if q > qs(t) then tsp > t and qsp = qs(tsp) < q
-! if q < qs(t) then tsp < t and qsp = qs(tsp) > q
-!
-! Method:
-! a Newton method is used
-! first guess uses an algorithm provided by John Petch from the UKMO
-! we exclude points where the physical situation is unrealistic
-! e.g. where the temperature is outside the range of validity for the
-! saturation vapor pressure, or where the water vapor pressure
-! exceeds the ambient pressure, or the saturation specific humidity is
-! unrealistic
-!
-! Author: P. Rasch
-!
-!-----------------------------------------------------------------------
-!
-! input arguments
-!
-
- real(r8), intent(in) :: q ! water vapor (kg/kg)
- real(r8), intent(in) :: t ! temperature (K)
- real(r8), intent(in) :: p ! pressure (Pa)
- logical, intent(in) :: use_ice ! flag to include ice phase in calculations
-!
-! output arguments
-!
- real(r8), intent(out) :: tsp ! saturation temp (K)
- real(r8), intent(out) :: qsp ! saturation mixing ratio (kg/kg)
- integer, intent(out) :: status ! flag representing state of output
- ! 0 => Successful convergence
- ! 1 => No calculation done: pressure or specific
- ! humidity not within usable range
- ! 2 => Run failed to converge
- ! 4 => Temperature fell below minimum
- ! 8 => Enthalpy not conserved
-!
-! local variables
-!
- integer, parameter :: iter = 8 ! max number of times to iterate the calculation
- integer :: l ! iterator
-
- real(r8) es ! sat. vapor pressure
- real(r8) gam ! change in sat spec. hum. wrt temperature (times hltalt/cpair)
- real(r8) dgdt ! work variable
- real(r8) g ! work variable
- real(r8) hltalt ! lat. heat. of vap.
- real(r8) qs ! spec. hum. of water vapor
-
-! work variables
- real(r8) t1, q1, dt, dq
- real(r8) qvd
- real(r8) r1b, c1, c2
- real(r8), parameter :: dttol = 1.e-4_r8 ! the relative temp error tolerance required to quit the iteration
- real(r8), parameter :: dqtol = 1.e-4_r8 ! the relative moisture error tolerance required to quit the iteration
- real(r8) enin, enout
-
- ! Saturation specific humidity at this temperature
- if (use_ice) then
- call qsat(t, p, es, qs)
- else
- call qsat_water(t, p, es, qs)
- end if
-
- ! make sure a meaningful calculation is possible
- if (p <= 5._r8*es .or. qs <= 0._r8 .or. qs >= 0.5_r8 &
- .or. t < tmin .or. t > tmax) then
- status = 1
- ! Keep initial parameters when conditions aren't suitable
- tsp = t
- qsp = q
- enin = 1._r8
- enout = 1._r8
-
- return
- end if
-
- ! Prepare to iterate
- status = 2
-
- ! Get initial enthalpy
- if (use_ice) then
- call calc_hltalt(t,hltalt)
- else
- call no_ip_hltalt(t,hltalt)
- end if
- enin = tq_enthalpy(t, q, hltalt)
-
- ! make a guess at the wet bulb temp using a UKMO algorithm (from J. Petch)
- c1 = hltalt*c3
- c2 = (t + 36._r8)**2
- r1b = c2/(c2 + c1*qs)
- qvd = r1b * (q - qs)
- tsp = t + ((hltalt/cpair)*qvd)
-
- ! Generate qsp, gam, and enout from tsp.
- if (use_ice) then
- call qsat(tsp, p, es, qsp, gam=gam, enthalpy=enout)
- else
- call qsat_water(tsp, p, es, qsp, gam=gam, enthalpy=enout)
- end if
-
- ! iterate on first guess
- do l = 1, iter
-
- g = enin - enout
- dgdt = -cpair * (1 + gam)
-
- ! New tsp
- t1 = tsp - g/dgdt
- dt = abs(t1 - tsp)/t1
- tsp = t1
-
- ! bail out if past end of temperature range
- if ( tsp < tmin ) then
- tsp = tmin
- ! Get latent heat and set qsp to a value
- ! that preserves enthalpy.
- if (use_ice) then
- call calc_hltalt(tsp,hltalt)
- else
- call no_ip_hltalt(tsp,hltalt)
- end if
- qsp = (enin - cpair*tsp)/hltalt
- enout = tq_enthalpy(tsp, qsp, hltalt)
- status = 4
- exit
- end if
-
- ! Re-generate qsp, gam, and enout from new tsp.
- if (use_ice) then
- call qsat(tsp, p, es, q1, gam=gam, enthalpy=enout)
- else
- call qsat_water(tsp, p, es, q1, gam=gam, enthalpy=enout)
- end if
- dq = abs(q1 - qsp)/max(q1,1.e-12_r8)
- qsp = q1
-
- ! if converged at this point, exclude it from more iterations
- if (dt < dttol .and. dq < dqtol) then
- status = 0
- exit
- endif
- end do
-
- ! Test for enthalpy conservation
- if (abs((enin-enout)/(enin+enout)) > 1.e-4_r8) status = 8
-
-end subroutine findsp
-
-end module wv_saturation
diff --git a/src/physics/cam/zm_conv_intr.F90 b/src/physics/cam/zm_conv_intr.F90
index 5db6d1bc03..44488ac737 100644
--- a/src/physics/cam/zm_conv_intr.F90
+++ b/src/physics/cam/zm_conv_intr.F90
@@ -14,6 +14,7 @@ module zm_conv_intr
use zm_convr, only: zm_convr_init, zm_convr_run
use zm_conv_convtran, only: zm_conv_convtran_run
use zm_conv_momtran, only: zm_conv_momtran_run
+ use cloud_fraction_fice, only: cloud_fraction_fice_run
use rad_constituents, only: rad_cnst_get_info, rad_cnst_get_mode_num, rad_cnst_get_aer_mmr, &
rad_cnst_get_aer_props, rad_cnst_get_mode_props !, &
@@ -23,6 +24,8 @@ module zm_conv_intr
use perf_mod
use cam_logfile, only: iulog
use constituents, only: cnst_add
+ use ref_pres, only: trop_cloud_top_lev
+ use phys_control, only: phys_getopts
implicit none
private
@@ -37,7 +40,7 @@ module zm_conv_intr
zm_conv_tend, &! return tendencies
zm_conv_tend_2 ! return tendencies
- public zmconv_ke, zmconv_ke_lnd, zmconv_org ! needed by convect_shallow
+ public zmconv_ke, zmconv_ke_lnd ! needed by convect_shallow
integer ::& ! indices for fields in the physics buffer
zm_mu_idx, &
@@ -52,11 +55,9 @@ module zm_conv_intr
zm_ideep_idx, &
dp_flxprc_idx, &
dp_flxsnw_idx, &
- ixorg, &
+ dp_cldliq_idx, &
+ dp_cldice_idx, &
dlfzm_idx, & ! detrained convective cloud water mixing ratio.
- difzm_idx, & ! detrained convective cloud ice mixing ratio.
- dnlfzm_idx, & ! detrained convective cloud water num concen.
- dnifzm_idx, & ! detrained convective cloud ice num concen.
prec_dp_idx, &
snow_dp_idx, &
mconzm_idx ! convective mass flux
@@ -70,8 +71,6 @@ module zm_conv_intr
real(r8) :: zmconv_momcd = unset_r8
integer :: zmconv_num_cin ! Number of negative buoyancy regions that are allowed
! before the convection top and CAPE calculations are completed.
- logical :: zmconv_org ! Parameterization for sub-grid scale convective organization for the ZM deep
- ! convective scheme based on Mapes and Neale (2011)
real(r8) :: zmconv_dmpdz = unset_r8 ! Parcel fractional mass entrainment rate
real(r8) :: zmconv_tiedke_add = unset_r8 ! Convective parcel temperature perturbation
real(r8) :: zmconv_capelmt = unset_r8 ! Triggering thereshold for ZM convection
@@ -141,15 +140,9 @@ subroutine zm_conv_register
! detrained convective cloud water mixing ratio.
call pbuf_add_field('DLFZM', 'physpkg', dtype_r8, (/pcols,pver/), dlfzm_idx)
- ! detrained convective cloud ice mixing ratio.
- call pbuf_add_field('DIFZM', 'physpkg', dtype_r8, (/pcols,pver/), difzm_idx)
! convective mass fluxes
call pbuf_add_field('CMFMC_DP', 'physpkg', dtype_r8, (/pcols,pverp/), mconzm_idx)
-!CACNOTE - Is zm_org really a constituent or was it just a handy structure to use for an allocatable which persists in the run?
- if (zmconv_org) then
- call cnst_add('ZM_ORG',0._r8,0._r8,0._r8,ixorg,longname='organization parameter')
- endif
end subroutine zm_conv_register
@@ -159,7 +152,6 @@ subroutine zm_conv_readnl(nlfile)
use spmd_utils, only: mpicom, masterproc, masterprocid, mpi_real8, mpi_integer, mpi_logical
use namelist_utils, only: find_group_name
- use units, only: getunit, freeunit
character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
@@ -168,15 +160,14 @@ subroutine zm_conv_readnl(nlfile)
character(len=*), parameter :: subname = 'zm_conv_readnl'
namelist /zmconv_nl/ zmconv_c0_lnd, zmconv_c0_ocn, zmconv_num_cin, &
- zmconv_ke, zmconv_ke_lnd, zmconv_org, &
+ zmconv_ke, zmconv_ke_lnd, &
zmconv_momcu, zmconv_momcd, &
zmconv_dmpdz, zmconv_tiedke_add, zmconv_capelmt, &
zmconv_parcel_pbl, zmconv_tau
!-----------------------------------------------------------------------------
if (masterproc) then
- unitn = getunit()
- open( unitn, file=trim(nlfile), status='old' )
+ open( newunit=unitn, file=trim(nlfile), status='old' )
call find_group_name(unitn, 'zmconv_nl', status=ierr)
if (ierr == 0) then
read(unitn, zmconv_nl, iostat=ierr)
@@ -185,7 +176,6 @@ subroutine zm_conv_readnl(nlfile)
end if
end if
close(unitn)
- call freeunit(unitn)
end if
@@ -204,8 +194,6 @@ subroutine zm_conv_readnl(nlfile)
if (ierr /= 0) call endrun("zm_conv_readnl: FATAL: mpi_bcast: zmconv_momcu")
call mpi_bcast(zmconv_momcd, 1, mpi_real8, masterprocid, mpicom, ierr)
if (ierr /= 0) call endrun("zm_conv_readnl: FATAL: mpi_bcast: zmconv_momcd")
- call mpi_bcast(zmconv_org, 1, mpi_logical, masterprocid, mpicom, ierr)
- if (ierr /= 0) call endrun("zm_conv_readnl: FATAL: mpi_bcast: zmconv_org")
call mpi_bcast(zmconv_dmpdz, 1, mpi_real8, masterprocid, mpicom, ierr)
if (ierr /= 0) call endrun("zm_conv_readnl: FATAL: mpi_bcast: zmconv_dmpdz")
call mpi_bcast(zmconv_tiedke_add, 1, mpi_real8, masterprocid, mpicom, ierr)
@@ -260,10 +248,6 @@ subroutine zm_conv_init(pref_edge)
! Register fields with the output buffer
!
- if (zmconv_org) then
- call addfld ('ZM_ORG ', (/ 'lev' /), 'A', '- ','Organization parameter')
- call addfld ('ZM_ORG2D ', (/ 'lev' /), 'A', '- ','Organization parameter 2D')
- endif
call addfld ('PRECZ', horiz_only, 'A', 'm/s','total precipitation from ZM convection')
call addfld ('ZMDT', (/ 'lev' /), 'A', 'K/s','T tendency - Zhang-McFarlane moist convection')
call addfld ('ZMDQ', (/ 'lev' /), 'A', 'kg/kg/s','Q tendency - Zhang-McFarlane moist convection')
@@ -306,16 +290,11 @@ subroutine zm_conv_init(pref_edge)
call addfld ('ZMICVU', (/ 'lev' /), 'A', 'm/s', 'ZM in-cloud V updrafts')
call addfld ('ZMICVD', (/ 'lev' /), 'A', 'm/s', 'ZM in-cloud V downdrafts')
- call addfld ('DIFZM' ,(/ 'lev' /), 'A','kg/kg/s ','Detrained ice water from ZM convection')
call addfld ('DLFZM' ,(/ 'lev' /), 'A','kg/kg/s ','Detrained liquid water from ZM convection')
call phys_getopts( history_budget_out = history_budget, &
history_budget_histfile_num_out = history_budget_histfile_num)
- if (zmconv_org) then
- call add_default('ZM_ORG', 1, ' ')
- call add_default('ZM_ORG2D', 1, ' ')
- endif
if ( history_budget ) then
call add_default('EVAPTZM ', history_budget_histfile_num, ' ')
call add_default('EVAPQZM ', history_budget_histfile_num, ' ')
@@ -362,14 +341,17 @@ subroutine zm_conv_init(pref_edge)
end if
no_deep_pbl = phys_deepconv_pbl()
-!CACNOTE - Need to check errflg and report errors
- call zm_convr_init(cpair, epsilo, gravit, latvap, tmelt, rair, &
- limcnv,zmconv_c0_lnd, zmconv_c0_ocn, zmconv_ke, zmconv_ke_lnd, &
- zmconv_momcu, zmconv_momcd, zmconv_num_cin, zmconv_org, &
+ call zm_convr_init(plev, plevp, cpair, epsilo, gravit, latvap, tmelt, rair, &
+ pref_edge,zmconv_c0_lnd, zmconv_c0_ocn, zmconv_ke, zmconv_ke_lnd, &
+ zmconv_momcu, zmconv_momcd, zmconv_num_cin, &
no_deep_pbl, zmconv_tiedke_add, &
zmconv_capelmt, zmconv_dmpdz,zmconv_parcel_pbl, zmconv_tau, &
masterproc, iulog, errmsg, errflg)
+ if (errflg /= 0) then
+ call endrun('From zm_convr_init:' // errmsg)
+ end if
+
cld_idx = pbuf_get_index('CLD')
fracis_idx = pbuf_get_index('FRACIS')
@@ -394,8 +376,10 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
use physics_buffer, only : pbuf_get_field, physics_buffer_desc, pbuf_old_tim_idx
use constituents, only: pcnst, cnst_get_ind, cnst_is_convtran1
use physconst, only: gravit, latice, latvap, tmelt, cpwv, cpliq, rh2o
+ use phys_grid, only: get_rlat_all_p, get_rlon_all_p
use phys_control, only: cam_physpkg_is
+ use ccpp_constituent_prop_mod, only: ccpp_const_props
! Arguments
@@ -451,9 +435,6 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
real(r8), pointer, dimension(:,:) :: flxprec ! Convective-scale flux of precip at interfaces (kg/m2/s)
real(r8), pointer, dimension(:,:) :: flxsnow ! Convective-scale flux of snow at interfaces (kg/m2/s)
real(r8), pointer :: dlf(:,:) ! detrained convective cloud water mixing ratio.
- real(r8), pointer :: dif(:,:) ! detrained convective cloud ice mixing ratio.
- real(r8), pointer :: dnlf(:,:) ! detrained convective cloud water num concen.
- real(r8), pointer :: dnif(:,:) ! detrained convective cloud ice num concen.
real(r8), pointer :: lambdadpcu(:,:) ! slope of cloud liquid size distr
real(r8), pointer :: mudpcu(:,:) ! width parameter of droplet size distr
real(r8), pointer :: mconzm(:,:) !convective mass fluxes
@@ -475,10 +456,13 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
real(r8) :: pcont(pcols), pconb(pcols), freqzm(pcols)
+ real(r8) :: lat_all(pcols), long_all(pcols)
+
! history output fields
real(r8) :: cape(pcols) ! w convective available potential energy.
real(r8) :: mu_out(pcols,pver)
real(r8) :: md_out(pcols,pver)
+ real(r8) :: dif(pcols,pver)
! used in momentum transport calculation
real(r8) :: pguallu(pcols, pver)
@@ -490,17 +474,18 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
real(r8) :: icwdu(pcols,pver)
real(r8) :: icwdv(pcols,pver)
real(r8) :: seten(pcols, pver)
- logical :: l_windt(2)
+ logical :: l_windt
real(r8) :: tfinal1, tfinal2
integer :: ii
- real(r8),pointer :: zm_org2d(:,:)
- real(r8),allocatable :: orgt_alloc(:,:), org_alloc(:,:)
-
- real(r8) :: zm_org2d_ncol(state%ncol,pver)
- real(r8) :: orgt_ncol(state%ncol,pver), org_ncol(state%ncol,pver)
+ real(r8) :: fice(pcols,pver)
+ real(r8) :: fsnow_conv(pcols,pver)
logical :: lq(pcnst)
+ character(len=16) :: macrop_scheme
+ character(len=40) :: scheme_name
+ character(len=40) :: str
+ integer :: top_lev
!----------------------------------------------------------------------
@@ -517,9 +502,6 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
lq(:) = .FALSE.
lq(1) = .TRUE.
- if (zmconv_org) then
- lq(ixorg) = .TRUE.
- endif
call physics_ptend_init(ptend_loc, state%psetcols, 'zm_convr_run', ls=.true., lq=lq)! initialize local ptend type
!
@@ -547,35 +529,22 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
call pbuf_get_field(pbuf, zm_ideep_idx, ideep)
call pbuf_get_field(pbuf, dlfzm_idx, dlf)
- call pbuf_get_field(pbuf, difzm_idx, dif)
call pbuf_get_field(pbuf, mconzm_idx, mconzm)
- allocate(dnlf(pcols,pver), dnif(pcols,pver))
-
-!
! Begin with Zhang-McFarlane (1996) convection parameterization
!
call t_startf ('zm_convr_run')
- if (zmconv_org) then
- allocate(zm_org2d(pcols,pver))
- allocate(org_alloc(ncol,pver))
- allocate(orgt_alloc(ncol,pver))
- org_ncol(:ncol,:) = state%q(1:ncol,:,ixorg)
- endif
-
!REMOVECAM - no longer need these when CAM is retired and pcols no longer exists
ptend_loc%q(:,:,1) = 0._r8
ptend_loc%s(:,:) = 0._r8
+ dif(:,:) = 0._r8
mcon(:,:) = 0._r8
dlf(:,:) = 0._r8
cme(:,:) = 0._r8
cape(:) = 0._r8
zdu(:,:) = 0._r8
rprd(:,:) = 0._r8
- dif(:,:) = 0._r8
- dnlf(:,:) = 0._r8
- dnif(:,:) = 0._r8
mu(:,:) = 0._r8
eu(:,:) = 0._r8
du(:,:) = 0._r8
@@ -591,29 +560,27 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
ideep(:) = 0._r8
!REMOVECAM_END
-!CACNOTE - Need to check errflg and report errors
+
+ call get_rlat_all_p(lchnk, ncol, lat_all)
+ call get_rlon_all_p(lchnk, ncol, long_all)
+
call zm_convr_run(ncol, pver, &
pverp, gravit, latice, cpwv, cpliq, rh2o, &
+ lat_all, long_all, &
state%t(:ncol,:), state%q(:ncol,:,1), prec(:ncol), &
pblh(:ncol), state%zm(:ncol,:), state%phis(:ncol), state%zi(:ncol,:), ptend_loc%q(:ncol,:,1), &
ptend_loc%s(:ncol,:), state%pmid(:ncol,:), state%pint(:ncol,:), state%pdel(:ncol,:), &
ztodt, mcon(:ncol,:), cme(:ncol,:), cape(:ncol), &
- tpert(:ncol), dlf(:ncol,:), zdu(:ncol,:), rprd(:ncol,:), &
+ tpert(:ncol), dlf(:ncol,:), dif(:ncol,:), zdu(:ncol,:), rprd(:ncol,:), &
mu(:ncol,:), md(:ncol,:), du(:ncol,:), eu(:ncol,:), ed(:ncol,:), &
dp(:ncol,:), dsubcld(:ncol), jt(:ncol), maxg(:ncol), ideep(:ncol), &
ql(:ncol,:), rliq(:ncol), landfrac(:ncol), &
- org_ncol(:ncol,:), orgt_ncol(:ncol,:), zm_org2d_ncol(:ncol,:), &
- dif(:ncol,:), dnlf(:ncol,:), dnif(:ncol,:), &
- rice(:ncol), errmsg, errflg)
+ rice(:ncol), lengath, scheme_name, errmsg, errflg)
-
- if (zmconv_org) then
- ptend_loc%q(:,:,ixorg)=orgt_ncol(:ncol,:)
- zm_org2d(:ncol,:) = zm_org2d_ncol(:ncol,:)
- endif
-
- lengath = count(ideep > 0)
- if (lengath > ncol) lengath = ncol ! should not happen, but force it to not be larger than ncol for safety sake
+ if (errflg /= 0) then
+ write(str,*) 'From zm_convr_run: at chunk ',lchnk, ' : '
+ call endrun(str // errmsg)
+ end if
jctop(:) = real(pver,r8)
jcbot(:) = 1._r8
@@ -657,7 +624,6 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
call outfld('ZMDQ ',ptend_loc%q(1,1,1) ,pcols ,lchnk )
call t_stopf ('zm_convr_run')
- call outfld('DIFZM' ,dif ,pcols, lchnk)
call outfld('DLFZM' ,dlf ,pcols, lchnk)
pcont(:ncol) = state%ps(:ncol)
@@ -683,9 +649,6 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
! initialize ptend for next process
lq(:) = .FALSE.
lq(1) = .TRUE.
- if (zmconv_org) then
- lq(ixorg) = .TRUE.
- endif
call physics_ptend_init(ptend_loc, state1%psetcols, 'zm_conv_evap_run', ls=.true., lq=lq)
call t_startf ('zm_conv_evap_run')
@@ -702,25 +665,28 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
flxprec(:,:) = 0._r8
flxsnow(:,:) = 0._r8
snow(:) = 0._r8
+ fice(:,:) = 0._r8
+ fsnow_conv(:,:) = 0._r8
!REMOVECAM_END
+ top_lev = 1
+ call phys_getopts (macrop_scheme_out = macrop_scheme)
+ if ( .not. (macrop_scheme == "rk" .or. macrop_scheme == "SPCAM_sam1mom")) top_lev = trop_cloud_top_lev
+
+ call cloud_fraction_fice_run(ncol, state1%t(:ncol,:), tmelt, top_lev, pver, fice(:ncol,:), fsnow_conv(:ncol,:))
+
call zm_conv_evap_run(state1%ncol, pver, pverp, &
gravit, latice, latvap, tmelt, &
- cpair, zmconv_ke, zmconv_ke_lnd, zmconv_org, &
+ cpair, zmconv_ke, zmconv_ke_lnd, &
state1%t(:ncol,:),state1%pmid(:ncol,:),state1%pdel(:ncol,:),state1%q(:ncol,:pver,1), &
landfrac(:ncol), &
ptend_loc%s(:ncol,:), tend_s_snwprd(:ncol,:), tend_s_snwevmlt(:ncol,:), ptend_loc%q(:ncol,:pver,1), &
rprd(:ncol,:), cld(:ncol,:), ztodt, &
- prec(:ncol), snow(:ncol), ntprprd(:ncol,:), ntsnprd(:ncol,:), flxprec(:ncol,:), flxsnow(:ncol,:) )
+ prec(:ncol), snow(:ncol), ntprprd(:ncol,:), ntsnprd(:ncol,:), fsnow_conv(:ncol,:), flxprec(:ncol,:), flxsnow(:ncol,:),&
+ scheme_name, errmsg, errflg)
evapcdp(:ncol,:pver) = ptend_loc%q(:ncol,:pver,1)
- if (zmconv_org) then
- ptend_loc%q(:ncol,:pver,ixorg) = min(1._r8,max(0._r8,(50._r8*1000._r8*1000._r8*abs(evapcdp(:ncol,:pver))) &
- -(state%q(:ncol,:pver,ixorg)/10800._r8)))
- ptend_loc%q(:ncol,:pver,ixorg) = (ptend_loc%q(:ncol,:pver,ixorg) - state%q(:ncol,:pver,ixorg))/ztodt
- endif
-
!
! Write out variables from zm_conv_evap_run
!
@@ -755,8 +721,7 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
call physics_ptend_init(ptend_loc, state1%psetcols, 'zm_conv_momtran_run', ls=.true., lu=.true., lv=.true.)
- l_windt(1) = .true.
- l_windt(2) = .true.
+ l_windt = .true.
!REMOVECAM - no longer need these when CAM is retired and pcols no longer exists
ptend_loc%s(:,:) = 0._r8
ptend_loc%u(:,:) = 0._r8
@@ -765,15 +730,16 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
call t_startf ('zm_conv_momtran_run')
- call zm_conv_momtran_run (ncol, pver, pverp, &
- l_windt,state1%u(:ncol,:), state1%v(:ncol,:), 2, mu(:ncol,:), md(:ncol,:), &
+ call zm_conv_momtran_run (ncol, pver, pverp, &
+ l_windt,state1%u(:ncol,:), state1%v(:ncol,:), mu(:ncol,:), md(:ncol,:), &
zmconv_momcu, zmconv_momcd, &
du(:ncol,:), eu(:ncol,:), ed(:ncol,:), dp(:ncol,:), dsubcld(:ncol), &
jt(:ncol), maxg(:ncol), ideep(:ncol), 1, lengath, &
nstep, ptend_loc%u(:ncol,:), ptend_loc%v(:ncol,:),&
pguallu(:ncol,:), pguallv(:ncol,:), pgdallu(:ncol,:), pgdallv(:ncol,:), &
- icwuu(:ncol,:), icwuv(:ncol,:), icwdu(:ncol,:), icwdv(:ncol,:), ztodt, seten(:ncol,:) )
- call t_stopf ('zm_conv_momtran_run')
+ icwuu(:ncol,:), icwuv(:ncol,:), icwdu(:ncol,:), icwdv(:ncol,:), ztodt, seten(:ncol,:), &
+ scheme_name, errmsg, errflg)
+ call t_stopf ('zm_conv_momtran_run')
ptend_loc%s(:ncol,:pver) = seten(:ncol,:pver)
@@ -786,12 +752,8 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
! update physics state type state1 with ptend_loc
call physics_update(state1, ptend_loc, ztodt)
- ftem(:ncol,:pver) = seten(:ncol,:pver)/cpair
- if (zmconv_org) then
- call outfld('ZM_ORG', state%q(:,:,ixorg), pcols, lchnk)
- call outfld('ZM_ORG2D', zm_org2d, pcols, lchnk)
- endif
- call outfld('ZMMTT', ftem , pcols, lchnk)
+ ftem(:ncol,:pver) = seten(:ncol,:pver)/cpair
+ call outfld('ZMMTT', ftem , pcols, lchnk)
! Output apparent force from pressure gradient
call outfld('ZMUPGU', pguallu, pcols, lchnk)
@@ -828,7 +790,8 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
ptend_loc%lq,state1%q(:ncol,:,:), pcnst, mu(:ncol,:), md(:ncol,:), &
du(:ncol,:), eu(:ncol,:), ed(:ncol,:), dp(:ncol,:), dsubcld(:ncol), &
jt(:ncol), maxg(:ncol), ideep(:ncol), 1, lengath, &
- nstep, fracis(:ncol,:,:), ptend_loc%q(:ncol,:,:), fake_dpdry(:ncol,:))
+ nstep, fracis(:ncol,:,:), ptend_loc%q(:ncol,:,:), fake_dpdry(:ncol,:), ccpp_const_props, &
+ scheme_name, errmsg, errflg)
call t_stopf ('convtran1')
call outfld('ZMDICE ',ptend_loc%q(1,1,ixcldice) ,pcols ,lchnk )
@@ -840,11 +803,7 @@ subroutine zm_conv_tend(pblh ,mcon ,cme , &
call physics_state_dealloc(state1)
call physics_ptend_dealloc(ptend_loc)
- if (zmconv_org) then
- deallocate(zm_org2d)
- end if
- deallocate(dnlf, dnif)
end subroutine zm_conv_tend
!=========================================================================================
@@ -856,6 +815,8 @@ subroutine zm_conv_tend_2( state, ptend, ztodt, pbuf)
use time_manager, only: get_nstep
use physics_buffer, only: pbuf_get_index, pbuf_get_field, physics_buffer_desc
use constituents, only: pcnst, cnst_is_convtran2
+ use ccpp_constituent_prop_mod, only: ccpp_const_props
+
! Arguments
type(physics_state), intent(in ) :: state ! Physics state variables
@@ -885,6 +846,11 @@ subroutine zm_conv_tend_2( state, ptend, ztodt, pbuf)
integer, pointer :: jt(:) ! (pcols)
integer, pointer :: maxg(:) ! (pcols)
integer, pointer :: ideep(:) ! (pcols)
+
+ character(len=40) :: scheme_name
+ character(len=512) :: errmsg
+ integer :: errflg
+
!-----------------------------------------------------------------------------------
@@ -928,7 +894,13 @@ subroutine zm_conv_tend_2( state, ptend, ztodt, pbuf)
ptend%lq,state%q(:ncol,:,:), pcnst, mu(:ncol,:), md(:ncol,:), &
du(:ncol,:), eu(:ncol,:), ed(:ncol,:), dp(:ncol,:), dsubcld(:ncol), &
jt(:ncol), maxg(:ncol), ideep(:ncol), 1, lengath, &
- nstep, fracis(:ncol,:,:), ptend%q(:ncol,:,:), dpdry(:ncol,:))
+ nstep, fracis(:ncol,:,:), ptend%q(:ncol,:,:), dpdry(:ncol,:), ccpp_const_props, &
+ scheme_name, errmsg, errflg)
+
+ if (errflg /= 0) then
+ call endrun('From zm_conv_convtran_run:' // errmsg)
+ end if
+
call t_stopf ('convtran2')
end if
diff --git a/src/physics/cam7/physpkg.F90 b/src/physics/cam7/physpkg.F90
index 160bd86764..43b55137f9 100644
--- a/src/physics/cam7/physpkg.F90
+++ b/src/physics/cam7/physpkg.F90
@@ -785,6 +785,7 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
! local variables
integer :: lchnk
integer :: ierr
+ integer :: ixq
logical :: history_budget ! output tendencies and state variables for
! temperature, water vapor, cloud
@@ -955,7 +956,8 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
! Initialize CAM CCPP constituent properties array
! for use in CCPP-ized physics schemes:
- call ccpp_const_props_init()
+ call cnst_get_ind('Q', ixq)
+ call ccpp_const_props_init(ixq)
! Initialize qneg3 and qneg4
call qneg_init()
diff --git a/src/physics/rrtmgp/rad_solar_var.F90 b/src/physics/rrtmgp/rad_solar_var.F90
new file mode 100644
index 0000000000..ab608db7f9
--- /dev/null
+++ b/src/physics/rrtmgp/rad_solar_var.F90
@@ -0,0 +1,149 @@
+!-------------------------------------------------------------------------------
+! This module uses the solar irradiance data
+! to provide a spectral scaling factor
+! to approximate the spectral distribution of irradiance
+! when the radiation scheme might use a different solar source function
+!-------------------------------------------------------------------------------
+module rad_solar_var
+
+ use shr_kind_mod , only : r8 => shr_kind_r8
+ use radconstants, only : nswbands, get_sw_spectral_boundaries, band2gpt_sw
+ use solar_irrad_data, only : sol_irrad, we, nbins, has_spectrum, sol_tsi
+ use solar_irrad_data, only : do_spctrl_scaling
+ use cam_abortutils, only : endrun
+ use error_messages, only : alloc_err
+
+ implicit none
+ save
+
+ private
+ public :: rad_solar_var_init
+ public :: get_variability
+
+ real(r8), allocatable :: irrad(:) ! solar irradiance at model timestep in each band
+
+ real(r8), allocatable :: radbinmax(:)
+ real(r8), allocatable :: radbinmin(:)
+
+!-------------------------------------------------------------------------------
+contains
+!-------------------------------------------------------------------------------
+
+ subroutine rad_solar_var_init( )
+
+ integer :: ierr
+ integer :: radmax_loc
+
+ if ( do_spctrl_scaling ) then
+
+ if ( .not.has_spectrum ) then
+ call endrun('rad_solar_var_init: solar input file must have irradiance spectrum')
+ endif
+
+ allocate (radbinmax(nswbands),stat=ierr)
+ if (ierr /= 0) then
+ call endrun('rad_solar_var_init: Error allocating space for radbinmax')
+ end if
+
+ allocate (radbinmin(nswbands),stat=ierr)
+ if (ierr /= 0) then
+ call endrun('rad_solar_var_init: Error allocating space for radbinmin')
+ end if
+
+ allocate (irrad(nswbands), stat=ierr)
+ if (ierr /= 0) then
+ call endrun('rad_solar_var_init: Error allocating space for irrad')
+ end if
+
+ call get_sw_spectral_boundaries(radbinmin, radbinmax, 'nm')
+
+ ! Make sure that the far-IR is included, even if radiation grid does not
+ ! extend that far down. 10^5 nm corresponds to a wavenumber of
+ ! 100 cm^-1.
+ radmax_loc = maxloc(radbinmax,1)
+ radbinmax(radmax_loc) = max(100000._r8,radbinmax(radmax_loc))
+
+ endif
+
+ end subroutine rad_solar_var_init
+
+!-------------------------------------------------------------------------------
+!-------------------------------------------------------------------------------
+
+ subroutine get_variability(toa_flux, sfac)
+
+ ! Arguments
+ real(r8), intent(in) :: toa_flux(:,:) ! TOA flux to be scaled (columns,gpts)
+ real(r8), intent(out) :: sfac(:,:) ! scaling factors (columns,gpts)
+
+ ! Local variables
+ integer :: i, j, istat, gpt_start, gpt_end, ncols
+ real(r8), allocatable :: scale(:)
+ character(len=*), parameter :: sub = 'get_variability'
+
+ if (do_spctrl_scaling) then
+
+ ! Determine target irradiance for each band
+ call integrate_spectrum(nbins, nswbands, we, radbinmin, radbinmax, sol_irrad, irrad)
+
+ ncols = size(toa_flux, 1)
+ allocate(scale(ncols), stat=istat)
+ call alloc_err(istat, sub, 'scale', ncols)
+
+ do i = 1, nswbands
+ gpt_start = band2gpt_sw(1,i)
+ gpt_end = band2gpt_sw(2,i)
+ scale = spread(irrad(i), 1, ncols) / sum(toa_flux(:, gpt_start:gpt_end), dim=2)
+ do j = gpt_start, gpt_end
+ sfac(:,j) = scale
+ end do
+ end do
+
+ else
+ sfac(:,:) = sol_tsi / spread(sum(toa_flux, 2), 2, size(toa_flux, 2))
+ end if
+ end subroutine get_variability
+
+
+!-------------------------------------------------------------------------------
+! private method.........
+!-------------------------------------------------------------------------------
+
+ subroutine integrate_spectrum( nsrc, ntrg, src_x, min_trg, max_trg, src, trg )
+
+ use mo_util, only : rebin
+
+ implicit none
+
+ !---------------------------------------------------------------
+ ! ... dummy arguments
+ !---------------------------------------------------------------
+ integer, intent(in) :: nsrc ! dimension source array
+ integer, intent(in) :: ntrg ! dimension target array
+ real(r8), intent(in) :: src_x(nsrc+1) ! source coordinates
+ real(r8), intent(in) :: max_trg(ntrg) ! target coordinates
+ real(r8), intent(in) :: min_trg(ntrg) ! target coordinates
+ real(r8), intent(in) :: src(nsrc) ! source array
+ real(r8), intent(out) :: trg(ntrg) ! target array
+
+ !---------------------------------------------------------------
+ ! ... local variables
+ !---------------------------------------------------------------
+ real(r8) :: trg_x(2), targ(1) ! target coordinates
+ integer :: i
+
+ do i = 1, ntrg
+
+ trg_x(1) = min_trg(i)
+ trg_x(2) = max_trg(i)
+
+ call rebin( nsrc, 1, src_x, trg_x, src(1:nsrc), targ(:) )
+ ! W/m2/nm --> W/m2
+ trg( i ) = targ(1)*(trg_x(2)-trg_x(1))
+
+ enddo
+
+
+ end subroutine integrate_spectrum
+
+end module rad_solar_var
diff --git a/src/physics/rrtmgp/radconstants.F90 b/src/physics/rrtmgp/radconstants.F90
index f490b81b7b..3d4b47d09e 100644
--- a/src/physics/rrtmgp/radconstants.F90
+++ b/src/physics/rrtmgp/radconstants.F90
@@ -26,6 +26,9 @@ module radconstants
logical :: wavenumber_boundaries_set = .false.
+! First and last g-point for each band.
+integer, public, protected :: band2gpt_sw(2,nswbands)
+
integer, public, protected :: nswgpts ! number of SW g-points
integer, public, protected :: nlwgpts ! number of LW g-points
@@ -104,6 +107,9 @@ subroutine set_wavenumber_bands(kdist_sw, kdist_lw)
wavenumber_low_shortwave = values(1,:)
wavenumber_high_shortwave = values(2,:)
+ ! First and last g-point for each SW band:
+ band2gpt_sw = kdist_sw%get_band_lims_gpoint()
+
! Indices into specific bands
idx_sw_diag = get_band_index_by_value('sw', 500.0_r8, 'nm')
idx_nir_diag = get_band_index_by_value('sw', 1000.0_r8, 'nm')
diff --git a/src/physics/rrtmgp/radiation.F90 b/src/physics/rrtmgp/radiation.F90
index bb1667b0ec..58a973a3f0 100644
--- a/src/physics/rrtmgp/radiation.F90
+++ b/src/physics/rrtmgp/radiation.F90
@@ -16,7 +16,6 @@ module radiation
pbuf_set_field, pbuf_get_field, pbuf_old_tim_idx
use camsrfexch, only: cam_out_t, cam_in_t
use physconst, only: cappa, cpair, gravit
-use solar_irrad_data, only: sol_tsi
use time_manager, only: get_nstep, is_first_step, is_first_restart_step, &
get_curr_calday, get_step_size
@@ -27,6 +26,7 @@ module radiation
use radconstants, only: nradgas, gasnamelength, gaslist, nswbands, nlwbands, &
nswgpts, set_wavenumber_bands
+use rad_solar_var, only: rad_solar_var_init, get_variability
use cloud_rad_props, only: cloud_rad_props_init
@@ -495,6 +495,7 @@ subroutine radiation_init(pbuf2d)
! Set the sw/lw band boundaries in radconstants. Also sets
! indicies of specific bands for diagnostic output and COSP input.
call set_wavenumber_bands(kdist_sw, kdist_lw)
+ call rad_solar_var_init()
! The spectral band boundaries need to be set before this init is called.
call rrtmgp_inputs_init(ktopcam, ktoprad)
@@ -937,8 +938,8 @@ subroutine radiation_tend( &
! TOA solar flux on RRTMGP g-points
real(r8), allocatable :: toa_flux(:,:)
- ! TSI from RRTMGP data (from sum over g-point representation)
- real(r8) :: tsi_ref
+ ! Scale factors based on spectral distribution from input irradiance dataset
+ real(r8), allocatable :: sfac(:,:)
! Planck sources for LW.
type(ty_source_func_lw) :: sources_lw
@@ -1097,6 +1098,7 @@ subroutine radiation_tend( &
allocate( &
t_sfc(ncol), emis_sfc(nlwbands,ncol), toa_flux(nday,nswgpts), &
+ sfac(nday,nswgpts), &
t_rad(ncol,nlay), pmid_rad(ncol,nlay), pint_rad(ncol,nlay+1), &
t_day(nday,nlay), pmid_day(nday,nlay), pint_day(nday,nlay+1), &
coszrs_day(nday), alb_dir(nswbands,nday), alb_dif(nswbands,nday), &
@@ -1174,8 +1176,8 @@ subroutine radiation_tend( &
call stop_on_err(errmsg, sub, 'kdist_sw%gas_optics')
! Scale the solar source
- tsi_ref = sum(toa_flux(1,:))
- toa_flux = toa_flux * sol_tsi * eccf / tsi_ref
+ call get_variability(toa_flux, sfac)
+ toa_flux = toa_flux * sfac * eccf
end if
@@ -1303,7 +1305,7 @@ subroutine radiation_tend( &
end if ! if (dolw)
deallocate( &
- t_sfc, emis_sfc, toa_flux, t_rad, pmid_rad, pint_rad, &
+ t_sfc, emis_sfc, toa_flux, sfac, t_rad, pmid_rad, pint_rad, &
t_day, pmid_day, pint_day, coszrs_day, alb_dir, alb_dif)
!================!
diff --git a/src/physics/simple/physpkg.F90 b/src/physics/simple/physpkg.F90
index 70a2f147d5..0bbb63dd7e 100644
--- a/src/physics/simple/physpkg.F90
+++ b/src/physics/simple/physpkg.F90
@@ -208,6 +208,8 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
use nudging, only: Nudge_Model, nudging_init
use cam_snapshot, only: cam_snapshot_init
use cam_budget, only: cam_budget_init
+ use constituents, only: cnst_get_ind
+
use ccpp_constituent_prop_mod, only: ccpp_const_props_init
@@ -220,7 +222,7 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
type(cam_out_t),intent(inout) :: cam_out(begchunk:endchunk)
! local variables
- integer :: lchnk
+ integer :: lchnk, ixq
!-----------------------------------------------------------------------
call physics_type_alloc(phys_state, phys_tend, begchunk, endchunk, pcols)
@@ -281,7 +283,8 @@ subroutine phys_init( phys_state, phys_tend, pbuf2d, cam_in, cam_out )
! Initialize CAM CCPP constituent properties array
! for use in CCPP-ized physics schemes:
- call ccpp_const_props_init()
+ call cnst_get_ind('Q', ixq)
+ call ccpp_const_props_init(ixq)
! Initialize qneg3 and qneg4
call qneg_init()
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,