From bcfaa79ba8e98cca676545d62e191b537d901edf Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 24 Jul 2025 11:06:20 -0400 Subject: [PATCH 001/112] added supplementation arguments to nutrient unpack routine --- src/utils/clmfates_interfaceMod.F90 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 289244ae89..026715e5c0 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -79,6 +79,7 @@ module CLMFatesInterfaceMod use clm_varctl , only : use_lch4 use clm_varctl , only : fates_history_dimlevel use clm_varctl , only : nsrest, nsrBranch + use clm_varctl , only : carbon_only use clm_varcon , only : tfrz use clm_varcon , only : spval use clm_varcon , only : denice @@ -1149,6 +1150,11 @@ subroutine dynamics_driv(this, nc, bounds_clump, & real(r8) :: s_node, smp_node ! local for relative water content and potential logical :: after_start_of_harvest_ts integer :: iharv + logical :: nitr_suppl ! Is CLM currently supplementing N + logical, parameter :: phos_dummy_suppl = .false. ! This argument is needed for FATES + ! to specify if phosphorus is being + ! supplemented, this is not cycled in CLM + ! so we set it to false !----------------------------------------------------------------------- ! --------------------------------------------------------------------------------- @@ -1330,10 +1336,16 @@ subroutine dynamics_driv(this, nc, bounds_clump, & end do + if(carbon_only)then + nitr_suppl = .true. + else + nitr_suppl = .false. + end if + ! Nutrient uptake fluxes have been accumulating with each short ! timestep, here, we unload them from the boundary condition ! structures into the cohort structures. - call UnPackNutrientAquisitionBCs(this%fates(nc)%sites, this%fates(nc)%bc_in) + call UnPackNutrientAquisitionBCs(this%fates(nc)%sites, this%fates(nc)%bc_in, nitr_suppl, phos_dummy_suppl) ! Distribute any seeds from neighboring gridcells into the current gridcell ! Global seed availability array populated by WrapGlobalSeedDispersal call From 4ece46d5b4214b6067983be1ef9b43443509140e Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 24 Jul 2025 11:09:18 -0400 Subject: [PATCH 002/112] set phosphorus supplementation status to true regarding fates --- src/utils/clmfates_interfaceMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 026715e5c0..854ae68386 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -1151,10 +1151,11 @@ subroutine dynamics_driv(this, nc, bounds_clump, & logical :: after_start_of_harvest_ts integer :: iharv logical :: nitr_suppl ! Is CLM currently supplementing N - logical, parameter :: phos_dummy_suppl = .false. ! This argument is needed for FATES + logical, parameter :: phos_dummy_suppl = .true . ! This argument is needed for FATES ! to specify if phosphorus is being ! supplemented, this is not cycled in CLM - ! so we set it to false + ! so we set it to TRUE (which essentially + ! means it is NOT limiting) !----------------------------------------------------------------------- ! --------------------------------------------------------------------------------- From 8cd660c5e312aa45978468dc864f90a6c80d2836 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 24 Jul 2025 10:32:42 -0600 Subject: [PATCH 003/112] fixed typos and carbon_only ref was changed to the getter function --- src/utils/clmfates_interfaceMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 854ae68386..23e063696c 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -79,7 +79,7 @@ module CLMFatesInterfaceMod use clm_varctl , only : use_lch4 use clm_varctl , only : fates_history_dimlevel use clm_varctl , only : nsrest, nsrBranch - use clm_varctl , only : carbon_only + use clm_varctl , only : CNAllocate_Carbon_only use clm_varcon , only : tfrz use clm_varcon , only : spval use clm_varcon , only : denice @@ -1151,7 +1151,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & logical :: after_start_of_harvest_ts integer :: iharv logical :: nitr_suppl ! Is CLM currently supplementing N - logical, parameter :: phos_dummy_suppl = .true . ! This argument is needed for FATES + logical, parameter :: phos_dummy_suppl = .true. ! This argument is needed for FATES ! to specify if phosphorus is being ! supplemented, this is not cycled in CLM ! so we set it to TRUE (which essentially @@ -1337,7 +1337,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & end do - if(carbon_only)then + if(CNAllocate_Carbon_only())then nitr_suppl = .true. else nitr_suppl = .false. From 2e5e87c7c4ecb91e9f28acf2de4c0dc9d56b8132 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Fri, 25 Jul 2025 17:59:52 -0600 Subject: [PATCH 004/112] For ctsm5.4 replace WhatsNewInCTSM5.3.md with WhatsNewInCTSM5.4.md I used WhatsNewInCTSM5.3.md as the starting point for WhatsNewInCTSM5.4.md and will start modifying in subsequent commits --- WhatsNewInCTSM5.3.md => WhatsNewInCTSM5.4.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WhatsNewInCTSM5.3.md => WhatsNewInCTSM5.4.md (100%) diff --git a/WhatsNewInCTSM5.3.md b/WhatsNewInCTSM5.4.md similarity index 100% rename from WhatsNewInCTSM5.3.md rename to WhatsNewInCTSM5.4.md From eadb1f77deeac4d398448aa79b19eeae57115675 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 28 Jul 2025 18:39:52 -0600 Subject: [PATCH 005/112] Draft WhatNewinCTSM54_release_notes (mostly deletes, some additions) --- WhatsNewInCTSM5.4.md | 98 ++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 68 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index 4717deac30..a6282c8d34 100644 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -1,108 +1,70 @@ -# What's new in CTSM 5.3 (tag `ctsm5.3.021`) +# What's new in CTSM 5.4 (tag `ctsm5.4.0xx`) -## Purpose and description of changes since CTSM 5.2 (tag `ctsm5.2.005`) +## Purpose and description of changes since CTSM 5.3 (tag `ctsm5.3.021`) ### New features -* `manage_externals` replaced by [`git-fleximod`](https://github.com/ESMCI/git-fleximod/blob/main/README.md). ([PR \#2559](https://github.com/ESCOMP/CTSM/pull/2559)) -* No longer runs the 0th time step in first segment of startup and hybrid runs; branch and continue runs never had this 0th time step. ([PR \#2084](https://github.com/ESCOMP/CTSM/pull/2084)) -* New CN Matrix method speeds up spinup for the BGC model. ([PR \#640](https://github.com/ESCOMP/CTSM/pull/640); [Liao et al. 2023](https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2023MS003625)). -* New `Leung_2023` dust emissions. ([PR \#1897](https://github.com/ESCOMP/CTSM/pull/1897); [Leung et al 2023](https://doi.org/10.5194/acp-23-6487-2023), [Leung et al. 2024](https://doi.org/10.5194/acp-24-2287-2024)) -* Explicit air conditioning for the urban model. ([PR \#2275](https://github.com/ESCOMP/CTSM/pull/2275); [Li et al. 2024](https://agupubs.onlinelibrary.wiley.com/share/NY4AYPREB8Y8BUDP7DXD?target=10.1029/2023MS004107)) -* FATES compsets can now be run with transient land use; off by default. ([PR \#2507](https://github.com/ESCOMP/CTSM/pull/2507)) -* Ability to handle CAM7 in `LND_TUNING_MODE`. ([PR \#2632](https://github.com/ESCOMP/CTSM/pull/2632)) +* ### Answer changes Changes to defaults for `clm6_0` physics: -* Urban explicit A/C turned on (links above). -* Snow thermal conductivity method is now `Sturm1997`. ([PR \#2348](https://github.com/ESCOMP/CTSM/pull/2348); see also [discussion \#1960](https://github.com/ESCOMP/CTSM/discussions/1960)) * New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000) resolutions. -* New crop calendars. ([PR \#2664](https://github.com/ESCOMP/CTSM/pull/2664); informed by [Rabin et al., 2023](https://gmd.copernicus.org/articles/16/7253/2023/gmd-16-7253-2023.html)) -* Dust emissions method is now `Leung_2023` (links above). -* Excess ice is turned on. ([PR \#1787](https://github.com/ESCOMP/CTSM/pull/1787)) -* Updates to MEGAN for BVOCs. ([PR \#2588](https://github.com/ESCOMP/CTSM/pull/2588)) -* New BGC fire method `li2024crujra`: Avoid crop fires during growing season; allow lightning ignitions in tropical closed forests; add effect of landscape fragmentation on ignitions and duration; recalibrate against GFED5 burned area and with CRU-JRA climate. ([PR \#2684](https://github.com/ESCOMP/CTSM/pull/2684), [PR \#2711](https://github.com/ESCOMP/CTSM/pull/2711), [PR \#2715](https://github.com/ESCOMP/CTSM/issues/2715)) +* Updates to MEGAN for BVOCs. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) Changes for all physics versions: -* Parameters updated for CRU-JRA forcing: PPE-based modifications were made to the parameters `leafcn`, `slatop`, `froot_leaf`, `medlynslope`, and `kmax`. Lowers LAI and biomass in boreal and tropical forests, without reducing latent heat in the tropics. Affected PFTs: NET temperate, NET boreal, BET tropical, BDS boreal, C3 arctic grass. ([PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) -* FATES parameter file updated (see section below). -* Pass active glacier (CISM) runoff directly to river model (MOSART) ([MOSART PR \#94](https://github.com/ESCOMP/MOSART/pull/94)) +* Parameters updated: PPE-based modifications were made to the parameters `xxxx`. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) +* FATES parameter file updated? * New surface datasets and landuse timeseries files (see section below). -* CNMatrix is new default spinup method (links above). ### Heads up -* Small glacier changes mean that you can’t use a 5.3 surface dataset with pre-5.3 code and vice versa anymore. (Merged with [PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) -* Updates the definition of history variable “time” from *end* of `time_bounds` to *middle* of `time_bounds`. ([PR \#2838](https://github.com/ESCOMP/CTSM/pull/2838); see section below) -* Standardizes history variable attributes and a history dimension name. ([PR \#2052](https://github.com/ESCOMP/CTSM/pull/2052); see section below) +* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details below and in the PRs [\#2445](https://github.com/ESCOMP/ctsm/pull/2445) [\#117](https://github.com/ESCOMP/MOSART/pull/117) [\#61](https://github.com/ESCOMP/RTM/pull/61) and the correspondng issues. ## ## Additional detail -### Changes related to time and history files +### Changes related to history files (Note that the same information in this section applies to MOSART and RTM.) -Startup and hybrid runs no longer run the 0th time step, consistent with the same change in CAM. (Branch and continue runs never had this 0th time step.) This means you will not get an extraneous initial history file anymore. In some circumstances this may also affect the names of history files. +Following ctsm5.3.018 "Change history time to be the middle of the time bounds" and to keep CLM history consistent with CAM history, the CTSM5.4 change intends to prevent confusion associated with the time corresponding to instantaneous history fields by putting them on separate files than non-instantaneous fields. The result is -In most cases, the history `time` variable is now defined as the middle of a history file’s `time_bounds` instead of the end, for consistency with the same change in CAM. The exception is if you specify `hist_avgflag_pertape = 'I'` for that file, in which case it will be treated as an “instantaneous” file. Instantaneous history files (a) have their `time` coordinate set to the end of the last timestep (as did all history files before this tag) and (b) do not include `time_bounds`. +1) two history files per clm, mosart, and rtm history tape: + tape h0 becomes h0a and h0i + tape h1 becomes h1a and h1i + ... + tape hX becomes hXa and hXi -The history dimension name `hist_interval` (of output variable `time_bounds`) is standardized to be `nbnd`. History variables `time_bounds`, `mcdate`, `mcsec`, `mdcur`, and `mscur` are standardized to include the calendar attribute. +2) two history restart files per history restart tape: + rh0 becomes rh0a and rh0i + rh1 becomes rh1a and rh1i + ... + rhX becomes rhXa and rhXi -### New surface datasets and landuse timeseries files ([PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) +The CLM handles empty history (and corresponding history restart) files by not generating them, while rtm and mosart give an error. Instead of refactoring rtm and mosart to behave like the clm (considered out of scope), we have introduced one active instantaneous field in mosart and one in rtm to bypass the "empty file" error. -* Transient landuse timeseries files going back to 1700 now possible (and made for f09). -* Fix an important bug on soil fields that was there since `ctsm5.2.0`. This has the side effect of `mksurfdata_esmf` now giving identical answers with a change in number of processors, as it should. ([Issue \#2744](https://github.com/ESCOMP/CTSM/issues/2744)) -* Surface datasets now provided for the `ne0np4.POLARCAP.ne30x4` grid. ([PR \#2716](https://github.com/ESCOMP/CTSM/pull/2716), [issue \#2720](https://github.com/ESCOMP/CTSM/issues/2720)) -* Surface datasets now have their version number embedded to prevent mismatch of surface dataset and CTSM version. ([Issue \#2723](https://github.com/ESCOMP/CTSM/issues/2723)) -* Remove outdated hydrology `VIC` (Variable Infiltration Capacity Hydrology model) fields from surface datasets. +### New surface datasets and landuse timeseries files ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) + +* Transient landuse timeseries files going back to 1700 made for f09. +* Surface datasets now provided for the `xxxx` grid. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx), [issue \#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx)) * Updates to input datasets: - * PFT/LAI/soil-color raw datasets; now from the TRENDY2024 timeseries that ends in 2023. (Issues [\#2570](https://github.com/ESCOMP/CTSM/issues/2570) and [\#2452](https://github.com/ESCOMP/CTSM/issues/2452)) - * Two fire datasets: crop fire peak month and peatland fraction. (Issue [\#2618](https://github.com/ESCOMP/CTSM/issues/2618)) - * Glacier behavior dataset (related to how non-Greenland glaciers are handled). (Issue [\#423](https://github.com/ESCOMP/CTSM/issues/423)) + * PFT/LAI/soil-color raw datasets; now from the xxxx timeseries that ends in 2023. (Issues [\#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx) + * Two fire datasets: crop fire peak month and population density. (Issue [\#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx)) ### Changes to FATES parameter file -* [PR \#2507](https://github.com/ESCOMP/CTSM/pull/2507) ([ctsm5.2.013](https://github.com/ESCOMP/CTSM/releases/tag/ctsm5.2.013)) / [FATES PR \#1116](https://github.com/NGEET/fates/pull/1116) ([sci.1.77.1\_api.36.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.77.0_api.36.0.0)) - * Adds new parameters for new land use harvest mode and land use by PFT capabilities -* [PR \#2700](https://github.com/ESCOMP/CTSM/pull/2700) ([ctsm5.3.003](https://github.com/ESCOMP/CTSM/releases/tag/ctsm5.3.003)) / [FATES PR \#1255](https://github.com/NGEET/fates/pull/1255) ([sci.1.78.3\_api.36.1.0](https://github.com/NGEET/fates/releases/tag/sci.1.78.3_api.36.1.0)) - * Adds two arctic shrub PFTs, increasing the number of default PFTs to 14 and update arctic grass parameters ([FATES PR\#1236](https://github.com/NGEET/fates/pull/1236)) - * Splits `fates_turnover_leaf` parameter into canopy and understory specific turnover rates and provides new values for both ([FATES PR\#1136](https://github.com/NGEET/fates/pull/1136)) - * Updates grass allometry parameters ([FATES PR\#1206](https://github.com/NGEET/fates/pull/1206)) - * Changes the prescribe nutrient uptake defaults from 1 to 0 for all PFTs (only relevant to ELM-FATES) + * ### Changes to rpointer files -The rpointer files are simple text files that CESM uses to keep track of how far simulations have progressed, pointing to the filename of the latest restart file for that component. There is one such file for each component, so for CTSM `I` cases that's `lnd`, `cpl`, and `atm` (and `rof` if it's active). Normally, when the user is just extending the length of simulations, there’s no need to worry about these files. - -However, if there was a problem when a simulation shut down, it's possible that different components will have mismatched restarts and rpointer files. In the past, this meant figuring out what restart file should be pointed to in each component rpointer file and correcting it by hand in an editor. There was only the final set of rpointer files that was kept for a case. - -Now, with this update, the `lnd`, `cpl`, and `atm` rpointer files have the simulation date in the filenames, so it's easy to spot if the restarts are mismatched for one of the components. Also, since there are matching rpointer files for each time restarts are created, it's now easier to (a) make sure restarts and rpointer files are all correctly matched and (b) for a user to take a set of restarts and matching rpointer files to start up from for any part of an existing simulation. This means you don't have to hand-edit the rpointer files, making sure you don't make a mistake when you do. - -Old rpointer filenames: - -* `rpointer.atm` -* `rpointer.cpl` -* `rpointer.lnd` - -New names: - -* `rpointer.atm.YYYY-MM-DD-SSSSS` -* `rpointer.cpl.YYYY-MM-DD-SSSSS` -* `rpointer.lnd.YYYY-MM-DD-SSSSS` - -Where `YYYY-MM-DD-SSSSS` is the year, month, day, and seconds into the day for the simulation timestamp. For example, `rpointer.lnd.2000-01-01-00000` for a rpointer file for starting at midnight (beginning of the day) on January 1st, 2000\. - -Note that this is backwards-compatible, so for all the components you can use either the new or old format for the rpointer filenames. Thus, if you are restarting from an existing case before `ctsm5.3.016`, you CAN use the rpointer filenames that don't have the timestamps in the name. ## Simulations supporting this release -- f19 `Clm60Bgc` 16pft: [https://github.com/NCAR/LMWG\_dev/issues/70](https://github.com/NCAR/LMWG_dev/issues/70) -- f09 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/69](https://github.com/NCAR/LMWG_dev/issues/69) -- ne30 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/68](https://github.com/NCAR/LMWG_dev/issues/68) +- f19 `Clm60Bgc` 16pft: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) +- f09 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) +- ne30 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) -Note: Dust emissions in CTSM 5.3 will be different from the above simulations because of a tuning update that came in after those. \ No newline at end of file From 50c7143053a0e9d2c228442563d3d8358c7a1fb1 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Tue, 29 Jul 2025 13:50:50 -0600 Subject: [PATCH 006/112] Draft WhatsNewInCTSM5.4.md with ChangeLog info from ctsm5.3.022 to .065 --- WhatsNewInCTSM5.4.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index a6282c8d34..62e1e45416 100644 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -2,21 +2,32 @@ ## Purpose and description of changes since CTSM 5.3 (tag `ctsm5.3.021`) +REMOVE THESE NOTES WHEN DONE AND READY TO RELEASE +- As of 2025/7/29 slevis has gone through the ChangeLog from cctsm5.3.022 to ctsm5.3.065 +- Ask for reviewers to browse/read this doc for accuracy, omissions, and redundancies +- For omissions, request contributions from the relevant developers + ### New features -* +* Can now choose the CRUJRA2024 atmospheric driver data with clm6 and clm5 [PR \#2956](https://github.com/ESCOMP/ctsm/pull/2956)) +* Can now run PLUMBER towers, similar to the NEON tower capability ([issue \#1487](https://github.com/ESCOMP/CTSM/issues/1487)) +* New namelist variables `snow_thermal_cond_glc_method` and `snow_thermal_cond_lake_method` ([PR \#3072](https://https://github.com/ESCOMP/CTSM/pull/3072)) +* Potentially time-evolving `leafcn_target` replaces time-constant `leafcn`: the former is calculated as a function of the latter and can be time-evolving depending on new paramfile parameter `leafcn_co2_slope` ([PR \#1654](https://github.com/ESCOMP/ctsm/pull/1654) +* Easier, more flexible use of anomaly forcings for ISSP cases ([PR \#2686](https://github.com/ESCOMP/CTSM/pull/2686) [PR \#3212](https://github.com/ESCOMP/CTSM/pull/3212)) +* New equilibrium script in /tools/contrib for spectral element grids ([PR \#2991](https://github.com/ESCOMP/ctsm/pull/2991)) ### Answer changes Changes to defaults for `clm6_0` physics: -* New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000) resolutions. -* Updates to MEGAN for BVOCs. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) +* Bytnerowicz is now the default nfix_method for clm6 ([PR \#2972](https://github.com/ESCOMP/ctsm/pull/2972)) +* New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000) resolutions? +* Updates to MEGAN for BVOCs ([PR \#3065](https://github.com/ESCOMP/CTSM/pull/3065) [PR \#3309](https://github.com/ESCOMP/CTSM/pull/3309)) Changes for all physics versions: -* Parameters updated: PPE-based modifications were made to the parameters `xxxx`. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) -* FATES parameter file updated? +* Parameters updated: Added MIMICS parameter `mimics_fi`. ([PR \#2365](https://github.com/ESCOMP/CTSM/pull/2365)) +* FATES parameter file updated: ([PR \#2965](https://github.com/ESCOMP/CTSM/pull/2965) [PR \#2904](https://github.com/ESCOMP/CTSM/pull/2904) [PR \#1344](https://https://github.com/NGEET/fates/pull/1344) [PR \#3087](https://github.com/ESCOMP/CTSM/pull/3087)) * New surface datasets and landuse timeseries files (see section below). ### Heads up From 5510a7e3569c0ad6770097cc58e1a92d02b9adf7 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Tue, 26 Aug 2025 18:09:59 -0600 Subject: [PATCH 007/112] Bring back WhatsNewInCTSM5.3.md but in the /doc directory --- doc/WhatsNewInCTSM5.3.md | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 doc/WhatsNewInCTSM5.3.md diff --git a/doc/WhatsNewInCTSM5.3.md b/doc/WhatsNewInCTSM5.3.md new file mode 100644 index 0000000000..4717deac30 --- /dev/null +++ b/doc/WhatsNewInCTSM5.3.md @@ -0,0 +1,108 @@ +# What's new in CTSM 5.3 (tag `ctsm5.3.021`) + +## Purpose and description of changes since CTSM 5.2 (tag `ctsm5.2.005`) + +### New features + +* `manage_externals` replaced by [`git-fleximod`](https://github.com/ESMCI/git-fleximod/blob/main/README.md). ([PR \#2559](https://github.com/ESCOMP/CTSM/pull/2559)) +* No longer runs the 0th time step in first segment of startup and hybrid runs; branch and continue runs never had this 0th time step. ([PR \#2084](https://github.com/ESCOMP/CTSM/pull/2084)) +* New CN Matrix method speeds up spinup for the BGC model. ([PR \#640](https://github.com/ESCOMP/CTSM/pull/640); [Liao et al. 2023](https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2023MS003625)). +* New `Leung_2023` dust emissions. ([PR \#1897](https://github.com/ESCOMP/CTSM/pull/1897); [Leung et al 2023](https://doi.org/10.5194/acp-23-6487-2023), [Leung et al. 2024](https://doi.org/10.5194/acp-24-2287-2024)) +* Explicit air conditioning for the urban model. ([PR \#2275](https://github.com/ESCOMP/CTSM/pull/2275); [Li et al. 2024](https://agupubs.onlinelibrary.wiley.com/share/NY4AYPREB8Y8BUDP7DXD?target=10.1029/2023MS004107)) +* FATES compsets can now be run with transient land use; off by default. ([PR \#2507](https://github.com/ESCOMP/CTSM/pull/2507)) +* Ability to handle CAM7 in `LND_TUNING_MODE`. ([PR \#2632](https://github.com/ESCOMP/CTSM/pull/2632)) + +### Answer changes + +Changes to defaults for `clm6_0` physics: + +* Urban explicit A/C turned on (links above). +* Snow thermal conductivity method is now `Sturm1997`. ([PR \#2348](https://github.com/ESCOMP/CTSM/pull/2348); see also [discussion \#1960](https://github.com/ESCOMP/CTSM/discussions/1960)) +* New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000) resolutions. +* New crop calendars. ([PR \#2664](https://github.com/ESCOMP/CTSM/pull/2664); informed by [Rabin et al., 2023](https://gmd.copernicus.org/articles/16/7253/2023/gmd-16-7253-2023.html)) +* Dust emissions method is now `Leung_2023` (links above). +* Excess ice is turned on. ([PR \#1787](https://github.com/ESCOMP/CTSM/pull/1787)) +* Updates to MEGAN for BVOCs. ([PR \#2588](https://github.com/ESCOMP/CTSM/pull/2588)) +* New BGC fire method `li2024crujra`: Avoid crop fires during growing season; allow lightning ignitions in tropical closed forests; add effect of landscape fragmentation on ignitions and duration; recalibrate against GFED5 burned area and with CRU-JRA climate. ([PR \#2684](https://github.com/ESCOMP/CTSM/pull/2684), [PR \#2711](https://github.com/ESCOMP/CTSM/pull/2711), [PR \#2715](https://github.com/ESCOMP/CTSM/issues/2715)) + +Changes for all physics versions: + +* Parameters updated for CRU-JRA forcing: PPE-based modifications were made to the parameters `leafcn`, `slatop`, `froot_leaf`, `medlynslope`, and `kmax`. Lowers LAI and biomass in boreal and tropical forests, without reducing latent heat in the tropics. Affected PFTs: NET temperate, NET boreal, BET tropical, BDS boreal, C3 arctic grass. ([PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) +* FATES parameter file updated (see section below). +* Pass active glacier (CISM) runoff directly to river model (MOSART) ([MOSART PR \#94](https://github.com/ESCOMP/MOSART/pull/94)) +* New surface datasets and landuse timeseries files (see section below). +* CNMatrix is new default spinup method (links above). + +### Heads up + +* Small glacier changes mean that you can’t use a 5.3 surface dataset with pre-5.3 code and vice versa anymore. (Merged with [PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) +* Updates the definition of history variable “time” from *end* of `time_bounds` to *middle* of `time_bounds`. ([PR \#2838](https://github.com/ESCOMP/CTSM/pull/2838); see section below) +* Standardizes history variable attributes and a history dimension name. ([PR \#2052](https://github.com/ESCOMP/CTSM/pull/2052); see section below) + +## + +## Additional detail + +### Changes related to time and history files + +(Note that the same information in this section applies to MOSART and RTM.) + +Startup and hybrid runs no longer run the 0th time step, consistent with the same change in CAM. (Branch and continue runs never had this 0th time step.) This means you will not get an extraneous initial history file anymore. In some circumstances this may also affect the names of history files. + +In most cases, the history `time` variable is now defined as the middle of a history file’s `time_bounds` instead of the end, for consistency with the same change in CAM. The exception is if you specify `hist_avgflag_pertape = 'I'` for that file, in which case it will be treated as an “instantaneous” file. Instantaneous history files (a) have their `time` coordinate set to the end of the last timestep (as did all history files before this tag) and (b) do not include `time_bounds`. + +The history dimension name `hist_interval` (of output variable `time_bounds`) is standardized to be `nbnd`. History variables `time_bounds`, `mcdate`, `mcsec`, `mdcur`, and `mscur` are standardized to include the calendar attribute. + +### New surface datasets and landuse timeseries files ([PR \#2500](https://github.com/ESCOMP/CTSM/pull/2500)) + +* Transient landuse timeseries files going back to 1700 now possible (and made for f09). +* Fix an important bug on soil fields that was there since `ctsm5.2.0`. This has the side effect of `mksurfdata_esmf` now giving identical answers with a change in number of processors, as it should. ([Issue \#2744](https://github.com/ESCOMP/CTSM/issues/2744)) +* Surface datasets now provided for the `ne0np4.POLARCAP.ne30x4` grid. ([PR \#2716](https://github.com/ESCOMP/CTSM/pull/2716), [issue \#2720](https://github.com/ESCOMP/CTSM/issues/2720)) +* Surface datasets now have their version number embedded to prevent mismatch of surface dataset and CTSM version. ([Issue \#2723](https://github.com/ESCOMP/CTSM/issues/2723)) +* Remove outdated hydrology `VIC` (Variable Infiltration Capacity Hydrology model) fields from surface datasets. +* Updates to input datasets: + * PFT/LAI/soil-color raw datasets; now from the TRENDY2024 timeseries that ends in 2023. (Issues [\#2570](https://github.com/ESCOMP/CTSM/issues/2570) and [\#2452](https://github.com/ESCOMP/CTSM/issues/2452)) + * Two fire datasets: crop fire peak month and peatland fraction. (Issue [\#2618](https://github.com/ESCOMP/CTSM/issues/2618)) + * Glacier behavior dataset (related to how non-Greenland glaciers are handled). (Issue [\#423](https://github.com/ESCOMP/CTSM/issues/423)) + +### Changes to FATES parameter file + +* [PR \#2507](https://github.com/ESCOMP/CTSM/pull/2507) ([ctsm5.2.013](https://github.com/ESCOMP/CTSM/releases/tag/ctsm5.2.013)) / [FATES PR \#1116](https://github.com/NGEET/fates/pull/1116) ([sci.1.77.1\_api.36.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.77.0_api.36.0.0)) + * Adds new parameters for new land use harvest mode and land use by PFT capabilities +* [PR \#2700](https://github.com/ESCOMP/CTSM/pull/2700) ([ctsm5.3.003](https://github.com/ESCOMP/CTSM/releases/tag/ctsm5.3.003)) / [FATES PR \#1255](https://github.com/NGEET/fates/pull/1255) ([sci.1.78.3\_api.36.1.0](https://github.com/NGEET/fates/releases/tag/sci.1.78.3_api.36.1.0)) + * Adds two arctic shrub PFTs, increasing the number of default PFTs to 14 and update arctic grass parameters ([FATES PR\#1236](https://github.com/NGEET/fates/pull/1236)) + * Splits `fates_turnover_leaf` parameter into canopy and understory specific turnover rates and provides new values for both ([FATES PR\#1136](https://github.com/NGEET/fates/pull/1136)) + * Updates grass allometry parameters ([FATES PR\#1206](https://github.com/NGEET/fates/pull/1206)) + * Changes the prescribe nutrient uptake defaults from 1 to 0 for all PFTs (only relevant to ELM-FATES) + +### Changes to rpointer files + +The rpointer files are simple text files that CESM uses to keep track of how far simulations have progressed, pointing to the filename of the latest restart file for that component. There is one such file for each component, so for CTSM `I` cases that's `lnd`, `cpl`, and `atm` (and `rof` if it's active). Normally, when the user is just extending the length of simulations, there’s no need to worry about these files. + +However, if there was a problem when a simulation shut down, it's possible that different components will have mismatched restarts and rpointer files. In the past, this meant figuring out what restart file should be pointed to in each component rpointer file and correcting it by hand in an editor. There was only the final set of rpointer files that was kept for a case. + +Now, with this update, the `lnd`, `cpl`, and `atm` rpointer files have the simulation date in the filenames, so it's easy to spot if the restarts are mismatched for one of the components. Also, since there are matching rpointer files for each time restarts are created, it's now easier to (a) make sure restarts and rpointer files are all correctly matched and (b) for a user to take a set of restarts and matching rpointer files to start up from for any part of an existing simulation. This means you don't have to hand-edit the rpointer files, making sure you don't make a mistake when you do. + +Old rpointer filenames: + +* `rpointer.atm` +* `rpointer.cpl` +* `rpointer.lnd` + +New names: + +* `rpointer.atm.YYYY-MM-DD-SSSSS` +* `rpointer.cpl.YYYY-MM-DD-SSSSS` +* `rpointer.lnd.YYYY-MM-DD-SSSSS` + +Where `YYYY-MM-DD-SSSSS` is the year, month, day, and seconds into the day for the simulation timestamp. For example, `rpointer.lnd.2000-01-01-00000` for a rpointer file for starting at midnight (beginning of the day) on January 1st, 2000\. + +Note that this is backwards-compatible, so for all the components you can use either the new or old format for the rpointer filenames. Thus, if you are restarting from an existing case before `ctsm5.3.016`, you CAN use the rpointer filenames that don't have the timestamps in the name. + +## Simulations supporting this release + +- f19 `Clm60Bgc` 16pft: [https://github.com/NCAR/LMWG\_dev/issues/70](https://github.com/NCAR/LMWG_dev/issues/70) +- f09 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/69](https://github.com/NCAR/LMWG_dev/issues/69) +- ne30 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/68](https://github.com/NCAR/LMWG_dev/issues/68) + +Note: Dust emissions in CTSM 5.3 will be different from the above simulations because of a tuning update that came in after those. \ No newline at end of file From 224cf73f025f3cf7e468c76501f7da35bd7654d2 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Tue, 26 Aug 2025 18:20:20 -0600 Subject: [PATCH 008/112] An update to WhatsNewInCTSM5.4.md about changes in ctsm5.3.040 --- WhatsNewInCTSM5.4.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index 62e1e45416..a36b8266c0 100644 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -33,6 +33,15 @@ Changes for all physics versions: ### Heads up * History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details below and in the PRs [\#2445](https://github.com/ESCOMP/ctsm/pull/2445) [\#117](https://github.com/ESCOMP/MOSART/pull/117) [\#61](https://github.com/ESCOMP/RTM/pull/61) and the correspondng issues. +* As of ctsm5.3.040, the new ctsm_pylib conda environment is incompatible with our tools from before ctsm5.3.040 and vice versa. If you have a ctsm_pylib conda environment installed from before ctsm5.3.040, keep that under a different name. We suggest the following command for doing this in a local copy of ctsm5.3.040 or later: + +./py_env_create -r ctsm_pylib_old + +This first renames your existing ctsm_pylib to ctsm_pylib_old and then installs the Python 3.13.2 version as ctsm_pylib. If you are unsure whether you already have ctsm_pylib installed, use the same command regardless, as it will skip the rename step if necessary. + +Information about additional py_env_create options — including how to install a fresh copy of the old conda environment — is available as follows: + +./py_env_create --help ## From f322bf6a13b0b03dd840b87f4faa3d531a272898 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 17 Oct 2025 11:33:38 -0400 Subject: [PATCH 009/112] docs-build-and-deploy: Only run on upstream. --- .github/workflows/docs-build-and-deploy.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs-build-and-deploy.yml b/.github/workflows/docs-build-and-deploy.yml index a802a0743a..55ad033ed7 100644 --- a/.github/workflows/docs-build-and-deploy.yml +++ b/.github/workflows/docs-build-and-deploy.yml @@ -33,6 +33,10 @@ concurrency: jobs: build-and-deploy: + + # Only run on upstream repository + if: ${{ github.repository == 'ESCOMP/CTSM' }} + environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} From 9b54d654488966bff0ed52aa8f043e1c53ca973e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 17 Oct 2025 10:44:01 -0600 Subject: [PATCH 010/112] Initial add of general structure for a CDEPS streams base class to use for CTSM, this compiles with intel on Derecho --- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 new file mode 100644 index 0000000000..61a56ff91d --- /dev/null +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -0,0 +1,146 @@ +module CTSMForce2DStreamBaseType + + + use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU + use dshr_strdata_mod , only : shr_strdata_type + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL + use clm_varctl , only : iulog + use spmdMod , only : masterproc, mpicom, iam + use abortutils , only : endrun + use decompMod , only : bounds_type + + implicit none + private + + type, abstract, public :: ctsm_force_2DStream_base_type + private + type(shr_strdata_type), private :: sdat ! Stream data type + contains + + ! PUBLIC METHODS + procedure(Init_interface) , public, deferred :: Init + procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams, , store the g_to_ig index array + procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method + procedure, public, non_overridable :: CleanBase ! Clean method for the base type + procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into output data + + end type ctsm_force_2DStream_base_type + + abstract interface + + subroutine Init_interface( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + end subroutine Init_interface + + subroutine Clean_interface(this) + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(in) :: this + end subroutine Clean_interface + + subroutine Interp_interface(this) + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(in) :: this + end subroutine Interp_interface + + end interface + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + + contains + + subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + ! Local variables + integer, parameter :: offset = 0 ! time offset in seconds of stream data + integer :: rc ! error return code + + call shr_strdata_init_from_inline(this%sdat, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock,& + model_mesh = mesh, & + stream_meshfile = trim(meshfile), & + stream_lev_dimname = 'null', & + stream_mapalgo = mapalgo, & + stream_filenames = (/trim(fldfilename)/), & + stream_fldlistFile = varnames, & + stream_fldListModel = varnames, & + stream_yearFirst = year_first, & + stream_yearLast = year_last, & + stream_yearAlign = model_year_align, & + stream_offset = offset, & + stream_taxmode = taxmode, & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = tintalgo, & + stream_name = name, & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + write(iulog,*) ' Streams initialization failing for ', trim(name), ' stream file = ', trim(fldfilename) + call endrun( 'CTSM forcing Streams initialization failing', file=sourcefile, line=__LINE__ ) + end if + end subroutine InitBase + + subroutine CleanBase( this ) + class(ctsm_force_2DStream_base_type) , intent(inout) :: this + + integer :: ierr ! error code + + ! Currently no data to deallocate other than the stream data type + ! The stream data type doesn't have a clean method right now + ! So doing a few things manually here + end subroutine CleanBase + + subroutine Advance(this) + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(in) :: this + end subroutine Advance + +end module CTSMForce2DStreamBaseType From 1ec43f9c1a4f15b2b29f446a59d58439bb8c5eca Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 20 Oct 2025 16:39:37 -0600 Subject: [PATCH 011/112] Add contents of the Advance, add some error checking, store some data in the local object --- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index 61a56ff91d..e7a9e05244 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -1,5 +1,6 @@ module CTSMForce2DStreamBaseType +#include "shr_assert.h" use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU use dshr_strdata_mod , only : shr_strdata_type @@ -8,6 +9,7 @@ module CTSMForce2DStreamBaseType use spmdMod , only : masterproc, mpicom, iam use abortutils , only : endrun use decompMod , only : bounds_type + use clm_varctl, only : FL => fname_len implicit none private @@ -15,6 +17,8 @@ module CTSMForce2DStreamBaseType type, abstract, public :: ctsm_force_2DStream_base_type private type(shr_strdata_type), private :: sdat ! Stream data type + character(len=FL) :: stream_filename ! The stream data filename (also in sdat) + character(len=CL) :: stream_name ! The stream name (also in sdat) contains ! PUBLIC METHODS @@ -79,6 +83,8 @@ subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tin ! Uses: use lnd_comp_shr , only : mesh, model_clock use dshr_strdata_mod , only : shr_strdata_init_from_inline + use decompMod , only : bounds_level_proc + use shr_log_mod , only : errMsg => shr_log_errMsg ! Arguments: class(ctsm_force_2DStream_base_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds @@ -98,6 +104,14 @@ subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tin integer, parameter :: offset = 0 ! time offset in seconds of stream data integer :: rc ! error return code + ! Some error checking... + SHR_ASSERT( bounds%level == bounds_level_proc, "InitBase should have a processor bounds, so we can do some checking"//errMsg( sourcefile, __LINE__) ) + SHR_ASSERT( bounds%begg == 1, "Make sure the starting bounds index is 1 so we know the mapping to gridcells is correct"//errMsg( sourcefile, __LINE__) ) + if ( len(fldfilename) >= FL )then + call endrun( 'stream field filename is too long:'//trim(fldfilename), file=sourcefile, line=__LINE__ ) + end if + this%stream_filename = fldfilename + this%stream_name = name call shr_strdata_init_from_inline(this%sdat, & my_task = iam, & logunit = iulog, & @@ -137,10 +151,28 @@ end subroutine CleanBase subroutine Advance(this) ! Uses: + use clm_time_manager , only : get_curr_date + use dshr_strdata_mod , only : shr_strdata_advance import :: ctsm_force_2DStream_base_type ! ! Arguments: - class(ctsm_force_2DStream_base_type), intent(in) :: this + class(ctsm_force_2DStream_base_type), intent(inout) :: this + ! !LOCAL VARIABLES: + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + integer :: rc ! Error return code + + ! Advance sdat stream + call get_curr_date(year, mon, day, sec) + mcdate = year*10000 + mon*100 + day + call shr_strdata_advance(this%sdat, ymd=mcdate, tod=sec, logunit=iulog, istr='CTSMForce2DStreamBase', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + write(iulog,*) ' Streams advance failing for ', trim(this.stream_name), ' stream file = ', trim(this.stream_filename) + call endrun( 'CTSM forcing Streams advance failing', file=sourcefile, line=__LINE__ ) + end if end subroutine Advance end module CTSMForce2DStreamBaseType From 20d62792787ea159c1680386c7df963169b9d90c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 21 Oct 2025 10:25:06 -0600 Subject: [PATCH 012/112] set_paramfile: Can now set all values of a parameter to same. --- python/ctsm/param_utils/set_paramfile.py | 11 ++-- python/ctsm/test/test_sys_set_paramfile.py | 56 +++++++++++++++++++-- python/ctsm/test/test_unit_set_paramfile.py | 13 +++-- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/python/ctsm/param_utils/set_paramfile.py b/python/ctsm/param_utils/set_paramfile.py index 2ceb52069b..6efa491846 100644 --- a/python/ctsm/param_utils/set_paramfile.py +++ b/python/ctsm/param_utils/set_paramfile.py @@ -130,7 +130,7 @@ def check_correct_ndims(da, new_value, throw_error=False): """ expected = da.ndim actual = np.array(new_value).ndim - is_ndim_correct = expected == actual + is_ndim_correct = actual in (0, expected) # If actual 0, apply it to all if throw_error and not is_ndim_correct: raise RuntimeError(f"Incorrect N dims: Expected {expected}, got {actual}") return is_ndim_correct @@ -323,11 +323,12 @@ def apply_new_value_to_parameter(args, ds_out, var, new_value, var_encoding, *, # Ensure that any NaNs are replaced with the fill value new_value = _replace_nans_with_fill(var_encoding, new_value, chg=chg) - # This can be needed if, e.g., you're selecting and changing just one PFT + # This can be needed if, (a) you're selecting and changing just one PFT or (b) you're changing + # all values in a dimensioned parameter to match one value. if ds_out[var].values.ndim > 0 and new_value.ndim == 0: - new_value = np.atleast_1d(new_value) - - ds_out[var].values = new_value + ds_out[var].values[:] = new_value + else: + ds_out[var].values = new_value return ds_out diff --git a/python/ctsm/test/test_sys_set_paramfile.py b/python/ctsm/test/test_sys_set_paramfile.py index 7d82a32302..bc8113d53b 100755 --- a/python/ctsm/test/test_sys_set_paramfile.py +++ b/python/ctsm/test/test_sys_set_paramfile.py @@ -121,8 +121,11 @@ def test_set_paramfile_changeparams_scalar_errors_given_list(self): with self.assertRaisesRegex(RuntimeError, "Incorrect N dims"): sp.main() - def test_set_paramfile_changeparam_1d_errors_given_scalar(self): - """Test that set_paramfile errors if given a scalar for a 1-d parameter""" + def test_set_paramfile_changeparam_1d_given_scalar(self): + """ + Test that set_paramfile works correctly if given a scalar for a 1-d parameter. We want it + to set all members of the 1d array to the given scalar. + """ output_path = os.path.join(self.tempdir, "output.nc") sys.argv = [ "set_paramfile", @@ -130,10 +133,53 @@ def test_set_paramfile_changeparam_1d_errors_given_scalar(self): PARAMFILE, "-o", output_path, - "xl=0.724", + "mxmat=1987", ] - with self.assertRaisesRegex(RuntimeError, "Incorrect N dims"): - sp.main() + sp.main() + self.assertTrue(os.path.exists(output_path)) + ds_in = open_paramfile(PARAMFILE) + ds_out = open_paramfile(output_path) + + for var in ds_in.variables: + # Check that all variables/coords are equal except the ones we changed, which should be + # set to what we asked + if var == "mxmat": + self.assertTrue(np.all(ds_out[var].values == 1987)) + else: + self.assertTrue(are_paramfile_dataarrays_identical(ds_in[var], ds_out[var])) + + def test_set_paramfile_changeparam_1d_given_scalar_and_pftlist(self): + """ + Test that set_paramfile works correctly if given a scalar for a 1-d parameter. We want it + to set all members of the 1d array to the given scalar. As + test_set_paramfile_changeparam_1d_given_scalar, but here we give a pft list. + """ + output_path = os.path.join(self.tempdir, "output.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output_path, + "-p", + "temperate_corn,irrigated_temperate_corn", + "mxmat=1987", + ] + sp.main() + self.assertTrue(os.path.exists(output_path)) + ds_in = open_paramfile(PARAMFILE) + ds_out = open_paramfile(output_path) + + for var in ds_in.variables: + # Check that all variables/coords are equal except the ones we changed, which should be + # set to what we asked + if var == "mxmat": + # First, check that they weren't 1987 before + self.assertFalse(np.any(ds_in[var].values[17:18] == 1987)) + # Now check that they are 1987 + self.assertTrue(np.all(ds_out[var].values[17:18] == 1987)) + else: + self.assertTrue(are_paramfile_dataarrays_identical(ds_in[var], ds_out[var])) def test_set_paramfile_changeparams_scalar_double(self): """Test that set_paramfile can copy to a new file with some scalar double params changed""" diff --git a/python/ctsm/test/test_unit_set_paramfile.py b/python/ctsm/test/test_unit_set_paramfile.py index 6b091f8967..e481c65a6c 100755 --- a/python/ctsm/test/test_unit_set_paramfile.py +++ b/python/ctsm/test/test_unit_set_paramfile.py @@ -65,14 +65,14 @@ def test_checkcorrectndims_0d_int_np(self): self.assertTrue(sp.check_correct_ndims(da, np.int32(1))) def test_checkcorrectndims_1d_int(self): - """Check False when given a standard int for a 0d parameter""" + """Check True when given a standard int for a 0d parameter""" da = xr.DataArray(data=[1, 2]) - self.assertFalse(sp.check_correct_ndims(da, 1)) + self.assertTrue(sp.check_correct_ndims(da, 1)) def test_checkcorrectndims_1d_int_np(self): - """Check False when given a numpy int for a 0d parameter""" + """Check True when given a numpy int for a 0d parameter""" da = xr.DataArray(data=[1, 2]) - self.assertFalse(sp.check_correct_ndims(da, np.int32(1))) + self.assertTrue(sp.check_correct_ndims(da, np.int32(1))) def test_checkcorrectndims_0d_list(self): """Check False when given a list for a 0d parameter""" @@ -100,6 +100,11 @@ def test_checkcorrectndims_1d_nparray(self): da = xr.DataArray(data=[1, 2]) self.assertTrue(sp.check_correct_ndims(da, np.array([1, 2]))) + def test_checkcorrectndims_1d_scalar(self): + """Check True when given a scalar for a 1d parameter (we want to apply it to all)""" + da = xr.DataArray(data=[1, 2]) + self.assertTrue(sp.check_correct_ndims(da, 87)) + class TestUnitIsInteger(unittest.TestCase): """Unit tests of is_integer""" From 396490c3b356f4d6466a24a81c2b6a39296bd486 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 21 Oct 2025 11:07:21 -0600 Subject: [PATCH 013/112] Update set_paramfile doc to show new capability. --- doc/source/users_guide/using-clm-tools/paramfile-tools.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/source/users_guide/using-clm-tools/paramfile-tools.md b/doc/source/users_guide/using-clm-tools/paramfile-tools.md index 71d881e362..aaee301bcb 100644 --- a/doc/source/users_guide/using-clm-tools/paramfile-tools.md +++ b/doc/source/users_guide/using-clm-tools/paramfile-tools.md @@ -57,9 +57,14 @@ Change a one-dimensional parameter (`mimics_fmet` has the `segment` dimension, l tools/param_utils/set_paramfile -i paramfile.nc -o output.nc mimics_fmet=0.1,0.2,0.3,0.4 ``` +Change a one-dimensional parameter to be all one value (`mxmat` has the `pft` dimension, length 79): +```bash +tools/param_utils/set_paramfile -i paramfile.nc -o output.nc mxmat=360 +``` + Change a parameter for specific PFTs: ```bash -tools/param_utils/set_paramfile -i paramfile.nc -o output.nc -p needleleaf_evergreen_temperate_tree,c4_grass medlynintercept=99.9,100.1 medlynslope=2.99,1.99 +tools/param_utils/set_paramfile -i paramfile.nc -o output.nc -p needleleaf_evergreen_temperate_tree,c4_grass medlynintercept=99.9,100.1 medlynslope=2.99,1.99 mxmat=199 ``` Set a parameter to the fill value: From adc13e1d5114c51cfa9a2c67f7f20d2c264a583f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 21 Oct 2025 12:00:15 -0600 Subject: [PATCH 014/112] Delete new test that's duplicative. --- python/ctsm/test/test_unit_set_paramfile.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/ctsm/test/test_unit_set_paramfile.py b/python/ctsm/test/test_unit_set_paramfile.py index e481c65a6c..13be015c67 100755 --- a/python/ctsm/test/test_unit_set_paramfile.py +++ b/python/ctsm/test/test_unit_set_paramfile.py @@ -100,11 +100,6 @@ def test_checkcorrectndims_1d_nparray(self): da = xr.DataArray(data=[1, 2]) self.assertTrue(sp.check_correct_ndims(da, np.array([1, 2]))) - def test_checkcorrectndims_1d_scalar(self): - """Check True when given a scalar for a 1d parameter (we want to apply it to all)""" - da = xr.DataArray(data=[1, 2]) - self.assertTrue(sp.check_correct_ndims(da, 87)) - class TestUnitIsInteger(unittest.TestCase): """Unit tests of is_integer""" From 01e2228ea75d8891f6c1fbe7be9358eb7aae34da Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 21 Oct 2025 13:11:41 -0600 Subject: [PATCH 015/112] Initial commit of streams for delta_C13 and delta_C14 that extend the CTSM stream base type --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/biogeochem/AtmCarbonIsotopeStreamType.F90 diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 new file mode 100644 index 0000000000..a6ec552b48 --- /dev/null +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -0,0 +1,141 @@ +module AtmCarbonIsotopeStreamType + use shr_kind_mod , only : r8 => shr_kind_r8 + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + use CTSMForce2DStreamBaseType, only : ctsm_force_2DStream_base_type + + implicit none + private + + type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type + private + real(r8), allocatable :: atm_delta_c13(:) ! delta C13 data array + contains + + ! Public Methods + procedure, public :: C13Init + procedure, public :: Init => C13Init + procedure, public :: C13Interp + procedure, public :: Interp => C13Interp + procedure, public :: C13Clean + procedure, public :: Clean => C13Clean + !final :: C13Clean + ! Private methods + procedure, private :: C13InitAllocate + + end type atm_delta_c13_stream_type + + type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type + private + real(r8), allocatable :: atm_delta_c14(:) ! delta c14 data array + contains + + ! Public Methods + procedure, public :: C14Init + procedure, public :: Init => C14Init + procedure, public :: C14Interp + procedure, public :: Interp => C14Interp + procedure, public :: C14Clean + procedure, public :: Clean => C14Clean + !final :: C14Clean + ! Private methods + procedure, private :: C14InitAllocate + + end type atm_delta_c14_stream_type + + contains + + subroutine C13Init( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + ! Arguments: + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + call this%InitBase( bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + call this%C13InitAllocate( bounds ) + + end subroutine C13Init + + subroutine C13InitAllocate( this, bounds ) + use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + integer :: begg, endg + + begg = bounds%begg; endg = bounds%endg + allocate( this%atm_delta_c13( bounds%begg : bounds%endg ) ); this%atm_delta_c13 = nan + end subroutine C13InitAllocate + + subroutine C13Clean( this ) + class(atm_delta_c13_stream_type), intent(inout) :: this + + deallocate( this%atm_delta_c13 ) + + end subroutine C13Clean + + subroutine C13Interp( this, bounds ) + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + end subroutine C13Interp + + subroutine C14Init( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + ! Arguments: + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + call this%InitBase( bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + call this%C14InitAllocate( bounds ) + + end subroutine C14Init + + subroutine C14InitAllocate( this, bounds ) + use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + integer :: begg, endg + + begg = bounds%begg; endg = bounds%endg + allocate( this%atm_delta_c14( bounds%begg : bounds%endg ) ); this%atm_delta_c14 = nan + end subroutine C14InitAllocate + + subroutine C14Interp( this, bounds ) + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + end subroutine C14Interp + + subroutine C14Clean( this ) + class(atm_delta_c14_stream_type), intent(inout) :: this + + deallocate( this%atm_delta_c14 ) + + end subroutine C14Clean + +end module AtmCarbonIsotopeStreamType \ No newline at end of file From cc8a0b0256a168ffbca62ed44619137eb1f8f430 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 21 Oct 2025 13:12:19 -0600 Subject: [PATCH 016/112] Correct the interface --- src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index e7a9e05244..3cfe6742c3 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -60,15 +60,17 @@ subroutine Clean_interface(this) import :: ctsm_force_2DStream_base_type ! ! Arguments: - class(ctsm_force_2DStream_base_type), intent(in) :: this + class(ctsm_force_2DStream_base_type), intent(inout) :: this end subroutine Clean_interface - subroutine Interp_interface(this) + subroutine Interp_interface(this, bounds) ! Uses: + use decompMod , only : bounds_type import :: ctsm_force_2DStream_base_type ! ! Arguments: - class(ctsm_force_2DStream_base_type), intent(in) :: this + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds end subroutine Interp_interface end interface From 1ca79d16eccd1d90820af338159349016b42d40e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 21 Oct 2025 16:14:52 -0600 Subject: [PATCH 017/112] Changes so there is a final method that the compiler can call to remove memory when appropriate --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index a6ec552b48..94b85de30e 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -18,9 +18,9 @@ module AtmCarbonIsotopeStreamType procedure, public :: Init => C13Init procedure, public :: C13Interp procedure, public :: Interp => C13Interp - procedure, public :: C13Clean - procedure, public :: Clean => C13Clean - !final :: C13Clean + procedure, public :: C13ClassClean + procedure, public :: Clean => C13ClassClean + final :: C13TypeClean ! Private methods procedure, private :: C13InitAllocate @@ -36,9 +36,9 @@ module AtmCarbonIsotopeStreamType procedure, public :: Init => C14Init procedure, public :: C14Interp procedure, public :: Interp => C14Interp - procedure, public :: C14Clean - procedure, public :: Clean => C14Clean - !final :: C14Clean + procedure, public :: C14ClassClean + procedure, public :: Clean => C14ClassClean + final :: C14TypeClean ! Private methods procedure, private :: C14InitAllocate @@ -80,12 +80,20 @@ subroutine C13InitAllocate( this, bounds ) allocate( this%atm_delta_c13( bounds%begg : bounds%endg ) ); this%atm_delta_c13 = nan end subroutine C13InitAllocate - subroutine C13Clean( this ) + subroutine C13ClassClean( this ) class(atm_delta_c13_stream_type), intent(inout) :: this + call C13TypeClean( this ) + + end subroutine C13ClassClean + + subroutine C13TypeClean( this ) + type(atm_delta_c13_stream_type), intent(inout) :: this + deallocate( this%atm_delta_c13 ) + call this%CleanBase() - end subroutine C13Clean + end subroutine C13TypeClean subroutine C13Interp( this, bounds ) class(atm_delta_c13_stream_type), intent(inout) :: this @@ -131,11 +139,20 @@ subroutine C14Interp( this, bounds ) type(bounds_type), intent(in) :: bounds end subroutine C14Interp - subroutine C14Clean( this ) + subroutine C14ClassClean( this ) class(atm_delta_c14_stream_type), intent(inout) :: this + call C14TypeClean( this ) + + end subroutine C14ClassClean + + + subroutine C14TypeClean( this ) + type(atm_delta_c14_stream_type), intent(inout) :: this + deallocate( this%atm_delta_c14 ) + call this%CleanBase() - end subroutine C14Clean + end subroutine C14TypeClean end module AtmCarbonIsotopeStreamType \ No newline at end of file From 7acfbc94bd927148e0c1d6ae932de8f847bed1b0 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 22 Oct 2025 00:26:37 -0600 Subject: [PATCH 018/112] Add contents of the Interp methods for c13/c14, change Init streambase method a bit, so that varnames and name aren't sent in, also have to make sdat public, so that the dshr_fldbun_getFldPtr method can use it's internal components --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 57 +++++++++++++++---- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 6 +- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index 94b85de30e..d3b845df9b 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -4,10 +4,13 @@ module AtmCarbonIsotopeStreamType use abortutils , only : endrun use decompMod , only : bounds_type use CTSMForce2DStreamBaseType, only : ctsm_force_2DStream_base_type + use dshr_methods_mod , only : dshr_fldbun_getfldptr + use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU implicit none private + character(len=*), parameter :: varname_c13 = 'atm_delta_c13' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type private real(r8), allocatable :: atm_delta_c13(:) ! delta C13 data array @@ -26,6 +29,7 @@ module AtmCarbonIsotopeStreamType end type atm_delta_c13_stream_type + character(len=*), parameter :: varname_c14 = 'atm_delta_c14' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type private real(r8), allocatable :: atm_delta_c14(:) ! delta c14 data array @@ -44,27 +48,29 @@ module AtmCarbonIsotopeStreamType end type atm_delta_c14_stream_type + character(len=*), parameter, private :: sourcefile = & + __FILE__ + contains - subroutine C13Init( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + subroutine C13Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) ! Uses: ! Arguments: class(atm_delta_c13_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds - character(*), intent(in) :: varnames(:) ! variable names to read from stream file character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type character(*), intent(in) :: tintalgo ! time interpolation algorithm character(*), intent(in) :: taxMode ! time axis mode - character(*), intent(in) :: name ! name of stream integer, intent(in) :: year_first ! first year to use integer, intent(in) :: year_last ! last year to use integer, intent(in) :: model_year_align ! align yearFirst with this model year - call this%InitBase( bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & - year_first, year_last, model_year_align ) + call this%InitBase( bounds, varnames = (/ varname_c13 /), fldfilename=fldfilename, meshfile=meshfile, & + mapalgo=mapalgo, tintalgo=tintalgo, taxmode=taxmode, name=varname_c13, & + year_first=year_first, year_last=year_last, model_year_align=model_year_align ) call this%C13InitAllocate( bounds ) end subroutine C13Init @@ -98,27 +104,41 @@ end subroutine C13TypeClean subroutine C13Interp( this, bounds ) class(atm_delta_c13_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds - end subroutine C13Interp - subroutine C14Init( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + ! Local Variables + integer :: g + real(r8), pointer :: dataptr1d(:) + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=varname_c13, fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + call endrun( 'Error getting field pointer for '//varname_c13//' from stream data', file=sourcefile, line=__LINE__ ) + end if + + do g = bounds%begg, bounds%endg + this%atm_delta_c13(g) = dataptr1d(g) + end do + end subroutine C13Interp + + subroutine C14Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) ! Uses: ! Arguments: class(atm_delta_c14_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds - character(*), intent(in) :: varnames(:) ! variable names to read from stream file character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type character(*), intent(in) :: tintalgo ! time interpolation algorithm character(*), intent(in) :: taxMode ! time axis mode - character(*), intent(in) :: name ! name of stream integer, intent(in) :: year_first ! first year to use integer, intent(in) :: year_last ! last year to use integer, intent(in) :: model_year_align ! align yearFirst with this model year - call this%InitBase( bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & - year_first, year_last, model_year_align ) + call this%InitBase( bounds, varnames = (/ varname_c14 /), fldfilename=fldfilename, meshfile=meshfile, & + mapalgo=mapalgo, tintalgo=tintalgo, taxmode=taxmode, name=varname_c14, & + year_first=year_first, year_last=year_last, model_year_align=model_year_align ) call this%C14InitAllocate( bounds ) end subroutine C14Init @@ -137,6 +157,21 @@ end subroutine C14InitAllocate subroutine C14Interp( this, bounds ) class(atm_delta_c14_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds + + ! Local Variables + integer :: g + real(r8), pointer :: dataptr1d(:) + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=varname_c14, fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + call endrun( 'Error getting field pointer for '//varname_c14//' from stream data', file=sourcefile, line=__LINE__ ) + end if + + do g = bounds%begg, bounds%endg + this%atm_delta_c14(g) = dataptr1d(g) + end do end subroutine C14Interp subroutine C14ClassClean( this ) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index 3cfe6742c3..5a928091f8 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -16,7 +16,7 @@ module CTSMForce2DStreamBaseType type, abstract, public :: ctsm_force_2DStream_base_type private - type(shr_strdata_type), private :: sdat ! Stream data type + type(shr_strdata_type), public :: sdat ! Stream data type character(len=FL) :: stream_filename ! The stream data filename (also in sdat) character(len=CL) :: stream_name ! The stream name (also in sdat) contains @@ -33,7 +33,7 @@ module CTSMForce2DStreamBaseType abstract interface - subroutine Init_interface( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) ! Uses: use decompMod , only : bounds_type @@ -42,14 +42,12 @@ subroutine Init_interface( this, bounds, varnames, fldfilename, meshfile, mapalg ! Arguments: class(ctsm_force_2DStream_base_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds - character(*), intent(in) :: varnames(:) ! variable names to read from stream file character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type character(*), intent(in) :: tintalgo ! time interpolation algorithm character(*), intent(in) :: taxMode ! time axis mode - character(*), intent(in) :: name ! name of stream integer, intent(in) :: year_first ! first year to use integer, intent(in) :: year_last ! last year to use integer, intent(in) :: model_year_align ! align yearFirst with this model year From 52b238b3a8093f9ac71851b1832f08204a58b5f5 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 22 Oct 2025 02:49:35 -0600 Subject: [PATCH 019/112] Hardcode namelist items and add calls to CTSM Streams Init subroutines for C13/C14 timeseries handling --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 529a547e88..f2e7392b3a 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -4,7 +4,7 @@ module CIsoAtmTimeseriesMod ! Module for transient atmospheric boundary to the c13 and c14 codes ! ! !USES: - use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL use clm_time_manager , only : get_curr_date, get_curr_yearfrac use clm_varcon , only : c14ratio, secspday use shr_const_mod , only : SHR_CONST_PDB ! Ratio of C13/C12 @@ -12,6 +12,8 @@ module CIsoAtmTimeseriesMod use abortutils , only : endrun use spmdMod , only : masterproc use shr_log_mod , only : errMsg => shr_log_errMsg + use AtmCarbonIsotopeStreamType, only : atm_delta_c13_stream_type, atm_delta_c14_stream_type + use decompMod , only : bounds_type ! implicit none private @@ -33,6 +35,9 @@ module CIsoAtmTimeseriesMod ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file + type(atm_delta_c13_stream_type), private :: atm_c13_stream ! Atmospheric C13 stream object + type(atm_delta_c14_stream_type), private :: atm_c14_stream ! Atmospheric C14 stream object + ! !PRIVATE TYPES: real(r8), allocatable, private :: atm_c14file_time(:) ! time for C14 data real(r8), allocatable, private :: atm_delta_c14(:,:) ! Delta C14 data @@ -40,6 +45,23 @@ module CIsoAtmTimeseriesMod real(r8), allocatable, private :: atm_delta_c13(:) ! Delta C13 data real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file + ! Private data for the control namelist: + character(len=CL), private :: stream_fldfilename_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' + character(len=CL), private :: stream_meshfile_atm_c14 = '/glade/campaignA/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/mesh_4x1_global_c20251013.nc' + character(len=CL), private :: stream_fldfilename_atm_c13 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' + integer, private :: stream_year_first_atm_c14 = 1850 + integer, private :: stream_year_last_atm_c14 = 2023 + integer, private :: stream_model_year_align_atm_c14 = 1850 + integer, private :: stream_year_first_atm_c13 = 1850 + integer, private :: stream_year_last_atm_c13 = 2023 + integer, private :: stream_model_year_align_atm_c13 = 1850 + character(len=CL), private :: stream_mapalgo_atm_c14 = 'nn' + character(len=CL), private :: stream_tintalgo_atm_c14 = 'linear' + character(len=CL), private :: stream_taxmode_atm_c14 = 'extend' + character(len=CL), private :: stream_mapalgo_atm_c13 = 'nn' + character(len=CL), private :: stream_tintalgo_atm_c13 = 'linear' + character(len=CL), private :: stream_taxmode_atm_c13 = 'extend' + character(len=*), parameter, private :: sourcefile = & __FILE__ !----------------------------------------------------------------------- @@ -109,6 +131,7 @@ subroutine C14_init_BombSpike() ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use decompMod , only : get_proc_bounds ! ! !LOCAL VARIABLES: implicit none @@ -119,6 +142,7 @@ subroutine C14_init_BombSpike() integer :: nsec ! number of input data sectors integer :: t ! time index logical :: readvar ! if variable read or not + type(bounds_type) :: bounds_proc character(len=*), parameter :: vname = 'Delta14co2_in_air' ! Variable name on file !----------------------------------------------------------------------- @@ -165,6 +189,20 @@ subroutine C14_init_BombSpike() endif end do + ! Streams method + call get_proc_bounds( bounds_proc ) + call atm_c14_stream%Init( bounds_proc, & + fldfilename=stream_fldfilename_atm_c14, & + meshfile= stream_meshfile_atm_c14, & + mapalgo=stream_mapalgo_atm_c14, & + tintalgo=stream_tintalgo_atm_c14, & + taxmode=stream_taxmode_atm_c14, & + year_first=stream_year_first_atm_c14, & + year_last=stream_year_last_atm_c14, & + model_year_align=stream_model_year_align_atm_c14 ) + call atm_c13_stream%Advance( ) + call atm_c13_stream%Interp( bounds_proc ) + end subroutine C14_init_BombSpike @@ -229,6 +267,7 @@ subroutine C13_init_TimeSeries() ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use decompMod , only : get_proc_bounds ! ! !LOCAL VARIABLES: implicit none @@ -238,6 +277,7 @@ subroutine C13_init_TimeSeries() integer :: ntim ! number of input data time samples integer :: t ! Time index logical :: readvar ! if variable read or not + type(bounds_type) :: bounds_proc character(len=*), parameter :: vname = 'delta13co2_in_air' ! Variable name on file !----------------------------------------------------------------------- @@ -280,6 +320,20 @@ subroutine C13_init_TimeSeries() endif end do + ! Streams method + call get_proc_bounds( bounds_proc ) + call atm_c13_stream%Init( bounds_proc, & + fldfilename=stream_fldfilename_atm_c13, & + meshfile= 'none', & + mapalgo=stream_mapalgo_atm_c13, & + tintalgo=stream_tintalgo_atm_c13, & + taxmode=stream_taxmode_atm_c13, & + year_first=stream_year_first_atm_c13, & + year_last=stream_year_last_atm_c13, & + model_year_align=stream_model_year_align_atm_c13 ) + call atm_c13_stream%Advance( ) + call atm_c13_stream%Interp( bounds_proc ) + end subroutine C13_init_TimeSeries !----------------------------------------------------------------------- From d8e2100488997e2ab0bec4942083c563e95b48b6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 22 Oct 2025 03:10:18 -0600 Subject: [PATCH 020/112] Correct varnames on the carbon isotope stream files --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index d3b845df9b..34e5916ea0 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -10,7 +10,7 @@ module AtmCarbonIsotopeStreamType implicit none private - character(len=*), parameter :: varname_c13 = 'atm_delta_c13' + character(len=*), parameter :: varname_c13 = 'delta13co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type private real(r8), allocatable :: atm_delta_c13(:) ! delta C13 data array @@ -29,7 +29,7 @@ module AtmCarbonIsotopeStreamType end type atm_delta_c13_stream_type - character(len=*), parameter :: varname_c14 = 'atm_delta_c14' + character(len=*), parameter :: varname_c14 = 'Delta14co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type private real(r8), allocatable :: atm_delta_c14(:) ! delta c14 data array From a23c8f8fa5a0b8add7fccfc37d9ee8fa7c4fe91d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 22 Oct 2025 09:46:02 -0600 Subject: [PATCH 021/112] Fix the C14 isotope read, it needs to NOT have a mesh file, and the filename was incorrect as well as a typo for C13 in the Advance and Interp routines --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index f2e7392b3a..e4e018a39e 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -47,7 +47,7 @@ module CIsoAtmTimeseriesMod ! Private data for the control namelist: character(len=CL), private :: stream_fldfilename_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' - character(len=CL), private :: stream_meshfile_atm_c14 = '/glade/campaignA/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/mesh_4x1_global_c20251013.nc' + character(len=CL), private :: stream_meshfile_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/mesh_4x1_global_c20251013.nc' character(len=CL), private :: stream_fldfilename_atm_c13 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' integer, private :: stream_year_first_atm_c14 = 1850 integer, private :: stream_year_last_atm_c14 = 2023 @@ -193,15 +193,16 @@ subroutine C14_init_BombSpike() call get_proc_bounds( bounds_proc ) call atm_c14_stream%Init( bounds_proc, & fldfilename=stream_fldfilename_atm_c14, & - meshfile= stream_meshfile_atm_c14, & + !meshfile= stream_meshfile_atm_c14, & + meshfile= 'none', & mapalgo=stream_mapalgo_atm_c14, & tintalgo=stream_tintalgo_atm_c14, & taxmode=stream_taxmode_atm_c14, & year_first=stream_year_first_atm_c14, & year_last=stream_year_last_atm_c14, & model_year_align=stream_model_year_align_atm_c14 ) - call atm_c13_stream%Advance( ) - call atm_c13_stream%Interp( bounds_proc ) + call atm_c14_stream%Advance( ) + call atm_c14_stream%Interp( bounds_proc ) end subroutine C14_init_BombSpike From 61dd157d853ee3099429d09bdb22cfb2ba89f494 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 24 Oct 2025 14:05:23 -0600 Subject: [PATCH 022/112] Change c13/c14 timeseries to put the data on begg:endg array, that can be used easier in the code, this increases memory, but matches the rest of CTSM better and isolates the handling of the grid for the C14 data which makes it easier for the grid to change, this is a prestep to moving to streams as this is how streams works --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 78 +++++++++++++++---- src/biogeophys/CanopyFluxesMod.F90 | 2 +- src/biogeophys/PhotosynthesisMod.F90 | 33 +++----- src/main/clm_initializeMod.F90 | 4 +- 4 files changed, 79 insertions(+), 38 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 529a547e88..fa3c467eff 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -12,6 +12,7 @@ module CIsoAtmTimeseriesMod use abortutils , only : endrun use spmdMod , only : masterproc use shr_log_mod , only : errMsg => shr_log_errMsg + use decompMod , only : bounds_type ! implicit none private @@ -27,40 +28,46 @@ module CIsoAtmTimeseriesMod character(len=256) , public :: atm_c14_filename = ' ' ! file name of C14 input data logical , public :: use_c13_timeseries = .false. ! do we use time-varying atmospheric C13? character(len=256) , public :: atm_c13_filename = ' ' ! file name of C13 input data - integer, parameter , public :: nsectors_c14 = 3 ! Number of latitude sectors the C14 data has + real(r8), allocatable, public :: rc14_atm_grc(:) ! Ratio of C14 C12 data on gridcell + real(r8), allocatable, public :: rc13_atm_grc(:) ! Ratio of C13 C12 data on gridcell ! ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file ! !PRIVATE TYPES: + integer, parameter , private :: nsectors_c14 = 3 ! Number of latitude sectors the C14 data has real(r8), allocatable, private :: atm_c14file_time(:) ! time for C14 data - real(r8), allocatable, private :: atm_delta_c14(:,:) ! Delta C14 data + real(r8), allocatable, private :: atm_delta_c14(:,:) ! Delta C14 data (time,nsectors) + real(r8), allocatable, private :: atm_delta_c14_grc(:) ! Delta C14 data on gridcell real(r8), allocatable, private :: atm_c13file_time(:) ! time for C13 data - real(r8), allocatable, private :: atm_delta_c13(:) ! Delta C13 data + real(r8), allocatable, private :: atm_delta_c13(:) ! Delta C13 data (time) + real(r8), allocatable, private :: atm_delta_c13_grc(:) ! Delta C13 data on gridcell real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file character(len=*), parameter, private :: sourcefile = & - __FILE__ + __FILE__ !----------------------------------------------------------------------- contains !----------------------------------------------------------------------- - subroutine C14BombSpike( rc14_atm ) + subroutine C14BombSpike( bounds ) ! ! !DESCRIPTION: ! for transient simulation, read in an atmospheric timeseries file to impose bomb spike ! + use GridcellType , only : grc ! !ARGUMENTS: implicit none - real(r8), intent(out) :: rc14_atm(nsectors_c14) ! Ratio of C14 to C12 + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: yr, mon, day, tod ! year, month, day, time-of-day real(r8) :: dateyear ! Date converted to year real(r8) :: delc14o2_atm(nsectors_c14) ! C14 delta units - integer :: fp, p, nt ! Indices + real(r8) :: rc14_atm(nsectors_c14) ! C14 ratio C14 C12units + integer :: fp, p, nt, g ! Indices integer :: ind_below ! Time index below current time integer :: ntim_atm_ts ! Number of times on file real(r8) :: twt_1, twt_2 ! weighting fractions for interpolating @@ -97,11 +104,26 @@ subroutine C14BombSpike( rc14_atm ) ! change delta units to ratio rc14_atm(l) = (delc14o2_atm(l) * 1.e-3_r8 + 1._r8) * c14ratio end do + ! + ! Now map to the gridcell from the sectors + ! + do g = bounds%begg, bounds%endg + ! determine latitute sector for radiocarbon bomb spike inputs + if ( grc%latdeg(g) >= 30._r8 ) then + l = 1 + else if ( grc%latdeg(g) >= -30._r8 ) then + l = 2 + else + l = 3 + endif + atm_delta_c14_grc(g) = delc14o2_atm(l) + rc14_atm_grc(g) = rc14_atm(l) + end do end subroutine C14BombSpike !----------------------------------------------------------------------- - subroutine C14_init_BombSpike() + subroutine C14_init_BombSpike( bounds ) ! ! !DESCRIPTION: ! read netcdf file containing a timeseries of atmospheric delta C14 values; save in module-level array @@ -109,9 +131,12 @@ subroutine C14_init_BombSpike() ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) + implicit none + ! Arguments: + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - implicit none character(len=256) :: locfn ! local file name type(file_desc_t) :: ncid ! netcdf id integer :: dimid,varid ! input netCDF id's @@ -142,6 +167,12 @@ subroutine C14_init_BombSpike() allocate(atm_delta_c14(nsectors_c14,ntim)) atm_delta_c14(:,:) = 0.0_r8 + ! Allocate the gridcell arrays + allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) + allocate(rc14_atm_grc(bounds%begg:bounds%endg)) + atm_delta_c14_grc(:) = nan + rc14_atm_grc(:) = nan + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c14file_time, & readvar=readvar) if ( .not. readvar ) then @@ -169,20 +200,22 @@ end subroutine C14_init_BombSpike !----------------------------------------------------------------------- - subroutine C13TimeSeries( rc13_atm ) + subroutine C13TimeSeries( bounds ) ! ! !DESCRIPTION: ! for transient pulse simulation, impose a time-varying atm boundary condition ! + use GridcellType , only : grc ! !ARGUMENTS: implicit none - real(r8), intent(out) :: rc13_atm ! Ratio of C13 to C12 + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: + real(r8) :: rc13_atm ! Ratio of C13 to C12 integer :: yr, mon, day, tod ! year, month, day, time-of-day real(r8) :: dateyear ! date translated to year real(r8) :: delc13o2_atm ! Delta C13 - integer :: fp, p, nt ! Indices + integer :: fp, p, nt, g ! Indices integer :: ind_below ! Index of time in file before current time integer :: ntim_atm_ts ! Number of times on file real(r8) :: twt_1, twt_2 ! weighting fractions for interpolating @@ -218,10 +251,18 @@ subroutine C13TimeSeries( rc13_atm ) rc13_atm = (delc13o2_atm * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB + ! + ! Copy to the gridcell arrays + ! + do g = bounds%begg, bounds%endg + atm_delta_c13_grc(g) = delc13o2_atm + rc13_atm_grc(g) = rc13_atm + end do + end subroutine C13TimeSeries !----------------------------------------------------------------------- - subroutine C13_init_TimeSeries() + subroutine C13_init_TimeSeries( bounds ) ! ! !DESCRIPTION: ! read netcdf file containing a timeseries of atmospheric delta C13 values; save in module-level array @@ -229,9 +270,12 @@ subroutine C13_init_TimeSeries() ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) + implicit none ! + ! Arguments: + type(bounds_type), intent(in) :: bounds ! !LOCAL VARIABLES: - implicit none character(len=256) :: locfn ! local file name type(file_desc_t) :: ncid ! netcdf id integer :: dimid,varid ! input netCDF id's @@ -256,6 +300,12 @@ subroutine C13_init_TimeSeries() allocate(atm_c13file_time(ntim)) allocate(atm_delta_c13(ntim)) + ! Allocate the gridcell arrays + allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) + allocate(rc13_atm_grc(bounds%begg:bounds%endg) ) + atm_delta_c13_grc(:) = nan + rc13_atm_grc(:) = nan + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c13file_time, & readvar=readvar) if ( .not. readvar ) then diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index afe665e6d1..ad3d171c43 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -1645,7 +1645,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, ! Determine total photosynthesis - call PhotosynthesisTotal(fn, filterp, & + call PhotosynthesisTotal(bounds, fn, filterp, & atm2lnd_inst, canopystate_inst, photosyns_inst) ! Calculate water use efficiency diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index b88fb170c7..d5dedd3a16 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -1,4 +1,4 @@ -module PhotosynthesisMod +module PhotosynthesisMod #include "shr_assert.h" @@ -21,7 +21,6 @@ module PhotosynthesisMod use decompMod , only : bounds_type, subgrid_level_patch use QuadraticMod , only : quadratic use pftconMod , only : pftcon - use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries, nsectors_c14 use atm2lndType , only : atm2lnd_type use CanopyStateType , only : canopystate_type use CNVegnitrogenstateType, only : cnveg_nitrogenstate_type @@ -2063,12 +2062,15 @@ subroutine Photosynthesis ( bounds, fn, filterp, & end subroutine Photosynthesis !------------------------------------------------------------------------------ - subroutine PhotosynthesisTotal (fn, filterp, & + subroutine PhotosynthesisTotal (bounds, fn, filterp, & atm2lnd_inst, canopystate_inst, photosyns_inst) ! ! Determine total photosynthesis ! + use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries + use CIsoAtmTimeseriesMod, only : rc13_atm_grc, rc14_atm_grc ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds integer , intent(in) :: fn ! size of pft filter integer , intent(in) :: filterp(fn) ! patch filter type(atm2lnd_type) , intent(in) :: atm2lnd_inst @@ -2078,8 +2080,6 @@ subroutine PhotosynthesisTotal (fn, filterp, & ! !LOCAL VARIABLES: integer :: f,fp,p,l,g ! indices - real(r8) :: rc14_atm(nsectors_c14), rc13_atm - integer :: sector_c14 !----------------------------------------------------------------------- associate( & @@ -2116,15 +2116,15 @@ subroutine PhotosynthesisTotal (fn, filterp, & if ( use_c14 ) then if (use_c14_bombspike) then - call C14BombSpike(rc14_atm) + call C14BombSpike(bounds) else - rc14_atm(:) = c14ratio + rc14_atm_grc(:) = c14ratio end if end if if ( use_c13 ) then if (use_c13_timeseries) then - call C13TimeSeries(rc13_atm) + call C13TimeSeries(bounds) end if end if @@ -2142,7 +2142,7 @@ subroutine PhotosynthesisTotal (fn, filterp, & if (use_cn) then if ( use_c13 ) then if (use_c13_timeseries) then - rc13_canair(p) = rc13_atm + rc13_canair(p) = rc13_atm_grc(g) else rc13_canair(p) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) endif @@ -2157,19 +2157,10 @@ subroutine PhotosynthesisTotal (fn, filterp, & endif if ( use_c14 ) then - ! determine latitute sector for radiocarbon bomb spike inputs - if ( grc%latdeg(g) .ge. 30._r8 ) then - sector_c14 = 1 - else if ( grc%latdeg(g) .ge. -30._r8 ) then - sector_c14 = 2 - else - sector_c14 = 3 - endif - - rc14_canair(p) = rc14_atm(sector_c14) + rc14_canair(p) = rc14_atm_grc(g) - c14_psnsun(p) = rc14_atm(sector_c14) * psnsun(p) - c14_psnsha(p) = rc14_atm(sector_c14) * psnsha(p) + c14_psnsun(p) = rc14_atm_grc(g) * psnsun(p) + c14_psnsha(p) = rc14_atm_grc(g) * psnsha(p) endif end if diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 1c26b55cfd..3fad2da025 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -476,10 +476,10 @@ subroutine initialize2(ni,nj, currtime) call SatellitePhenologyInit(bounds_proc) end if if ( use_c14 .and. use_c14_bombspike ) then - call C14_init_BombSpike() + call C14_init_BombSpike( bounds_proc ) end if if ( use_c13 .and. use_c13_timeseries ) then - call C13_init_TimeSeries() + call C13_init_TimeSeries( bounds_proc ) end if else ! FATES OR Satellite phenology From 492d6e98f75279752f3b34f037473bda1246b4ae Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 26 Oct 2025 15:51:03 -0600 Subject: [PATCH 023/112] Remove a trailing whitespace --- src/biogeophys/PhotosynthesisMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index d5dedd3a16..0d44a3b24a 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -2067,7 +2067,7 @@ subroutine PhotosynthesisTotal (bounds, fn, filterp, & ! ! Determine total photosynthesis ! - use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries + use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries use CIsoAtmTimeseriesMod, only : rc13_atm_grc, rc14_atm_grc ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds From 3d73bb383ddaab9fc7f89fff5f470c3cdd624d4f Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 27 Oct 2025 09:00:56 -0600 Subject: [PATCH 024/112] Updated fates pointer to match fates branch fates-l2fr-conly --- src/fates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fates b/src/fates index c1dfc21c50..8c5a24ce88 160000 --- a/src/fates +++ b/src/fates @@ -1 +1 @@ -Subproject commit c1dfc21c505b5c8b29d4592b7d4a5e058239f6fb +Subproject commit 8c5a24ce880ac117dffa7f5b87427a57250af8ed From e296b2ed1984cf8852b72c5d7d62b0b91355d784 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 27 Oct 2025 09:02:56 -0600 Subject: [PATCH 025/112] set fleximod pointer to fatest testing branch --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index a9bfb47b0c..3a3298ccec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,8 +27,8 @@ # [submodule "fates"] path = src/fates -url = https://github.com/NGEET/fates -fxtag = sci.1.87.2_api.41.0.0 +url = https://github.com/rgknox/fates +fxbranch = cnp-l2fr-conly fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/fates From 425e254e4d1d466b6ba0e845128fbf875bd8aba1 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 27 Oct 2025 14:46:52 -0600 Subject: [PATCH 026/112] removed n/p spec argument to fates --- src/utils/clmfates_interfaceMod.F90 | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 31a22cef23..273c715d75 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -475,23 +475,6 @@ subroutine CLMFatesGlobals2() end if call set_fates_ctrlparms('use_tree_damage',ival=pass_tree_damage) - ! These may be in a non-limiting status (ie when supplements) - ! are added, but they are always allocated and cycled non-the less - ! FATES may want to interact differently with other models - ! that don't even have these arrays allocated. - ! FATES also checks that if NO3 is cycled in ELM, then - ! any plant affinity parameters are checked. - - if(use_nitrif_denitrif) then - call set_fates_ctrlparms('nitrogen_spec',ival=1) - else - call set_fates_ctrlparms('nitrogen_spec',ival=2) - end if - - ! Phosphorus is not tracked in CLM - call set_fates_ctrlparms('phosphorus_spec',ival=0) - - ! Pass spitfire mode values call set_fates_ctrlparms('spitfire_mode',ival=fates_spitfire_mode) call set_fates_ctrlparms('sf_nofire_def',ival=no_fire) From 6fd895ec62c474152290e99c620674e7c3a95131 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 27 Oct 2025 15:42:26 -0600 Subject: [PATCH 027/112] paramfile utils: Add failing tests for pft order obedience. --- python/ctsm/test/test_sys_set_paramfile.py | 40 +++++++++++++++ .../ctsm/test/test_unit_paramfile_shared.py | 50 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100755 python/ctsm/test/test_unit_paramfile_shared.py diff --git a/python/ctsm/test/test_sys_set_paramfile.py b/python/ctsm/test/test_sys_set_paramfile.py index bc8113d53b..f65c8e0d50 100755 --- a/python/ctsm/test/test_sys_set_paramfile.py +++ b/python/ctsm/test/test_sys_set_paramfile.py @@ -778,6 +778,46 @@ def test_set_paramfile_double_ok_given_int(self): ds_out = open_paramfile(output_path) self.assertFalse(sp.is_integer(ds_out[param_name].values)) + def test_set_paramfile_pft_order(self): + """ + Test that set_paramfile gives the same result regardless of the order you specify the PFTs + """ + + # First order + pfts_to_include = ["rice", "irrigated_rice"] + output0_path = os.path.join(self.tempdir, "output0.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output0_path, + "-p", + ",".join(pfts_to_include), + "mxmat=100,200", + ] + sp.main() + + # Reverse order + pfts_to_include.reverse() + output1_path = os.path.join(self.tempdir, "output1.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output1_path, + "-p", + ",".join(pfts_to_include), + "mxmat=200,100", + ] + sp.main() + + # These files should be identical + ds0 = open_paramfile(output0_path) + ds1 = open_paramfile(output1_path) + self.assertTrue(ds0.equals(ds1)) + if __name__ == "__main__": unit_testing.setup_for_tests() diff --git a/python/ctsm/test/test_unit_paramfile_shared.py b/python/ctsm/test/test_unit_paramfile_shared.py new file mode 100755 index 0000000000..cc4eb710db --- /dev/null +++ b/python/ctsm/test/test_unit_paramfile_shared.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +"""Unit tests for paramfile_shared""" + +import unittest + +from ctsm import unit_testing + +from ctsm.param_utils import paramfile_shared as ps + +# Allow names that pylint doesn't like, because otherwise I find it hard +# to make readable unit test names +# pylint: disable=invalid-name + + +class TestUnitGetSelectedPftIndices(unittest.TestCase): + """Unit tests of get_selected_pft_indices""" + + def test_get_selected_pft_indices_1strselected_onlyinlist(self): + """Check get_selected_pft_indices() given the only one in the list, as a string""" + selected_pfts = "rice" + pft_names = ["rice"] + result = ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + self.assertListEqual(result, [0]) + + def test_get_selected_pft_indices_1selected_onlyinlist(self): + """Check get_selected_pft_indices() given the only one in the list, as a list""" + selected_pfts = ["rice"] + pft_names = ["rice"] + result = ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + self.assertListEqual(result, [0]) + + def test_get_selected_pft_indices_2selected_sameorder(self): + """Check get_selected_pft_indices() given 2 selected in the same order as the list""" + pft_names = ["rice", "irrigated_rice"] + result = ps.get_selected_pft_indices(selected_pfts=pft_names, pft_names=pft_names) + self.assertListEqual(result, [0, 1]) + + def test_get_selected_pft_indices_2selected_difforder(self): + """Check get_selected_pft_indices() given 2 selected NOT in the same order as the list""" + pft_names = ["rice", "irrigated_rice"] + result = ps.get_selected_pft_indices( + selected_pfts=list(reversed(pft_names)), pft_names=pft_names + ) + self.assertListEqual(result, [1, 0]) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() From 62812ec75320f60c3aba66e81710238c24367e81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 27 Oct 2025 15:43:09 -0600 Subject: [PATCH 028/112] paramfile utils: Fix PFT order obedience. Resolves ESCOMP/CTSM#3571. --- python/ctsm/param_utils/paramfile_shared.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ctsm/param_utils/paramfile_shared.py b/python/ctsm/param_utils/paramfile_shared.py index b9860be0db..d12773dbdb 100644 --- a/python/ctsm/param_utils/paramfile_shared.py +++ b/python/ctsm/param_utils/paramfile_shared.py @@ -85,7 +85,9 @@ def get_selected_pft_indices(selected_pfts, pft_names): list of int Indices of selected PFTs. """ - indices = [i for i, name in enumerate(pft_names) if name in selected_pfts] + if isinstance(selected_pfts, str): + selected_pfts = [selected_pfts] + indices = [pft_names.index(pft) for pft in selected_pfts] return indices From c5c8b566e1db595a70906c8f3c716921bfd4bfcf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 27 Oct 2025 16:13:35 -0600 Subject: [PATCH 029/112] get_selected_pft_indices(): Test for ValueError when selecting PFT not in list. --- python/ctsm/test/test_unit_paramfile_shared.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ctsm/test/test_unit_paramfile_shared.py b/python/ctsm/test/test_unit_paramfile_shared.py index cc4eb710db..e0adebc7c2 100755 --- a/python/ctsm/test/test_unit_paramfile_shared.py +++ b/python/ctsm/test/test_unit_paramfile_shared.py @@ -44,6 +44,13 @@ def test_get_selected_pft_indices_2selected_difforder(self): ) self.assertListEqual(result, [1, 0]) + def test_get_selected_pft_indices_missing_valueerror(self): + """Check get_selected_pft_indices() given selected pft NOT in the list""" + selected_pfts = ["wheat"] + pft_names = ["rice", "irrigated_rice"] + with self.assertRaises(ValueError): + ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + if __name__ == "__main__": unit_testing.setup_for_tests() From c021623b15db8fd928e8199a29bb17d3d72ee2c7 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 28 Oct 2025 09:48:48 -0600 Subject: [PATCH 030/112] Make any of the C13/C14 isotope constants that can be private to be private --- src/main/clm_varcon.F90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/clm_varcon.F90 b/src/main/clm_varcon.F90 index 985660142a..234b89c797 100644 --- a/src/main/clm_varcon.F90 +++ b/src/main/clm_varcon.F90 @@ -131,23 +131,23 @@ module clm_varcon !!! C13 real(r8), public, parameter :: preind_atm_del13c = -6.0_r8 ! preindustrial value for atmospheric del13C - real(r8), public, parameter :: preind_atm_ratio = SHR_CONST_PDB + (preind_atm_del13c * SHR_CONST_PDB)/1000.0_r8 ! 13C/12C + real(r8), private, parameter :: preind_atm_ratio = SHR_CONST_PDB + (preind_atm_del13c * SHR_CONST_PDB)/1000.0_r8 ! 13C/12C real(r8), public :: c13ratio = preind_atm_ratio/(1.0_r8+preind_atm_ratio) ! 13C/(12+13)C preind atmosphere ! typical del13C for C3 photosynthesis (permil, relative to PDB) - real(r8), public, parameter :: c3_del13c = -28._r8 + real(r8), private, parameter :: c3_del13c = -28._r8 ! typical del13C for C4 photosynthesis (permil, relative to PDB) - real(r8), public, parameter :: c4_del13c = -13._r8 + real(r8), private, parameter :: c4_del13c = -13._r8 ! isotope ratio (13c/12c) for C3 photosynthesis - real(r8), public, parameter :: c3_r1 = SHR_CONST_PDB + ((c3_del13c*SHR_CONST_PDB)/1000._r8) + real(r8), private, parameter :: c3_r1 = SHR_CONST_PDB + ((c3_del13c*SHR_CONST_PDB)/1000._r8) ! isotope ratio (13c/[12c+13c]) for C3 photosynthesis real(r8), public, parameter :: c3_r2 = c3_r1/(1._r8 + c3_r1) ! isotope ratio (13c/12c) for C4 photosynthesis - real(r8), public, parameter :: c4_r1 = SHR_CONST_PDB + ((c4_del13c*SHR_CONST_PDB)/1000._r8) + real(r8), private, parameter :: c4_r1 = SHR_CONST_PDB + ((c4_del13c*SHR_CONST_PDB)/1000._r8) ! isotope ratio (13c/[12c+13c]) for C4 photosynthesis real(r8), public, parameter :: c4_r2 = c4_r1/(1._r8 + c4_r1) From 33c0518403cb341224a1bcdf06919c85c50c78f7 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 28 Oct 2025 09:50:44 -0600 Subject: [PATCH 031/112] Move references of the Carbon isotope datasets to inside of the timeseries handling as per the review, so setting to both constant preindustrial value or timeseries from the file is done in the same place --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 308 ++++++++++-------- src/biogeophys/PhotosynthesisMod.F90 | 25 +- 2 files changed, 185 insertions(+), 148 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index fa3c467eff..9abd2f0d42 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,12 +1,14 @@ module CIsoAtmTimeseriesMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use clm_time_manager , only : get_curr_date, get_curr_yearfrac - use clm_varcon , only : c14ratio, secspday + use clm_varcon , only : c13ratio, c14ratio, secspday use shr_const_mod , only : SHR_CONST_PDB ! Ratio of C13/C12 use clm_varctl , only : iulog use abortutils , only : endrun @@ -74,36 +76,48 @@ subroutine C14BombSpike( bounds ) integer :: l ! Loop index of sectors !----------------------------------------------------------------------- - ! get current date - call get_curr_date(yr, mon, day, tod) - dateyear = real(yr) + get_curr_yearfrac() - - ! find points in atm timeseries to interpolate between - ntim_atm_ts = size(atm_c14file_time) - ind_below = 0 - do nt = 1, ntim_atm_ts - if ((dateyear - time_axis_offset) >= atm_c14file_time(nt) ) then - ind_below = ind_below+1 - endif - end do - - ! loop over lat bands to pass all three to photosynthesis - do l = 1,nsectors_c14 - ! interpolate between nearest two points in atm c14 timeseries - if (ind_below .eq. 0 ) then - delc14o2_atm(l) = atm_delta_c14(l,1) - elseif (ind_below .eq. ntim_atm_ts ) then - delc14o2_atm(l) = atm_delta_c14(l,ntim_atm_ts) - else - twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c14file_time(ind_below)) & - / (atm_c14file_time(ind_below+1)-atm_c14file_time(ind_below)))) - twt_1 = 1._r8 - twt_2 - delc14o2_atm(l) = atm_delta_c14(l,ind_below) * twt_1 + atm_delta_c14(l,ind_below+1) * twt_2 - endif - - ! change delta units to ratio - rc14_atm(l) = (delc14o2_atm(l) * 1.e-3_r8 + 1._r8) * c14ratio - end do + ! + ! If the bombspike timeseries file is being used, read the file in + ! + if ( use_c14_bombspike )then + + ! get current date + call get_curr_date(yr, mon, day, tod) + dateyear = real(yr) + get_curr_yearfrac() + + ! find points in atm timeseries to interpolate between + ntim_atm_ts = size(atm_c14file_time) + ind_below = 0 + do nt = 1, ntim_atm_ts + if ((dateyear - time_axis_offset) >= atm_c14file_time(nt) ) then + ind_below = ind_below+1 + endif + end do + + ! loop over lat bands to pass all three to photosynthesis + do l = 1,nsectors_c14 + ! interpolate between nearest two points in atm c14 timeseries + if (ind_below .eq. 0 ) then + delc14o2_atm(l) = atm_delta_c14(l,1) + elseif (ind_below .eq. ntim_atm_ts ) then + delc14o2_atm(l) = atm_delta_c14(l,ntim_atm_ts) + else + twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c14file_time(ind_below)) & + / (atm_c14file_time(ind_below+1)-atm_c14file_time(ind_below)))) + twt_1 = 1._r8 - twt_2 + delc14o2_atm(l) = atm_delta_c14(l,ind_below) * twt_1 + atm_delta_c14(l,ind_below+1) * twt_2 + endif + + ! change delta units to ratio + rc14_atm(l) = (delc14o2_atm(l) * 1.e-3_r8 + 1._r8) * c14ratio + end do + ! + ! When not using a time series file -- use the constant preindustrial value + ! + else + rc14_atm(:) = c14ratio + delc14o2_atm(:) = (rc14_atm(1)/c14ratio -1.0_r8)*1000.0_r8 + endif ! ! Now map to the gridcell from the sectors ! @@ -119,14 +133,14 @@ subroutine C14BombSpike( bounds ) atm_delta_c14_grc(g) = delc14o2_atm(l) rc14_atm_grc(g) = rc14_atm(l) end do - + end subroutine C14BombSpike !----------------------------------------------------------------------- subroutine C14_init_BombSpike( bounds ) ! ! !DESCRIPTION: - ! read netcdf file containing a timeseries of atmospheric delta C14 values; save in module-level array + ! read netcdf file containing a timeseries of atmospheric delta C14 values; save in module-level array ! ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io @@ -147,68 +161,77 @@ subroutine C14_init_BombSpike( bounds ) character(len=*), parameter :: vname = 'Delta14co2_in_air' ! Variable name on file !----------------------------------------------------------------------- - call getfil(atm_c14_filename, locfn, 0) + ! + ! If the bombspike timeseries file is being used, read the file in + ! + if ( use_c14_bombspike )then - if ( masterproc ) then - write(iulog, *) 'C14_init_BombSpike: preparing to open file:' - write(iulog, *) trim(locfn) - endif + call getfil(atm_c14_filename, locfn, 0) - call ncd_pio_openfile (ncid, trim(locfn), 0) + if ( masterproc ) then + write(iulog, *) 'C14_init_BombSpike: preparing to open file:' + write(iulog, *) trim(locfn) + endif - call ncd_inqdlen(ncid,dimid,ntim,'time') - call ncd_inqdlen(ncid,dimid,nsec,'sector') - if ( nsec /= nsectors_c14 )then - call endrun(msg="ERROR: number of sectors on file not what's expected"//errMsg(sourcefile, __LINE__)) + call ncd_pio_openfile (ncid, trim(locfn), 0) + + call ncd_inqdlen(ncid,dimid,ntim,'time') + call ncd_inqdlen(ncid,dimid,nsec,'sector') + if ( nsec /= nsectors_c14 )then + call endrun(msg="ERROR: number of sectors on file not what's expected"//errMsg(sourcefile, __LINE__)) + end if + + !! allocate arrays based on size of netcdf timeseries + allocate(atm_c14file_time(ntim)) + allocate(atm_delta_c14(nsectors_c14,ntim)) + atm_delta_c14(:,:) = 0.0_r8 + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c14file_time, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) + end if + + call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c14, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) + end if + ! Check units + call check_units( ncid, vname, "Modern" ) + call ncd_pio_closefile(ncid) + + ! check to make sure that time dimension is well behaved + do t = 2, ntim + if ( atm_c14file_time(t) - atm_c14file_time(t-1) <= 0._r8 ) then + write(iulog, *) 'C14_init_BombSpike: error. time axis must be monotonically increasing' + call endrun(msg=errMsg(sourcefile, __LINE__)) + endif + end do end if - !! allocate arrays based on size of netcdf timeseries - allocate(atm_c14file_time(ntim)) - allocate(atm_delta_c14(nsectors_c14,ntim)) - atm_delta_c14(:,:) = 0.0_r8 - ! Allocate the gridcell arrays allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) allocate(rc14_atm_grc(bounds%begg:bounds%endg)) atm_delta_c14_grc(:) = nan rc14_atm_grc(:) = nan - call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c14file_time, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) - end if - - call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c14, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) - end if - ! Check units - call check_units( ncid, vname, "Modern" ) - call ncd_pio_closefile(ncid) - - ! check to make sure that time dimension is well behaved - do t = 2, ntim - if ( atm_c14file_time(t) - atm_c14file_time(t-1) <= 0._r8 ) then - write(iulog, *) 'C14_init_BombSpike: error. time axis must be monotonically increasing' - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - end do end subroutine C14_init_BombSpike !----------------------------------------------------------------------- - subroutine C13TimeSeries( bounds ) + subroutine C13TimeSeries( bounds, atm2lnd_inst ) ! ! !DESCRIPTION: ! for transient pulse simulation, impose a time-varying atm boundary condition ! use GridcellType , only : grc + use clm_varcon , only : preind_atm_del13c + use atm2lndType, only : atm2lnd_Type ! !ARGUMENTS: implicit none type(bounds_type), intent(in) :: bounds + type(atm2lnd_Type), intent(in) :: atm2lnd_inst ! ! !LOCAL VARIABLES: real(r8) :: rc13_atm ! Ratio of C13 to C12 @@ -221,42 +244,63 @@ subroutine C13TimeSeries( bounds ) real(r8) :: twt_1, twt_2 ! weighting fractions for interpolating !----------------------------------------------------------------------- - ! get current date - call get_curr_date(yr, mon, day, tod) - dateyear = real(yr) + get_curr_yearfrac() - - ! find points in atm timeseries to interpolate between - ntim_atm_ts = size(atm_c13file_time) - ind_below = 0 - do nt = 1, ntim_atm_ts - if ((dateyear - time_axis_offset) >= atm_c13file_time(nt) ) then - ind_below = ind_below+1 - endif - end do + ! + ! If the timeseries file is being used, read the file in + ! + if ( use_c13_timeseries )then + + ! get current date + call get_curr_date(yr, mon, day, tod) + dateyear = real(yr) + get_curr_yearfrac() + + ! find points in atm timeseries to interpolate between + ntim_atm_ts = size(atm_c13file_time) + ind_below = 0 + do nt = 1, ntim_atm_ts + if ((dateyear - time_axis_offset) >= atm_c13file_time(nt) ) then + ind_below = ind_below+1 + endif + end do + + ! interpolate between nearest two points in atm c13 timeseries + ! cdknotes. for now and for simplicity, just use the northern hemisphere values (sector 1) + if (ind_below .eq. 0 ) then + delc13o2_atm = atm_delta_c13(1) + elseif (ind_below .eq. ntim_atm_ts ) then + delc13o2_atm = atm_delta_c13(ntim_atm_ts) + else + twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c13file_time(ind_below)) & + / (atm_c13file_time(ind_below+1)-atm_c13file_time(ind_below)))) + twt_1 = 1._r8 - twt_2 + delc13o2_atm = atm_delta_c13(ind_below) * twt_1 + atm_delta_c13(ind_below+1) * twt_2 + endif - ! interpolate between nearest two points in atm c13 timeseries - ! cdknotes. for now and for simplicity, just use the northern hemisphere values (sector 1) - if (ind_below .eq. 0 ) then - delc13o2_atm = atm_delta_c13(1) - elseif (ind_below .eq. ntim_atm_ts ) then - delc13o2_atm = atm_delta_c13(ntim_atm_ts) + ! + ! When not using a time series file -- use the constant value + ! else - twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c13file_time(ind_below)) & - / (atm_c13file_time(ind_below+1)-atm_c13file_time(ind_below)))) - twt_1 = 1._r8 - twt_2 - delc13o2_atm = atm_delta_c13(ind_below) * twt_1 + atm_delta_c13(ind_below+1) * twt_2 - endif + delc13o2_atm = preind_atm_del13c + rc13_atm = c13ratio + end if ! change delta units to ratio, put on patch loop rc13_atm = (delc13o2_atm * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB + !SHR_ASSERT_FL( abs(rc13_atm - c13ratio) < epsilon(c13ratio), sourcefile, __LINE__) ! ! Copy to the gridcell arrays ! do g = bounds%begg, bounds%endg + + associate( & + forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) + forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) + ) atm_delta_c13_grc(g) = delc13o2_atm rc13_atm_grc(g) = rc13_atm + !SHR_ASSERT_FL( (rc13_atm == (forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) )), sourcefile, __LINE__) + end associate end do end subroutine C13TimeSeries @@ -265,7 +309,7 @@ end subroutine C13TimeSeries subroutine C13_init_TimeSeries( bounds ) ! ! !DESCRIPTION: - ! read netcdf file containing a timeseries of atmospheric delta C13 values; save in module-level array + ! read netcdf file containing a timeseries of atmospheric delta C13 values; save in module-level array ! ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io @@ -284,21 +328,51 @@ subroutine C13_init_TimeSeries( bounds ) logical :: readvar ! if variable read or not character(len=*), parameter :: vname = 'delta13co2_in_air' ! Variable name on file !----------------------------------------------------------------------- + ! + ! If the timeseries file is being used, read the file in + ! + if ( use_c13_timeseries )then - call getfil(atm_c13_filename, locfn, 0) + call getfil(atm_c13_filename, locfn, 0) - if ( masterproc ) then - write(iulog, *) 'C13_init_TimeSeries: preparing to open file:' - write(iulog, *) trim(locfn) - endif + if ( masterproc ) then + write(iulog, *) 'C13_init_TimeSeries: preparing to open file:' + write(iulog, *) trim(locfn) + endif + + call ncd_pio_openfile (ncid, trim(locfn), 0) - call ncd_pio_openfile (ncid, trim(locfn), 0) + call ncd_inqdlen(ncid,dimid,ntim,'time') - call ncd_inqdlen(ncid,dimid,ntim,'time') + !! allocate arrays based on size of netcdf timeseries + allocate(atm_c13file_time(ntim)) + allocate(atm_delta_c13(ntim)) - !! allocate arrays based on size of netcdf timeseries - allocate(atm_c13file_time(ntim)) - allocate(atm_delta_c13(ntim)) + + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c13file_time, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) + end if + + call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c13, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) + end if + + ! Check units + call check_units( ncid, vname, "VPDB" ) + call ncd_pio_closefile(ncid) + + ! check to make sure that time dimension is well behaved + do t = 2, ntim + if ( atm_c13file_time(t) - atm_c13file_time(t-1) <= 0._r8 ) then + write(iulog, *) 'C13_init_TimeSeries: error. time axis must be monotonically increasing' + call endrun(msg=errMsg(sourcefile, __LINE__)) + endif + end do + end if ! Allocate the gridcell arrays allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) @@ -306,30 +380,6 @@ subroutine C13_init_TimeSeries( bounds ) atm_delta_c13_grc(:) = nan rc13_atm_grc(:) = nan - call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c13file_time, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) - end if - - call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c13, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) - end if - - ! Check units - call check_units( ncid, vname, "VPDB" ) - call ncd_pio_closefile(ncid) - - ! check to make sure that time dimension is well behaved - do t = 2, ntim - if ( atm_c13file_time(t) - atm_c13file_time(t-1) <= 0._r8 ) then - write(iulog, *) 'C13_init_TimeSeries: error. time axis must be monotonically increasing' - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - end do - end subroutine C13_init_TimeSeries !----------------------------------------------------------------------- diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index 0d44a3b24a..d8300c9442 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -2067,7 +2067,7 @@ subroutine PhotosynthesisTotal (bounds, fn, filterp, & ! ! Determine total photosynthesis ! - use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries + use CIsoAtmTimeseriesMod, only : C14BombSpike, C13TimeSeries use CIsoAtmTimeseriesMod, only : rc13_atm_grc, rc14_atm_grc ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -2114,19 +2114,10 @@ subroutine PhotosynthesisTotal (bounds, fn, filterp, & fpsn_wp => photosyns_inst%fpsn_wp_patch & ! Output: [real(r8) (:) ] product-limited photosynthesis (umol CO2 /m**2 /s) ) - if ( use_c14 ) then - if (use_c14_bombspike) then - call C14BombSpike(bounds) - else - rc14_atm_grc(:) = c14ratio - end if - end if - - if ( use_c13 ) then - if (use_c13_timeseries) then - call C13TimeSeries(bounds) - end if - end if + ! Get the current C13/C14 ratio in the atmosphere from timeseries data or the fixed values + ! These calls fill the data: rc13_atm_grc and rc14_atm_grc + if ( use_c14 ) call C14BombSpike(bounds) + if ( use_c13 ) call C13TimeSeries(bounds, atm2lnd_inst) do f = 1, fn p = filterp(f) @@ -2141,11 +2132,7 @@ subroutine PhotosynthesisTotal (bounds, fn, filterp, & if (use_cn) then if ( use_c13 ) then - if (use_c13_timeseries) then - rc13_canair(p) = rc13_atm_grc(g) - else - rc13_canair(p) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) - endif + rc13_canair(p) = rc13_atm_grc(g) rc13_psnsun(p) = rc13_canair(p)/alphapsnsun(p) rc13_psnsha(p) = rc13_canair(p)/alphapsnsha(p) c13_psnsun(p) = psnsun(p) * (rc13_psnsun(p)/(1._r8+rc13_psnsun(p))) From 5a01ffff3ee6aa85478bfcd7e7c10b4eb8a5f9ff Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 28 Oct 2025 12:41:49 -0600 Subject: [PATCH 032/112] changed cnallocate_carbon_only() routines to be CN agnostic --- src/main/clm_varctl.F90 | 14 +++++++------- .../SoilBiogeochemCompetitionMod.F90 | 12 ++++++------ src/utils/clmfates_interfaceMod.F90 | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 9a0d2901b9..a49d877d29 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -11,8 +11,8 @@ module clm_varctl ! !PUBLIC MEMBER FUNCTIONS: implicit none public :: clm_varctl_set ! Set variables - public :: cnallocate_carbon_only_set - public :: cnallocate_carbon_only + public :: allocate_carbon_only_set + public :: allocate_carbon_only ! private save @@ -586,14 +586,14 @@ subroutine clm_varctl_set( caseid_in, ctitle_in, brnch_retain_casename_in, & end subroutine clm_varctl_set ! Set module carbon_only flag - subroutine cnallocate_carbon_only_set(carbon_only_in) + subroutine allocate_carbon_only_set(carbon_only_in) logical, intent(in) :: carbon_only_in carbon_only = carbon_only_in - end subroutine cnallocate_carbon_only_set + end subroutine allocate_carbon_only_set ! Get module carbon_only flag - logical function CNAllocate_Carbon_only() - cnallocate_carbon_only = carbon_only - end function CNAllocate_Carbon_only + logical function Allocate_Carbon_only() + allocate_carbon_only = carbon_only + end function Allocate_Carbon_only end module clm_varctl diff --git a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 index 57bc82984e..041f6ee740 100644 --- a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 @@ -76,7 +76,7 @@ subroutine readParams ( ncid ) type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id ! ! !LOCAL VARIABLES: - character(len=32) :: subname = 'CNAllocParamsType' + character(len=32) :: subname = 'readParams' character(len=100) :: errCode = '-Error reading in parameters file:' logical :: readv ! has variable been read in or not real(r8) :: tempr ! temporary to read in parameter @@ -130,7 +130,7 @@ subroutine SoilBiogeochemCompetitionInit ( bounds) ! !USES: use clm_varcon , only: secspday use clm_time_manager, only: get_step_size_real - use clm_varctl , only: iulog, cnallocate_carbon_only_set + use clm_varctl , only: iulog, allocate_carbon_only_set use shr_infnan_mod , only: nan => shr_infnan_nan, assignment(=) ! ! !ARGUMENTS: @@ -160,7 +160,7 @@ subroutine SoilBiogeochemCompetitionInit ( bounds) errMsg(sourcefile, __LINE__)) end select - call cnallocate_carbon_only_set(carbon_only) + call allocate_carbon_only_set(carbon_only) end subroutine SoilBiogeochemCompetitionInit @@ -175,7 +175,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu soilbiogeochem_nitrogenflux_inst,canopystate_inst) ! ! !USES: - use clm_varctl , only: cnallocate_carbon_only, iulog + use clm_varctl , only: allocate_carbon_only, iulog use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions use clm_varpar , only: i_cop_mic, i_oli_mic use clm_varcon , only: nitrif_n2o_loss_frac @@ -338,7 +338,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu fpi_vr(c,j) = 1.0_r8 actual_immob_vr(c,j) = potential_immob_vr(c,j) sminn_to_plant_vr(c,j) = plant_ndemand(c) * nuptake_prof(c,j) - else if ( cnallocate_carbon_only()) then !.or. & + else if ( allocate_carbon_only()) then !.or. & ! this code block controls the addition of N to sminn pool ! to eliminate any N limitation, when Carbon_Only is set. This lets the ! model behave essentially as a carbon-only model, but with the @@ -729,7 +729,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu ! eliminate N limitations, so there is still a diagnostic quantity ! that describes the degree of N limitation at steady-state. - if ( cnallocate_carbon_only()) then !.or. & + if ( allocate_carbon_only()) then !.or. & if ( fpi_no3_vr(c,j) + fpi_nh4_vr(c,j) < 1._r8 ) then fpi_nh4_vr(c,j) = 1.0_r8 - fpi_no3_vr(c,j) supplement_to_sminn_vr(c,j) = (potential_immob_vr(c,j) & diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 273c715d75..3216c98ac5 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -80,7 +80,7 @@ module CLMFatesInterfaceMod use clm_varctl , only : use_lch4 use clm_varctl , only : fates_history_dimlevel use clm_varctl , only : nsrest, nsrBranch - use clm_varctl , only : CNAllocate_Carbon_only + use clm_varctl , only : Allocate_Carbon_only use clm_varcon , only : tfrz use clm_varcon , only : spval use clm_varcon , only : denice @@ -1332,7 +1332,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & end do - if(CNAllocate_Carbon_only())then + if(Allocate_Carbon_only())then nitr_suppl = .true. else nitr_suppl = .false. From 2a89883ea0dfb971cd5c5c61e03b9cb41ea2b3a2 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 28 Oct 2025 14:45:43 -0400 Subject: [PATCH 033/112] Update src/utils/clmfates_interfaceMod.F90 Co-authored-by: Erik Kluzek --- src/utils/clmfates_interfaceMod.F90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 3216c98ac5..81f8a78b8e 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -1145,12 +1145,12 @@ subroutine dynamics_driv(this, nc, bounds_clump, & real(r8) :: s_node, smp_node ! local for relative water content and potential logical :: after_start_of_harvest_ts integer :: iharv - logical :: nitr_suppl ! Is CLM currently supplementing N - logical, parameter :: phos_dummy_suppl = .true. ! This argument is needed for FATES + logical :: nitr_suppl ! true -> CLM is supplementing Nitrogen + logical, parameter :: phos_dummy_suppl = .true. ! true -> Phosphorus is NOT limited (i.e. supplemented) + ! This argument is needed for FATES ! to specify if phosphorus is being - ! supplemented, this is not cycled in CLM - ! so we set it to TRUE (which essentially - ! means it is NOT limiting) + ! supplemented, Phosphorous is not limited in CLM + ! so we set it to TRUE !----------------------------------------------------------------------- ! --------------------------------------------------------------------------------- From dd891e6bce6a9509d285dbf29b1b554541468edd Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 28 Oct 2025 12:47:45 -0600 Subject: [PATCH 034/112] uppdated some text --- src/main/clm_varctl.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index a49d877d29..83133acf2b 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -585,13 +585,13 @@ subroutine clm_varctl_set( caseid_in, ctitle_in, brnch_retain_casename_in, & end subroutine clm_varctl_set - ! Set module carbon_only flag + ! Set module carbon_only flag (applies to both CN and FATES) subroutine allocate_carbon_only_set(carbon_only_in) logical, intent(in) :: carbon_only_in carbon_only = carbon_only_in end subroutine allocate_carbon_only_set - ! Get module carbon_only flag + ! Get module carbon_only flag (applies to both CN and FATES) logical function Allocate_Carbon_only() allocate_carbon_only = carbon_only end function Allocate_Carbon_only From bb679d1a7cd21684259081fa3575df217b2b2f9e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 28 Oct 2025 15:49:03 -0600 Subject: [PATCH 035/112] Apply suggestion from @ekluzek Have the fates submodule point to the hash along the branch rather than the branch itself. This means we get a fixed version for testing rather than just the tip of the branch. --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 3a3298ccec..eb911c882b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,8 @@ [submodule "fates"] path = src/fates url = https://github.com/rgknox/fates -fxbranch = cnp-l2fr-conly +#fxtag = cnp-l2fr-conly # This is the branch being pointed to, the hash used on the branch is below +fxtag = 8c5a24ce880ac117dffa7f5b87427a57250af8ed fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/fates From 4c1c4d5a59f8ead05841af78f93e39532b562b23 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 28 Oct 2025 20:18:19 -0600 Subject: [PATCH 036/112] More changes to remove the CN prefix to CNAllocate_Carbon_Only getter --- src/biogeophys/PhotosynthesisMod.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index b88fb170c7..e7784d1184 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -1257,7 +1257,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & use clm_varcon , only : rgas, tfrz, spval use GridcellType , only : grc use clm_time_manager , only : get_step_size_real, is_near_local_noon - use clm_varctl , only : cnallocate_carbon_only + use clm_varctl , only : allocate_carbon_only use clm_varctl , only : lnc_opt, reduce_dayl_factor, vcmax_opt use pftconMod , only : nbrdlf_dcd_tmp_shrub @@ -1642,7 +1642,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & if (.not. use_cn) then vcmax25top = vcmax25top * fnitr(patch%itype(p)) else - if ( CNAllocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) + if ( Allocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) end if else if (vcmax_opt == 3) then vcmax25top = ( i_vcad(patch%itype(p)) + s_vcad(patch%itype(p)) * lnc(p) ) * dayl_factor(p) @@ -2750,7 +2750,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & use clm_varcon , only : rgas, tfrz, rpi, spval use GridcellType , only : grc use clm_time_manager , only : get_step_size_real, is_near_local_noon - use clm_varctl , only : cnallocate_carbon_only + use clm_varctl , only : allocate_carbon_only use clm_varctl , only : lnc_opt, reduce_dayl_factor, vcmax_opt use clm_varpar , only : nlevsoi use pftconMod , only : nbrdlf_dcd_tmp_shrub @@ -3267,7 +3267,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & if (.not. use_cn) then vcmax25top = vcmax25top * fnitr(patch%itype(p)) else - if ( CNAllocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) + if ( Allocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) end if else if (vcmax_opt == 3) then vcmax25top = ( i_vcad(patch%itype(p)) + s_vcad(patch%itype(p)) * lnc(p) ) * dayl_factor(p) From 2b46ae57695862aa0fd4a6ed00ddb57d4df27c9d Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Wed, 29 Oct 2025 10:03:20 -0600 Subject: [PATCH 037/112] updated fleximod and file pointer to fates api42 --- .gitmodules | 5 ++--- src/fates | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index eb911c882b..8ede482f6a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,9 +27,8 @@ # [submodule "fates"] path = src/fates -url = https://github.com/rgknox/fates -#fxtag = cnp-l2fr-conly # This is the branch being pointed to, the hash used on the branch is below -fxtag = 8c5a24ce880ac117dffa7f5b87427a57250af8ed +url = https://github.com/NGEET/fates +fxtag = sci.1.88.0_api.41.0.0 fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/fates diff --git a/src/fates b/src/fates index 8c5a24ce88..5bb36cba29 160000 --- a/src/fates +++ b/src/fates @@ -1 +1 @@ -Subproject commit 8c5a24ce880ac117dffa7f5b87427a57250af8ed +Subproject commit 5bb36cba29b95295aca5aedc13f348ef1e001b99 From 7786e26886de8e144fd718d112f64a7d03797eb8 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Wed, 29 Oct 2025 10:06:19 -0600 Subject: [PATCH 038/112] added tag --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 8ede482f6a..2abc02aa48 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,7 @@ [submodule "fates"] path = src/fates url = https://github.com/NGEET/fates -fxtag = sci.1.88.0_api.41.0.0 +fxtag = sci.1.88.0_api.42.0.0 fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/fates From 065a22005f9214b5110483363f00dbe8d517e0eb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 29 Oct 2025 13:21:49 -0600 Subject: [PATCH 039/112] Add the expected fail for #3500 on Izumi commented out, the compset longname violates the expected fail, so we can't have it uncommented yet --- cime_config/testdefs/ExpectedTestFails.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index f8f05bf5f1..1acc725738 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -288,6 +288,15 @@ + + FAIL From 9a7f022e7cec3b379951ad6ab9da2ef1833288e8 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 29 Oct 2025 13:27:27 -0600 Subject: [PATCH 040/112] Changes needed for unit testing to work on my Mac For the minimum version check: an update to 3.5 was required; updating to 3.10 avoided a deprecation warning. --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2682775ca5..c15727a859 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY}) include(CIME_initial_setup) @@ -93,7 +93,7 @@ add_library(csm_share ${share_sources} ${drv_sources_needed}) declare_generated_dependencies(csm_share "${share_genf90_sources}") add_library(clm ${clm_sources}) declare_generated_dependencies(clm "${clm_genf90_sources}") -add_dependencies(clm csm_share esmf) +add_dependencies(clm csm_share ESMF) # We need to look for header files here, in order to pick up shr_assert.h include_directories(${CLM_ROOT}/share/include) From 6ec762ba3169f7419024f121f92c53b5a42ff4b8 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Wed, 29 Oct 2025 15:46:03 -0600 Subject: [PATCH 041/112] update change logs --- doc/ChangeLog | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 75 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 551de3ae12..ddd4e0554e 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,78 @@ =============================================================== +Tag name: ctsm5.3.083 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) +Date: Wed 29 Oct 2025 03:35:50 PM MDT +One-line Summary: Changes to coupling of supplementation status with FATES. + +Purpose and description of changes +---------------------------------- + +Supplementation status is now passed to FATES as a run-time boundary condition. This set of changes is synchronized with FATES-side changes that were oriented around how supplementation status impacts whether or not fine-root proportions are allowed to change during CNP runs. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? No + +Bugs fixed +---------- + +No bugs were fixed. + +Notes of particular relevance for users +--------------------------------------- + +When CTSM-FATES has nutrient coupling, note that FATES plants will default to not changing L2FR when supplementing nitrogen. + +Caveats for users (e.g., need to interpolate initial conditions): None + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): None + +Changes made to namelist defaults (e.g., changed parameter values): None + +Changes to the datasets (e.g., parameter, surface or initial files): None + +Changes to documentation: None necessary. + +Substantial timing or memory changes: None + +Notes of particular relevance for developers: +--------------------------------------------- + +We had been passing a stealth namelist variable from CTSM to FATES, it gave FATES information about what types of mineralized nutrient were available (ie NO3 or NH4), but FATES has never used this information, so it was removed. + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): None + +Changes to tests or testing: None + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: No answer changes with one exception. Some FATES-specific variables related to radiation diagnostics and error tracking changed with updating the FATES tag. These were diagnostic only changes, and not related to FATES internals changing in any way. + +ther details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/CTSM/pull/3348 + + +=============================================================== +=============================================================== Tag name: ctsm5.3.082 Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) Date: Fri 24 Oct 2025 11:17:41 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index e19584ae9d..f2b5862a28 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases ctsm5.3.081 erik 10/22/2025 Change defaults for when Carbon isotopes are turned on, and turn on irrigate for Sp/Bgc cases for clm6_0 historical transient cases ctsm5.3.080 samrabin 10/16/2025 Merge b4b-dev to master From 9114c6f6f73188b4da4c5684fd69b1446a808c95 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 02:13:26 -0600 Subject: [PATCH 042/112] If C13/C14 is on, call the time-series initialize no matter if constant or timeseries being used --- src/main/clm_initializeMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 3fad2da025..387f4cd4ca 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -475,10 +475,10 @@ subroutine initialize2(ni,nj, currtime) ! Also do this for FATES see below call SatellitePhenologyInit(bounds_proc) end if - if ( use_c14 .and. use_c14_bombspike ) then + if ( use_c14 ) then call C14_init_BombSpike( bounds_proc ) end if - if ( use_c13 .and. use_c13_timeseries ) then + if ( use_c13 ) then call C13_init_TimeSeries( bounds_proc ) end if From a9baf0b5e4f51f530a16e0d83833d99c8087d8a4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 02:14:56 -0600 Subject: [PATCH 043/112] Get the fixed C13/C14 option working with some testing around it --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 9abd2f0d42..bb8adcec19 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -121,6 +121,17 @@ subroutine C14BombSpike( bounds ) ! ! Now map to the gridcell from the sectors ! + + SHR_ASSERT_FL( allocated(atm_delta_c14_grc), sourcefile, __LINE__) + SHR_ASSERT_FL( allocated(rc14_atm_grc), sourcefile, __LINE__) + SHR_ASSERT_FL( (lbound(atm_delta_c14_grc, 1) == bounds%begg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (lbound(rc14_atm_grc, 1) == bounds%begg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(atm_delta_c14_grc, 1) == bounds%endg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(rc14_atm_grc, 1) == bounds%endg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (lbound(rc14_atm, 1) == 1 ), sourcefile, __LINE__) + SHR_ASSERT_FL( (lbound(delc14o2_atm, 1) == 1 ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(rc14_atm, 1) == nsectors_c14 ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(delc14o2_atm, 1) == nsectors_c14 ), sourcefile, __LINE__) do g = bounds%begg, bounds%endg ! determine latitute sector for radiocarbon bomb spike inputs if ( grc%latdeg(g) >= 30._r8 ) then @@ -279,14 +290,17 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) ! When not using a time series file -- use the constant value ! else - delc13o2_atm = preind_atm_del13c rc13_atm = c13ratio + delc13o2_atm = (rc13_atm/SHR_CONST_PDB - 1.0_r8)*1000.0_r8 end if ! change delta units to ratio, put on patch loop rc13_atm = (delc13o2_atm * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB - !SHR_ASSERT_FL( abs(rc13_atm - c13ratio) < epsilon(c13ratio), sourcefile, __LINE__) + !if ( .not. use_c13_timeseries )then + !write(iulog,*) 'rc13_atm, c13ratio, epsilon(c13ratio) = ', rc13_atm, c13ratio, epsilon(c13ratio) + !SHR_ASSERT_FL( abs(rc13_atm - c13ratio) < epsilon(c13ratio), sourcefile, __LINE__) + !end if ! ! Copy to the gridcell arrays @@ -297,9 +311,16 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) ) - atm_delta_c13_grc(g) = delc13o2_atm rc13_atm_grc(g) = rc13_atm - !SHR_ASSERT_FL( (rc13_atm == (forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) )), sourcefile, __LINE__) + atm_delta_c13_grc(g) = delc13o2_atm + if ( .not. use_c13_timeseries )then + rc13_atm_grc(g) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) + atm_delta_c13_grc(g) = (rc13_atm_grc(g) / SHR_CONST_PDB - 1.0_r8)*1000.0_r8 + !write(iulog,*) 'c13ratio, pc13o2 = ', c13ratio, forc_pc13o2(g) + !write(iulog,*) 'rc13_atm_grc, c13o2/(co2 - c13o2), epsilon(c13ratio) = ', rc13_atm_grc(g), & + !(forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g))), epsilon(c13ratio) + SHR_ASSERT_FL( (abs(rc13_atm_grc(g) - (forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) )) <= epsilon(c13ratio)*10._r8 ), sourcefile, __LINE__) + end if end associate end do From 8d2d0aa613436da475dc2d277f4ea69ed6d2d9bf Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 12:42:17 -0600 Subject: [PATCH 044/112] Start adding change files --- doc/ChangeLog | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 88 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index ddd4e0554e..40f70eb3e6 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,91 @@ =============================================================== +Tag name: ctsm5.3.084 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Thu 30 Oct 2025 12:41:47 PM MDT +One-line Summary: Merge b4b-dev to master + +Purpose and description of changes +---------------------------------- + +Bring changes on b4b-dev to master + +- Fix FUNIT testing on Izumi and on Mac's +- Some fixes to set_paramfile +- Don't auto build documentation on personal forks, only on ESCOMP + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Fixes #3182 -- FUNIT tests on Izumi + Fixes #3571 -- set_paramfile ordering of PFT's from user doesn't have to match paramfile order + Fixes #3559 -- Correct Ndims error + Fixes #3369 -- Docs build and deploy only runs on ESCOMP not personal forks + +Notes of particular relevance for users +--------------------------------------- + +Changes to documentation: + Some changes to set_paramfile documentation + +Notes of particular relevance for developers: +--------------------------------------------- + +Testing summary: regular +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- + izumi ------- + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: No bit-for-bit + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + -- #3577 unit testing on Mac + -- #3572 set_paramfile ordering + -- #3560 Fix Ndim error in set_paramfile + -- #3557 doc/build/run/deploy only on ESCOMP + +=============================================================== +=============================================================== Tag name: ctsm5.3.083 Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) Date: Wed 29 Oct 2025 03:35:50 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index f2b5862a28..8a24f6074a 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.084 erik 10/30/2025 Merge b4b-dev to master ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases ctsm5.3.081 erik 10/22/2025 Change defaults for when Carbon isotopes are turned on, and turn on irrigate for Sp/Bgc cases for clm6_0 historical transient cases From cb8255496b66bf9bce9095b00a7ce6de078e7fd0 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 15:51:05 -0600 Subject: [PATCH 045/112] Remove the SHR_ASSERT tests as they were failing with threaded cases, and they are uneeded now --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index bb8adcec19..8c837a22ce 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,7 +1,5 @@ module CIsoAtmTimeseriesMod -#include "shr_assert.h" - !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! @@ -122,16 +120,6 @@ subroutine C14BombSpike( bounds ) ! Now map to the gridcell from the sectors ! - SHR_ASSERT_FL( allocated(atm_delta_c14_grc), sourcefile, __LINE__) - SHR_ASSERT_FL( allocated(rc14_atm_grc), sourcefile, __LINE__) - SHR_ASSERT_FL( (lbound(atm_delta_c14_grc, 1) == bounds%begg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (lbound(rc14_atm_grc, 1) == bounds%begg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(atm_delta_c14_grc, 1) == bounds%endg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(rc14_atm_grc, 1) == bounds%endg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (lbound(rc14_atm, 1) == 1 ), sourcefile, __LINE__) - SHR_ASSERT_FL( (lbound(delc14o2_atm, 1) == 1 ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(rc14_atm, 1) == nsectors_c14 ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(delc14o2_atm, 1) == nsectors_c14 ), sourcefile, __LINE__) do g = bounds%begg, bounds%endg ! determine latitute sector for radiocarbon bomb spike inputs if ( grc%latdeg(g) >= 30._r8 ) then @@ -297,10 +285,6 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) ! change delta units to ratio, put on patch loop rc13_atm = (delc13o2_atm * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB - !if ( .not. use_c13_timeseries )then - !write(iulog,*) 'rc13_atm, c13ratio, epsilon(c13ratio) = ', rc13_atm, c13ratio, epsilon(c13ratio) - !SHR_ASSERT_FL( abs(rc13_atm - c13ratio) < epsilon(c13ratio), sourcefile, __LINE__) - !end if ! ! Copy to the gridcell arrays @@ -313,13 +297,11 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) ) rc13_atm_grc(g) = rc13_atm atm_delta_c13_grc(g) = delc13o2_atm + + ! Currently when C13 is fixed, it's dependent on CO2 levels and changes with pressure if ( .not. use_c13_timeseries )then rc13_atm_grc(g) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) atm_delta_c13_grc(g) = (rc13_atm_grc(g) / SHR_CONST_PDB - 1.0_r8)*1000.0_r8 - !write(iulog,*) 'c13ratio, pc13o2 = ', c13ratio, forc_pc13o2(g) - !write(iulog,*) 'rc13_atm_grc, c13o2/(co2 - c13o2), epsilon(c13ratio) = ', rc13_atm_grc(g), & - !(forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g))), epsilon(c13ratio) - SHR_ASSERT_FL( (abs(rc13_atm_grc(g) - (forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) )) <= epsilon(c13ratio)*10._r8 ), sourcefile, __LINE__) end if end associate end do From 8d08ed60b05e6f24503713752971f4ad2301db4f Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 15:59:11 -0600 Subject: [PATCH 046/112] Remove unused references to c13 and c14 as they are unused in the actual code --- src/biogeophys/CanopyFluxesMod.F90 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index ad3d171c43..3b0d990b21 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -13,7 +13,7 @@ module CanopyFluxesMod use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun - use clm_varctl , only : iulog, use_cn, use_lch4, use_c13, use_c14, use_cndv, use_fates, & + use clm_varctl , only : iulog, use_cn, use_lch4, use_c13, use_cndv, use_fates, & use_luna, use_hydrstress, use_biomass_heat_storage, z0param_method use clm_varpar , only : nlevgrnd, nlevsno, nlevcan, mxpft use pftconMod , only : pftcon @@ -229,7 +229,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, use clm_time_manager , only : get_step_size_real, get_prev_date, is_near_local_noon use clm_varcon , only : sb, cpair, hvap, vkc, grav, denice, c_to_b use clm_varcon , only : denh2o, tfrz, tlsai_crit, alpha_aero - use clm_varcon , only : c14ratio, spval + use clm_varcon , only : spval use clm_varcon , only : c_water, c_dry_biomass, c_to_b use clm_varcon , only : nu_param, cd1_param use perf_mod , only : t_startf, t_stopf @@ -354,7 +354,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, real(r8) :: err(bounds%begp:bounds%endp) ! balance error real(r8) :: erre ! balance error real(r8) :: co2(bounds%begp:bounds%endp) ! atmospheric co2 partial pressure (pa) - real(r8) :: c13o2(bounds%begp:bounds%endp) ! atmospheric c13o2 partial pressure (pa) real(r8) :: o2(bounds%begp:bounds%endp) ! atmospheric o2 partial pressure (pa) real(r8) :: svpts(bounds%begp:bounds%endp) ! saturation vapor pressure at t_veg (pa) real(r8) :: eah(bounds%begp:bounds%endp) ! canopy air vapor pressure (pa) @@ -479,7 +478,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, forc_u => atm2lnd_inst%forc_u_grc , & ! Input: [real(r8) (:) ] atmospheric wind speed in east direction (m/s) forc_v => atm2lnd_inst%forc_v_grc , & ! Input: [real(r8) (:) ] atmospheric wind speed in north direction (m/s) forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) - forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc , & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) forc_po2 => atm2lnd_inst%forc_po2_grc , & ! Input: [real(r8) (:) ] partial pressure o2 (Pa) tc_ref2m => humanindex_inst%tc_ref2m_patch , & ! Output: [real(r8) (:) ] 2 m height surface air temperature (C) @@ -961,10 +959,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, co2(p) = forc_pco2(g) o2(p) = forc_po2(g) - if ( use_c13 ) then - c13o2(p) = forc_pc13o2(g) - end if - ! Initialize flux profile nmozsgn(p) = 0 From 0b2b48d480bbaa2a3cc872a9c6b3360c017824d2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 16:04:03 -0600 Subject: [PATCH 047/112] Remove forc_c13o2 as it's now unused --- src/biogeophys/PhotosynthesisMod.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index d8300c9442..73a5099876 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -2084,7 +2084,6 @@ subroutine PhotosynthesisTotal (bounds, fn, filterp, & associate( & forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) - forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc , & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) forc_po2 => atm2lnd_inst%forc_po2_grc , & ! Input: [real(r8) (:) ] partial pressure o2 (Pa) laisun => canopystate_inst%laisun_patch , & ! Input: [real(r8) (:) ] sunlit leaf area From c1c71df4b8506c24b6c7e0382f7c9dd275c6f5eb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 30 Oct 2025 16:11:40 -0600 Subject: [PATCH 048/112] Add a note that this duplicates the partial pressure calculation in lnd_import_export --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 8c837a22ce..2c2492e484 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -299,6 +299,7 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) atm_delta_c13_grc(g) = delc13o2_atm ! Currently when C13 is fixed, it's dependent on CO2 levels and changes with pressure + ! NOTE: This duplicates code in lnd_import_export.F90 if ( .not. use_c13_timeseries )then rc13_atm_grc(g) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) atm_delta_c13_grc(g) = (rc13_atm_grc(g) / SHR_CONST_PDB - 1.0_r8)*1000.0_r8 From 817afc93b24c4a4f59a7bff5ca3d410982db7371 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 31 Oct 2025 13:15:09 -0600 Subject: [PATCH 049/112] Update to final Change files --- doc/ChangeLog | 13 ++++++------- doc/ChangeSum | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 40f70eb3e6..a2131b269b 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.3.084 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) -Date: Thu 30 Oct 2025 12:41:47 PM MDT +Date: Fri 31 Oct 2025 01:12:26 PM MDT One-line Summary: Merge b4b-dev to master Purpose and description of changes @@ -9,7 +9,7 @@ Purpose and description of changes Bring changes on b4b-dev to master -- Fix FUNIT testing on Izumi and on Mac's +- Fix FUNIT testing on Mac's - Some fixes to set_paramfile - Don't auto build documentation on personal forks, only on ESCOMP @@ -33,7 +33,6 @@ Does this tag change answers significantly for any of the following physics conf Bugs fixed ---------- List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: - Fixes #3182 -- FUNIT tests on Izumi Fixes #3571 -- set_paramfile ordering of PFT's from user doesn't have to match paramfile order Fixes #3559 -- Correct Ndims error Fixes #3369 -- Docs build and deploy only runs on ESCOMP not personal forks @@ -54,16 +53,16 @@ Testing summary: regular build-namelist tests (if CLMBuildNamelist.pm has changed): - derecho - + derecho - OK python testing (if python code has changed; see instructions in python/README.md; document testing done): - derecho - + derecho - PASS regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - derecho ----- - izumi ------- + derecho ----- OK + izumi ------- OK If the tag used for baseline comparisons was NOT the previous tag, note that here: diff --git a/doc/ChangeSum b/doc/ChangeSum index 8a24f6074a..cf6a49e8fd 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.3.084 erik 10/30/2025 Merge b4b-dev to master + ctsm5.3.084 erik 10/31/2025 Merge b4b-dev to master ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases ctsm5.3.081 erik 10/22/2025 Change defaults for when Carbon isotopes are turned on, and turn on irrigate for Sp/Bgc cases for clm6_0 historical transient cases From dd1237f94ec64f6048c4a63fd1b43d6dbd1472d4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 31 Oct 2025 17:17:30 -0600 Subject: [PATCH 050/112] Remove meshfile which isn't needed --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index e4e018a39e..35f3b30a5b 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -47,7 +47,6 @@ module CIsoAtmTimeseriesMod ! Private data for the control namelist: character(len=CL), private :: stream_fldfilename_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' - character(len=CL), private :: stream_meshfile_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/mesh_4x1_global_c20251013.nc' character(len=CL), private :: stream_fldfilename_atm_c13 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' integer, private :: stream_year_first_atm_c14 = 1850 integer, private :: stream_year_last_atm_c14 = 2023 @@ -193,7 +192,6 @@ subroutine C14_init_BombSpike() call get_proc_bounds( bounds_proc ) call atm_c14_stream%Init( bounds_proc, & fldfilename=stream_fldfilename_atm_c14, & - !meshfile= stream_meshfile_atm_c14, & meshfile= 'none', & mapalgo=stream_mapalgo_atm_c14, & tintalgo=stream_tintalgo_atm_c14, & From d7d7c3455ef4caf23966fe70c9fce15a0d8265a2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 31 Oct 2025 17:35:32 -0600 Subject: [PATCH 051/112] Add some tests to ensure that ig and g are identical --- src/cpl/share_esmf/FireDataBaseType.F90 | 2 ++ src/cpl/share_esmf/PrigentRoughnessStreamType.F90 | 2 ++ src/cpl/share_esmf/cropcalStreamMod.F90 | 1 + src/cpl/share_esmf/ndepStreamMod.F90 | 4 ++++ 4 files changed, 9 insertions(+) diff --git a/src/cpl/share_esmf/FireDataBaseType.F90 b/src/cpl/share_esmf/FireDataBaseType.F90 index fd8b140c95..aa9395b770 100644 --- a/src/cpl/share_esmf/FireDataBaseType.F90 +++ b/src/cpl/share_esmf/FireDataBaseType.F90 @@ -338,6 +338,7 @@ subroutine hdm_interp( this, bounds) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%forc_hdm(g) = dataptr1d(ig) end do @@ -506,6 +507,7 @@ subroutine lnfm_interp(this, bounds ) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%forc_lnfm(g) = dataptr1d(ig) end do diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index afc65f2ece..158c75e434 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -1,5 +1,6 @@ module PrigentRoughnessStreamType +#include "shr_assert.h" !----------------------------------------------------------------------- ! !DESCRIPTION: @@ -151,6 +152,7 @@ subroutine Init(this, bounds, NLFilename) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%prigent_rghn(g) = dataptr1d(ig) end do diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 591476e59e..df48ef5e98 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -480,6 +480,7 @@ subroutine cropcal_advance( bounds ) do g = begg,endg ig = ig+1 g_to_ig(g) = ig + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) end do end if diff --git a/src/cpl/share_esmf/ndepStreamMod.F90 b/src/cpl/share_esmf/ndepStreamMod.F90 index f8edd96ffc..860dd35641 100644 --- a/src/cpl/share_esmf/ndepStreamMod.F90 +++ b/src/cpl/share_esmf/ndepStreamMod.F90 @@ -1,5 +1,7 @@ module ndepStreamMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! !DESCRIPTION: ! Contains methods for reading in nitrogen deposition data file @@ -256,12 +258,14 @@ subroutine ndep_interp(bounds, atm2lnd_inst) dayspyr = get_curr_days_per_year( ) do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) atm2lnd_inst%forc_ndep_grc(g) = dataptr1d(ig) / (secspday * dayspyr) end do else ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) atm2lnd_inst%forc_ndep_grc(g) = dataptr1d(ig) end do end if From cbfe3032f0626f84abe810417a89f7cd9f9fa962 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 3 Nov 2025 11:36:05 -0700 Subject: [PATCH 052/112] Make isotope data protected so can view it, but not change it --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index 34e5916ea0..8b3b1bc59f 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -13,7 +13,7 @@ module AtmCarbonIsotopeStreamType character(len=*), parameter :: varname_c13 = 'delta13co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type private - real(r8), allocatable :: atm_delta_c13(:) ! delta C13 data array + real(r8), public, allocatable :: atm_delta_c13(:) ! delta C13 data array contains ! Public Methods @@ -32,7 +32,7 @@ module AtmCarbonIsotopeStreamType character(len=*), parameter :: varname_c14 = 'Delta14co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type private - real(r8), allocatable :: atm_delta_c14(:) ! delta c14 data array + real(r8), public, allocatable :: atm_delta_c14(:) ! delta c14 data array contains ! Public Methods From 03d0813c66fdea93efe42852bec11e0f84bedcda Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 3 Nov 2025 11:37:31 -0700 Subject: [PATCH 053/112] Add some shr_assert calls to ensure the difference between the new and old method (and CMIP6 and CMIP7 datasets) are reasonably small --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index e16991ce39..88e2f4da40 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,5 +1,7 @@ module CIsoAtmTimeseriesMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! @@ -155,6 +157,10 @@ subroutine C14BombSpike( bounds ) call atm_c14_stream%Interp( bounds) + ! Make sure the difference between the streams and the old method is within reasonable bounds + call shr_assert_all( abs( atm_delta_c14_grc - atm_c14_stream%atm_delta_c14 ) < 1.0e-2_r8, & + 'C14BombSpike: difference between streams and old method too large', file=sourcefile, line=__LINE__) + end subroutine C14BombSpike !----------------------------------------------------------------------- @@ -344,6 +350,10 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) call atm_c13_stream%Interp( bounds) + ! Make sure the difference between the streams and the old method is within reasonable bounds + call shr_assert_all( abs( atm_delta_c13_grc - atm_c13_stream%atm_delta_c13 ) < 1.0e-2_r8, & + 'C13TimeSeries: difference between streams and old method too large', file=sourcefile, line=__LINE__) + end subroutine C13TimeSeries !----------------------------------------------------------------------- From 6ef8762b98fa4b6af49a48fe5f4cad789c1d591b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 3 Nov 2025 12:57:58 -0700 Subject: [PATCH 054/112] Differences between new CMIP7 streams and CMIP6 old method is too large to do a meaningful assert --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 88e2f4da40..e16991ce39 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,7 +1,5 @@ module CIsoAtmTimeseriesMod -#include "shr_assert.h" - !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! @@ -157,10 +155,6 @@ subroutine C14BombSpike( bounds ) call atm_c14_stream%Interp( bounds) - ! Make sure the difference between the streams and the old method is within reasonable bounds - call shr_assert_all( abs( atm_delta_c14_grc - atm_c14_stream%atm_delta_c14 ) < 1.0e-2_r8, & - 'C14BombSpike: difference between streams and old method too large', file=sourcefile, line=__LINE__) - end subroutine C14BombSpike !----------------------------------------------------------------------- @@ -350,10 +344,6 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) call atm_c13_stream%Interp( bounds) - ! Make sure the difference between the streams and the old method is within reasonable bounds - call shr_assert_all( abs( atm_delta_c13_grc - atm_c13_stream%atm_delta_c13 ) < 1.0e-2_r8, & - 'C13TimeSeries: difference between streams and old method too large', file=sourcefile, line=__LINE__) - end subroutine C13TimeSeries !----------------------------------------------------------------------- From 2cfa8fbb11eaa605a9b90de0ab065609d5840cc2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 4 Nov 2025 09:46:28 -0700 Subject: [PATCH 055/112] Add protected to the streams datatype so can't be changed outside of this module --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index e16991ce39..621bb242ae 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -30,8 +30,8 @@ module CIsoAtmTimeseriesMod logical , public :: use_c13_timeseries = .false. ! do we use time-varying atmospheric C13? character(len=256) , public :: atm_c13_filename = ' ' ! file name of C13 input data - real(r8), allocatable, public :: rc14_atm_grc(:) ! Ratio of C14 C12 data on gridcell - real(r8), allocatable, public :: rc13_atm_grc(:) ! Ratio of C13 C12 data on gridcell + real(r8), allocatable, public, protected :: rc14_atm_grc(:) ! Ratio of C14 C12 data on gridcell + real(r8), allocatable, public, protected :: rc13_atm_grc(:) ! Ratio of C13 C12 data on gridcell ! ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file From dd5176b5796437b7aebee7de457bdcd26b1126ff Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 4 Nov 2025 12:31:13 -0700 Subject: [PATCH 056/112] Generalize some paths so unit testing works in a CESM checkout Previously these paths assumed a standalone CTSM checkout --- src/CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c15727a859..ae5f4cc4ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ project(clm_tests Fortran C) include(CIME_utils) +set(SRCROOT "${CIMEROOT}/..") set(CLM_ROOT "..") # find needed external packages @@ -38,15 +39,15 @@ link_libraries(${ESMF_INTERFACE_LINK_LIBRARIES}) # done first, so that in case of name collisions, the CLM versions take # precedence (when there are two files with the same name, the one added later # wins). -add_subdirectory(${CLM_ROOT}/share/src csm_share) -add_subdirectory(${CLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) +add_subdirectory(${SRCROOT}/share/src csm_share) +add_subdirectory(${SRCROOT}/share/unit_test_stubs/util csm_share_stubs) # Add files needed from CMEPS list ( APPEND drv_sources_needed - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_expr_parser_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_fire_emis_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_expr_parser_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_fire_emis_mod.F90 ) # Add CLM source directories @@ -96,7 +97,7 @@ declare_generated_dependencies(clm "${clm_genf90_sources}") add_dependencies(clm csm_share ESMF) # We need to look for header files here, in order to pick up shr_assert.h -include_directories(${CLM_ROOT}/share/include) +include_directories(${SRCROOT}/share/include) # Tell cmake to look for libraries & mod files here, because this is where we built libraries include_directories(${CMAKE_CURRENT_BINARY_DIR}) From c39b56e9c9d206c2f56b05124de7d710f1042d09 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 15:09:05 -0700 Subject: [PATCH 057/112] Add new namelist items for carbon isotope streams and six failure tests for them that do not pass --- bld/CLMBuildNamelist.pm | 68 +++++++++++++++---- .../namelist_definition_ctsm.xml | 45 ++++++++++++ bld/unit_testers/build-namelist_test.pl | 26 ++++++- 3 files changed, 126 insertions(+), 13 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 8cc5b25187..f7bfa7c6f2 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3757,32 +3757,56 @@ sub setup_logic_c_isotope { $log->warning("use_c14 is ONLY scientifically validated with the bgc=BGC configuration" ); } } + my $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); + my $stream_fldfilename_atm_c14 = $nl->get_value('stream_fldfilename_atm_c14'); + my $atm_c14_filename = $nl->get_value('atm_c14_filename'); if ( &value_is_true($use_c14) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c14_bombspike', 'use_c14'=>$use_c14 ); - my $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); if ( &value_is_true($use_c14_bombspike) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c14_filename', - 'use_c14'=>$use_c14, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c14_bombspike'=>$nl->get_value('use_c14_bombspike'), - 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + if ( defined($stream_fldfilename_atm_c14) ) { + setup_logic_c14_streams($opts, $nl_flags, $definition, $defaults, $nl); + } else { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c14_filename', + 'use_c14'=>$use_c14, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c14_bombspike'=>$nl->get_value('use_c14_bombspike'), + 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + } + $stream_fldfilename_atm_c14 = $nl->get_value('stream_fldfilename_atm_c14'); + $atm_c14_filename = $nl->get_value('atm_c14_filename'); + #if ( defined($stream_fldfilename_atm_c14) && defined($atm_c14_filename) ) { + #$log->fatal_error("Both stream_fldfilename_atm_c14 and atm_c14_filename set, only one should be set"); + #} } } else { - if ( defined($nl->get_value('use_c14_bombspike')) || - defined($nl->get_value('atm_c14_filename')) ) { - $log->fatal_error("use_c14 is FALSE and use_c14_bombspike or atm_c14_filename set"); + if ( defined($use_c14_bombspike) || + # defined($stream_fldfilename_atm_c14) || + defined($atm_c14_filename) ) { + $log->fatal_error("use_c14 is FALSE and use_c14_bombspike, stream_fldfilename_atm_c14 or atm_c14_filename set"); } } + my $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); + my $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); + my $atm_c13_filename = $nl->get_value('atm_c13_filename'); if ( &value_is_true($use_c13) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c13_timeseries', 'use_c13'=>$use_c13 ); - my $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); if ( &value_is_true($use_c13_timeseries) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c13_filename', - 'use_c13'=>$use_c13, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c13_timeseries'=>$nl->get_value('use_c13_timeseries'), - 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + if ( defined($stream_fldfilename_atm_c13) ) { + setup_logic_c13_streams($opts, $nl_flags, $definition, $defaults, $nl); + } else { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c13_filename', + 'use_c13'=>$use_c13, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c13_timeseries'=>$nl->get_value('use_c13_timeseries'), + 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + } + # $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); + $atm_c13_filename = $nl->get_value('atm_c13_filename'); + #if ( defined($stream_fldfilename_atm_c13) && defined($atm_c13_filename) ) { + #$log->fatal_error("Both stream_fldfilename_atm_c13 and atm_c13_filename set, only one should be set"); + #} } } else { if ( defined($nl->get_value('use_c13_timeseries')) || + # defined($nl->get_value('stream_fldfilename_atm_c13')) || defined($nl->get_value('atm_c13_filename')) ) { - $log->fatal_error("use_c13 is FALSE and use_c13_timeseries or atm_c13_filename set"); + $log->fatal_error("use_c13 is FALSE and use_c13_timeseries, stream_fldfilename_atm_c13 or atm_c13_filename set"); } } } else { @@ -3790,8 +3814,10 @@ sub setup_logic_c_isotope { &value_is_true($use_c14) || &value_is_true($nl->get_value('use_c14_bombspike')) || defined($nl->get_value('atm_c14_filename')) || + # defined($nl->get_value('stream_fldfilename_atm_c14')) || &value_is_true($nl->get_value('use_c13_timeseries')) || defined($nl->get_value('atm_c13_filename')) ) { + # defined($nl->get_value('stream_fldfilename_atm_c13')) || $log->fatal_error("bgc=sp and C isotope namelist variables were set, both can't be used at the same time"); } } @@ -3799,6 +3825,24 @@ sub setup_logic_c_isotope { #------------------------------------------------------------------------------- +sub setup_logic_c13_streams { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + # + # C13 stream file settings + # +} + +#------------------------------------------------------------------------------- + +sub setup_logic_c14_streams { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + # + # C14 stream file settings + # +} + +#------------------------------------------------------------------------------- + sub setup_logic_nitrogen_deposition { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 69a243bd27..f88ab82625 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1806,6 +1806,51 @@ Mapping method from Nitrogen deposition input file to the model resolution none = no interpolation + + + +First year to loop over for atmospheric C14 isotope delta data + + + +Last year to loop over for data atmospheric C14 isotope delta data + + + +Simulation year that aligns with stream_year_first_atm_c14 value + + + +Filename of input stream data for atmospheric C14 isotope delta data + + + +First year to loop over for atmospheric C13 isotope delta data + + + +Last year to loop over for data atmospheric C13 isotope delta data + + + +Simulation year that aligns with stream_year_first_atm_c13 value + + + +Filename of input stream data for atmospheric C13 isotope delta data + + diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index ac5956506f..cc747cdc5f 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3394; +my $ntests = 3400; if ( defined($opts{'compare'}) ) { $ntests += 2061; @@ -795,6 +795,30 @@ sub cat_and_create_namelistinfile { namelst=>"use_c14_bombspike=.true.", phys=>"clm5_0", }, + "bombspike file and stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c14=TRUE use_c14_bombspike=.true. stream_fldfilename_atm_c14='/dev/null', atm_c14_filename='/dev/null'", + phys=>"clm6_0", + }, + "c13 file and stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c13_timeseries=.true. stream_fldfilename_atm_c13='/dev/null', atm_c13_filename='/dev/null'", + phys=>"clm6_0", + }, + "c13 off, but stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c13=.false. stream_fldfilename_atm_c13='/dev/null'", + phys=>"clm6_0", + }, + "c14 off, but stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c14=.false. stream_fldfilename_atm_c14='/dev/null'", + phys=>"clm6_0", + }, + "sp, but c13 stream" =>{ options=>"-bgc sp -envxml_dir .", + namelst=>"stream_fldfilename_atm_c13='/dev/null'", + phys=>"clm6_0", + }, + "sp, but c14 stream" =>{ options=>"-bgc sp -envxml_dir .", + namelst=>"stream_fldfilename_atm_c14='/dev/null'", + phys=>"clm6_0", + }, "lightres no cn" =>{ options=>"-bgc sp -envxml_dir . -light_res 360x720", namelst=>"", phys=>"clm5_0", From c94e68d6710f6a087c7b2b13e504a04fc9dbae0a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 15:34:32 -0700 Subject: [PATCH 058/112] Uncomment the error checks and fix a bug so that the new tests all work now --- bld/CLMBuildNamelist.pm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index f7bfa7c6f2..81cdeb0814 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3772,13 +3772,13 @@ sub setup_logic_c_isotope { } $stream_fldfilename_atm_c14 = $nl->get_value('stream_fldfilename_atm_c14'); $atm_c14_filename = $nl->get_value('atm_c14_filename'); - #if ( defined($stream_fldfilename_atm_c14) && defined($atm_c14_filename) ) { - #$log->fatal_error("Both stream_fldfilename_atm_c14 and atm_c14_filename set, only one should be set"); - #} + if ( defined($stream_fldfilename_atm_c14) && defined($atm_c14_filename) ) { + $log->fatal_error("Both stream_fldfilename_atm_c14 and atm_c14_filename set, only one should be set"); + } } } else { if ( defined($use_c14_bombspike) || - # defined($stream_fldfilename_atm_c14) || + defined($stream_fldfilename_atm_c14) || defined($atm_c14_filename) ) { $log->fatal_error("use_c14 is FALSE and use_c14_bombspike, stream_fldfilename_atm_c14 or atm_c14_filename set"); } @@ -3796,15 +3796,15 @@ sub setup_logic_c_isotope { 'use_c13'=>$use_c13, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c13_timeseries'=>$nl->get_value('use_c13_timeseries'), 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); } - # $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); + $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); $atm_c13_filename = $nl->get_value('atm_c13_filename'); - #if ( defined($stream_fldfilename_atm_c13) && defined($atm_c13_filename) ) { - #$log->fatal_error("Both stream_fldfilename_atm_c13 and atm_c13_filename set, only one should be set"); - #} + if ( defined($stream_fldfilename_atm_c13) && defined($atm_c13_filename) ) { + $log->fatal_error("Both stream_fldfilename_atm_c13 and atm_c13_filename set, only one should be set"); + } } } else { if ( defined($nl->get_value('use_c13_timeseries')) || - # defined($nl->get_value('stream_fldfilename_atm_c13')) || + defined($nl->get_value('stream_fldfilename_atm_c13')) || defined($nl->get_value('atm_c13_filename')) ) { $log->fatal_error("use_c13 is FALSE and use_c13_timeseries, stream_fldfilename_atm_c13 or atm_c13_filename set"); } @@ -3814,10 +3814,10 @@ sub setup_logic_c_isotope { &value_is_true($use_c14) || &value_is_true($nl->get_value('use_c14_bombspike')) || defined($nl->get_value('atm_c14_filename')) || - # defined($nl->get_value('stream_fldfilename_atm_c14')) || + defined($nl->get_value('stream_fldfilename_atm_c14')) || &value_is_true($nl->get_value('use_c13_timeseries')) || - defined($nl->get_value('atm_c13_filename')) ) { - # defined($nl->get_value('stream_fldfilename_atm_c13')) || + defined($nl->get_value('atm_c13_filename')) || + defined($nl->get_value('stream_fldfilename_atm_c13')) ) { $log->fatal_error("bgc=sp and C isotope namelist variables were set, both can't be used at the same time"); } } From 3697d1e0063e286739a9d7fcd477ecbf52b2fcc8 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 17:36:38 -0700 Subject: [PATCH 059/112] Output the carbon_isotope_streams namelist when CN is on --- bld/CLMBuildNamelist.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 81cdeb0814..a4ac04b94f 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -5363,6 +5363,9 @@ sub write_output_files { push @groups, "clm_canopy_inparm"; push @groups, "prigentroughness"; push @groups, "zendersoilerod"; + if ( &value_is_true($nl_flags->{'use_cn'}) ) { + push @groups, "carbon_isotope_streams"; + } if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } From 16698108cf3fb72fedfb9919621beb28a9c0b26e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 23:48:45 -0700 Subject: [PATCH 060/112] Add a CIsoReadNML subroutine to set carbon isotope streams settings --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 621bb242ae..3d12425f86 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -19,6 +19,7 @@ module CIsoAtmTimeseriesMod private ! ! !PUBLIC MEMBER FUNCTIONS: + public:: CIsoAtmReadNML ! Read namelist for atmospheric C14/C13 isotope time series public:: C14BombSpike ! Time series for C14 data public:: C14_init_BombSpike ! Initialize C14 data series and read data in public:: C13Timeseries ! Time series for C13 data @@ -50,8 +51,8 @@ module CIsoAtmTimeseriesMod real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file ! Private data for the control namelist: - character(len=CL), private :: stream_fldfilename_atm_c14 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' - character(len=CL), private :: stream_fldfilename_atm_c13 = '/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' + character(len=CL), private :: stream_fldfilename_atm_c14 + character(len=CL), private :: stream_fldfilename_atm_c13 integer, private :: stream_year_first_atm_c14 = 1850 integer, private :: stream_year_last_atm_c14 = 2023 integer, private :: stream_model_year_align_atm_c14 = 1850 @@ -71,6 +72,60 @@ module CIsoAtmTimeseriesMod contains + !----------------------------------------------------------------------- + subroutine CIsoAtmReadNML( NLFilename ) + ! + ! !DESCRIPTION: + ! Read in the namelist for atmospheric C14/C13 isotope time series + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use spmdMod , only : masterproc, mpicom + use shr_mpi_mod, only : shr_mpi_bcast + + ! Arguments: + character(len=*), intent(in) :: NLFilename ! Namelist filename to read + + ! !LOCAL VARIABLES: + integer :: ierr ! error code + integer :: unitn ! unit for namelist file + character(len=*), parameter :: nml_name = 'carbon_isotope_streams' ! MUST agree with name in namelist and read + + namelist /carbon_isotope_streams/ stream_fldfilename_atm_c14, & + stream_fldfilename_atm_c13, stream_year_first_atm_c14, & + stream_year_last_atm_c14, stream_model_year_align_atm_c14, & + stream_year_first_atm_c13, stream_year_last_atm_c13, & + stream_model_year_align_atm_c13 + + ! Read in the namelist on the main task + if (masterproc) then + open( newunit=unitn, file=trim(NLFilename), status='old', iostat=ierr ) + write(iulog,*) 'Read in '//nml_name//' namelist' + call shr_nl_find_group_name(unitn, nml_name, status=ierr) + if (ierr == 0) then + read(unitn, nml=carbon_isotope_streams, iostat=ierr) + if (ierr /= 0) then + call endrun(msg="ERROR reading "//nml_name//"namelist", file=sourcefile, line=__LINE__) + return + end if + else + call endrun(msg="ERROR could NOT find "//nml_name//"namelist", file=sourcefile, line=__LINE__) + return + end if + close( unitn ) + end if + ! Broadcast namelist values to all tasks + call shr_mpi_bcast( stream_fldfilename_atm_c14, mpicom ) + call shr_mpi_bcast( stream_year_first_atm_c14, mpicom ) + call shr_mpi_bcast( stream_year_last_atm_c14, mpicom ) + call shr_mpi_bcast( stream_model_year_align_atm_c14, mpicom ) + call shr_mpi_bcast( stream_fldfilename_atm_c13, mpicom ) + call shr_mpi_bcast( stream_year_first_atm_c13, mpicom ) + call shr_mpi_bcast( stream_year_last_atm_c13, mpicom ) + call shr_mpi_bcast( stream_model_year_align_atm_c13, mpicom ) + + end subroutine + !----------------------------------------------------------------------- subroutine C14BombSpike( bounds ) ! From dede57102cae81c5f35b62f7a93d6021b6e18178 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 23:49:27 -0700 Subject: [PATCH 061/112] Call the CIsoAtmReadNML here --- src/main/clm_initializeMod.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 387f4cd4ca..4530fda860 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -157,6 +157,7 @@ subroutine initialize2(ni,nj, currtime) use clm_time_manager , only : get_curr_date, get_nstep, advance_timestep use clm_time_manager , only : timemgr_init, timemgr_restart_io, timemgr_restart, is_restart use CIsoAtmTimeseriesMod , only : C14_init_BombSpike, use_c14_bombspike, C13_init_TimeSeries, use_c13_timeseries + use CIsoAtmTimeseriesMod , only : CIsoAtmReadNML use DaylengthMod , only : InitDaylength use dynSubgridDriverMod , only : dynSubgrid_init use dynConsBiogeophysMod , only : dyn_hwcontent_set_baselines @@ -475,6 +476,7 @@ subroutine initialize2(ni,nj, currtime) ! Also do this for FATES see below call SatellitePhenologyInit(bounds_proc) end if + if ( use_c13 .or. use_c14 ) call CIsoAtmReadNML( NLFilename ) if ( use_c14 ) then call C14_init_BombSpike( bounds_proc ) end if From 42fca56c47d261891eac21f1dfc214463e1e7d9c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 5 Nov 2025 23:50:36 -0700 Subject: [PATCH 062/112] Fix some problems that the NAG compiler on Izumi found --- src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index 5a928091f8..4c87b2d205 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -153,7 +153,6 @@ subroutine Advance(this) ! Uses: use clm_time_manager , only : get_curr_date use dshr_strdata_mod , only : shr_strdata_advance - import :: ctsm_force_2DStream_base_type ! ! Arguments: class(ctsm_force_2DStream_base_type), intent(inout) :: this @@ -170,7 +169,7 @@ subroutine Advance(this) mcdate = year*10000 + mon*100 + day call shr_strdata_advance(this%sdat, ymd=mcdate, tod=sec, logunit=iulog, istr='CTSMForce2DStreamBase', rc=rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then - write(iulog,*) ' Streams advance failing for ', trim(this.stream_name), ' stream file = ', trim(this.stream_filename) + write(iulog,*) ' Streams advance failing for ', trim(this%stream_name), ' stream file = ', trim(this%stream_filename) call endrun( 'CTSM forcing Streams advance failing', file=sourcefile, line=__LINE__ ) end if end subroutine Advance From 96f8f4ca592666e0b6b7cf55dec087e7e085f6f2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 6 Nov 2025 11:20:27 -0700 Subject: [PATCH 063/112] Add a private subroutine to do checking of the Ciso namelist inputs --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 3d12425f86..4494cb262f 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,5 +1,7 @@ module CIsoAtmTimeseriesMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! @@ -8,7 +10,7 @@ module CIsoAtmTimeseriesMod use clm_time_manager , only : get_curr_date, get_curr_yearfrac use clm_varcon , only : c13ratio, c14ratio, secspday use shr_const_mod , only : SHR_CONST_PDB ! Ratio of C13/C12 - use clm_varctl , only : iulog + use clm_varctl , only : iulog, use_c13, use_c14 use abortutils , only : endrun use spmdMod , only : masterproc use shr_log_mod , only : errMsg => shr_log_errMsg @@ -36,6 +38,7 @@ module CIsoAtmTimeseriesMod ! ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file + private:: CIsoCheckNMLInputs ! Check that the namelist inputs are valid type(atm_delta_c13_stream_type), private :: atm_c13_stream ! Atmospheric C13 stream object type(atm_delta_c14_stream_type), private :: atm_c14_stream ! Atmospheric C14 stream object @@ -51,8 +54,8 @@ module CIsoAtmTimeseriesMod real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file ! Private data for the control namelist: - character(len=CL), private :: stream_fldfilename_atm_c14 - character(len=CL), private :: stream_fldfilename_atm_c13 + character(len=CL), private :: stream_fldfilename_atm_c14 = ' ' + character(len=CL), private :: stream_fldfilename_atm_c13 = ' ' integer, private :: stream_year_first_atm_c14 = 1850 integer, private :: stream_year_last_atm_c14 = 2023 integer, private :: stream_model_year_align_atm_c14 = 1850 @@ -124,7 +127,62 @@ subroutine CIsoAtmReadNML( NLFilename ) call shr_mpi_bcast( stream_year_last_atm_c13, mpicom ) call shr_mpi_bcast( stream_model_year_align_atm_c13, mpicom ) - end subroutine + ! Do some error checking of input namelist items + call CIsoCheckNMLInputs() + + end subroutine CIsoAtmReadNML + + !----------------------------------------------------------------------- + subroutine CIsoCheckNMLInputs() + ! + ! !DESCRIPTION: + ! Check that the namelist inputs are valid + ! + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + ! When carbon isotopes are off nothing should be set + if ( .not. use_c13 )then + call shr_assert( .not. use_c13_timeseries, & + msg="ERROR: use_c13 is false but use_c13_timeseries is true", file=sourcefile, line=__LINE__) + call shr_assert( trim(atm_c13_filename) == '', & + msg="ERROR: use_c13 is false but atm_c13_filename is set", file=sourcefile, line=__LINE__) + call shr_assert( trim(stream_fldfilename_atm_c13) == '', & + msg="ERROR: use_c13 is false but stream_fldfilename_atm_c13 is set", file=sourcefile, line=__LINE__) + end if + if ( .not. use_c14 )then + call shr_assert( .not. use_c14_bombspike, & + msg="ERROR: use_c14 is false but use_c14_bombspike is true", file=sourcefile, line=__LINE__) + call shr_assert( trim(atm_c14_filename) == '', & + msg="ERROR: use_c14 is false but atm_c14_filename is set", file=sourcefile, line=__LINE__) + call shr_assert( trim(stream_fldfilename_atm_c14) == '', & + msg="ERROR: use_c14 is false but stream_fldfilename_atm_c14 is set", file=sourcefile, line=__LINE__) + end if + + ! + ! Check C14 stream namelist inputs + ! + if ( use_c14_bombspike ) then + if ( trim(atm_c14_filename) == '' .and. trim(stream_fldfilename_atm_c14) == '' ) then + call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are blank", file=sourcefile, line=__LINE__) + end if + if ( trim(atm_c14_filename) /= '' .and. trim(stream_fldfilename_atm_c14) /= '' ) then + call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are set", file=sourcefile, line=__LINE__) + end if + end if + ! + ! Check C13 stream namelist inputs + ! + if ( use_c13_timeseries ) then + if ( trim(atm_c13_filename) == '' .and. trim(stream_fldfilename_atm_c13) == '' ) then + call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are blank", file=sourcefile, line=__LINE__) + end if + if ( trim(atm_c13_filename) /= '' .and. trim(stream_fldfilename_atm_c13) /= '' ) then + call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are set", file=sourcefile, line=__LINE__) + end if + end if + + end subroutine CIsoCheckNMLInputs !----------------------------------------------------------------------- subroutine C14BombSpike( bounds ) From 246826aadff495cbbf2bdbab4a9e52c56a655cdb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 6 Nov 2025 15:10:26 -0700 Subject: [PATCH 064/112] Have either old format C14/C13 CMIP6 datafile format read in or new streams format, not both make a few new methods for streams handling, and for only one to be active at a time --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 126 +++++++++++++++--- 1 file changed, 110 insertions(+), 16 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 4494cb262f..2785a93a6a 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -53,6 +53,9 @@ module CIsoAtmTimeseriesMod real(r8), allocatable, private :: atm_delta_c13_grc(:) ! Delta C13 data on gridcell real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file + logical, private :: use_c13_streams = .false. ! By default read in the CMIP6 file format for C13 + logical, private :: use_c14_streams = .false. ! By default read in the CMIP6 file format for C14 + ! Private data for the control namelist: character(len=CL), private :: stream_fldfilename_atm_c14 = ' ' character(len=CL), private :: stream_fldfilename_atm_c13 = ' ' @@ -130,6 +133,14 @@ subroutine CIsoAtmReadNML( NLFilename ) ! Do some error checking of input namelist items call CIsoCheckNMLInputs() + ! Decide if C14/C13 streams are going to be used or the old method + if ( trim(stream_fldfilename_atm_c13) /= '' ) then + use_c13_streams = .true. + end if + if ( trim(stream_fldfilename_atm_c14) /= '' ) then + use_c14_streams = .true. + end if + end subroutine CIsoAtmReadNML !----------------------------------------------------------------------- @@ -212,6 +223,10 @@ subroutine C14BombSpike( bounds ) ! if ( use_c14_bombspike )then + if ( use_c14_streams )then + call C14Streams( bounds ) + RETURN + end if ! get current date call get_curr_date(yr, mon, day, tod) dateyear = real(yr) + get_curr_yearfrac() @@ -266,9 +281,29 @@ subroutine C14BombSpike( bounds ) rc14_atm_grc(g) = rc14_atm(l) end do + end subroutine C14BombSpike + + !----------------------------------------------------------------------- + subroutine C14Streams( bounds ) + ! Description: + ! + ! Use the streams method to read in atmospheric C14 bomb spike data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + integer :: g ! Indices + call atm_c14_stream%Interp( bounds) - end subroutine C14BombSpike + do g = bounds%begg, bounds%endg + atm_delta_c14_grc(g) = atm_c14_stream%atm_delta_c14(g) + rc14_atm_grc(g) = (atm_delta_c14_grc(g) * 1.e-3_r8 + 1._r8) * c14ratio + end do + + end subroutine C14Streams !----------------------------------------------------------------------- subroutine C14_init_BombSpike( bounds ) @@ -295,11 +330,21 @@ subroutine C14_init_BombSpike( bounds ) character(len=*), parameter :: vname = 'Delta14co2_in_air' ! Variable name on file !----------------------------------------------------------------------- + ! Allocate the gridcell arrays + allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) + allocate(rc14_atm_grc(bounds%begg:bounds%endg)) + atm_delta_c14_grc(:) = nan + rc14_atm_grc(:) = nan ! ! If the bombspike timeseries file is being used, read the file in ! if ( use_c14_bombspike )then + if ( use_c14_streams )then + call C14StreamsInit( bounds ) + RETURN + end if + call getfil(atm_c14_filename, locfn, 0) if ( masterproc ) then @@ -343,12 +388,20 @@ subroutine C14_init_BombSpike( bounds ) end do end if - ! Allocate the gridcell arrays - allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) - allocate(rc14_atm_grc(bounds%begg:bounds%endg)) - atm_delta_c14_grc(:) = nan - rc14_atm_grc(:) = nan + end subroutine C14_init_BombSpike + + !----------------------------------------------------------------------- + subroutine C14StreamsInit( bounds ) + ! Description: + ! + ! Initialize the streams method to read in atmospheric C14 bomb spike data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- ! Streams method call atm_c14_stream%Init( bounds, & @@ -363,8 +416,7 @@ subroutine C14_init_BombSpike( bounds ) call atm_c14_stream%Advance( ) call atm_c14_stream%Interp( bounds ) - end subroutine C14_init_BombSpike - + end subroutine C14StreamsInit !----------------------------------------------------------------------- subroutine C13TimeSeries( bounds, atm2lnd_inst ) @@ -396,6 +448,10 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) ! if ( use_c13_timeseries )then + if ( use_c13_streams )then + call C13Streams( bounds ) + RETURN + end if ! get current date call get_curr_date(yr, mon, day, tod) dateyear = real(yr) + get_curr_yearfrac() @@ -455,9 +511,28 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) end associate end do + end subroutine C13TimeSeries + + !----------------------------------------------------------------------- + subroutine C13Streams( bounds ) + ! Description: + ! + ! Use the streams method to read in atmospheric C13 data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: g ! Indices + call atm_c13_stream%Interp( bounds) - end subroutine C13TimeSeries + do g = bounds%begg, bounds%endg + atm_delta_c13_grc(g) = atm_c13_stream%atm_delta_c13(g) + rc13_atm_grc(g) = (atm_delta_c13_grc(g) * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB + end do + + end subroutine C13Streams !----------------------------------------------------------------------- subroutine C13_init_TimeSeries( bounds ) @@ -482,11 +557,22 @@ subroutine C13_init_TimeSeries( bounds ) logical :: readvar ! if variable read or not character(len=*), parameter :: vname = 'delta13co2_in_air' ! Variable name on file !----------------------------------------------------------------------- + + ! Allocate the gridcell arrays + allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) + allocate(rc13_atm_grc(bounds%begg:bounds%endg) ) + atm_delta_c13_grc(:) = nan + rc13_atm_grc(:) = nan ! ! If the timeseries file is being used, read the file in ! if ( use_c13_timeseries )then + if ( use_c13_streams )then + call C13StreamsInit( bounds ) + RETURN + end if + call getfil(atm_c13_filename, locfn, 0) if ( masterproc ) then @@ -528,11 +614,19 @@ subroutine C13_init_TimeSeries( bounds ) end do end if - ! Allocate the gridcell arrays - allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) - allocate(rc13_atm_grc(bounds%begg:bounds%endg) ) - atm_delta_c13_grc(:) = nan - rc13_atm_grc(:) = nan + end subroutine C13_init_TimeSeries + + !----------------------------------------------------------------------- + subroutine C13StreamsInit( bounds ) + ! Description: + ! + ! Initialize the streams method to read in atmospheric C13 data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- ! Streams method call atm_c13_stream%Init( bounds, & @@ -545,9 +639,9 @@ subroutine C13_init_TimeSeries( bounds ) year_last=stream_year_last_atm_c13, & model_year_align=stream_model_year_align_atm_c13 ) call atm_c13_stream%Advance( ) - call atm_c13_stream%Interp( bounds) + call atm_c13_stream%Interp( bounds ) - end subroutine C13_init_TimeSeries + end subroutine C13StreamsInit !----------------------------------------------------------------------- subroutine check_units( ncid, vname, relativeto ) From 5f3a896389c273710c82d5a1d45c248690f8ed72 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 6 Nov 2025 15:30:01 -0700 Subject: [PATCH 065/112] All both stream/cmip6 options for C13/C14 timeseries to be off, so that constant version is used, add a missing Advance at timestep update, add a TODO note to move where the allocation happens. --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 2785a93a6a..bc1d069176 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -174,9 +174,11 @@ subroutine CIsoCheckNMLInputs() ! Check C14 stream namelist inputs ! if ( use_c14_bombspike ) then - if ( trim(atm_c14_filename) == '' .and. trim(stream_fldfilename_atm_c14) == '' ) then - call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are blank", file=sourcefile, line=__LINE__) - end if + ! This is actually allowed, if you want to use the constant method + ! TODO: Remove this commented code + !if ( trim(atm_c14_filename) == '' .and. trim(stream_fldfilename_atm_c14) == '' ) then + ! call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are blank", file=sourcefile, line=__LINE__) + !end if if ( trim(atm_c14_filename) /= '' .and. trim(stream_fldfilename_atm_c14) /= '' ) then call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are set", file=sourcefile, line=__LINE__) end if @@ -185,9 +187,11 @@ subroutine CIsoCheckNMLInputs() ! Check C13 stream namelist inputs ! if ( use_c13_timeseries ) then - if ( trim(atm_c13_filename) == '' .and. trim(stream_fldfilename_atm_c13) == '' ) then - call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are blank", file=sourcefile, line=__LINE__) - end if + ! This is actually allowed, if you want to use the constant method + ! TODO: Remove this commented code + !if ( trim(atm_c13_filename) == '' .and. trim(stream_fldfilename_atm_c13) == '' ) then + !call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are blank", file=sourcefile, line=__LINE__) + !end if if ( trim(atm_c13_filename) /= '' .and. trim(stream_fldfilename_atm_c13) /= '' ) then call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are set", file=sourcefile, line=__LINE__) end if @@ -296,6 +300,7 @@ subroutine C14Streams( bounds ) !----------------------------------------------------------------------- integer :: g ! Indices + call atm_c14_stream%Advance( ) call atm_c14_stream%Interp( bounds) do g = bounds%begg, bounds%endg @@ -331,6 +336,7 @@ subroutine C14_init_BombSpike( bounds ) !----------------------------------------------------------------------- ! Allocate the gridcell arrays + ! TODO: This should be below within the use_c14_bombspike if block allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) allocate(rc14_atm_grc(bounds%begg:bounds%endg)) atm_delta_c14_grc(:) = nan @@ -558,6 +564,7 @@ subroutine C13_init_TimeSeries( bounds ) character(len=*), parameter :: vname = 'delta13co2_in_air' ! Variable name on file !----------------------------------------------------------------------- + ! TODO: This should be below within the use_c13_timeseries if block ! Allocate the gridcell arrays allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) allocate(rc13_atm_grc(bounds%begg:bounds%endg) ) From 94d1bd621fe08d528460b2bd5be9832514d70dd1 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 7 Nov 2025 11:07:21 -0700 Subject: [PATCH 066/112] Add file and line to shr_sys_abort calls as per #1452 --- src/utils/fileutils.F90 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils/fileutils.F90 b/src/utils/fileutils.F90 index e7146468e7..f67cf91af0 100644 --- a/src/utils/fileutils.F90 +++ b/src/utils/fileutils.F90 @@ -21,6 +21,9 @@ module fileutils public :: getavu !Get next available Fortran unit number !----------------------------------------------------------------------- + character(len=*), parameter, private :: sourcefile = & + __FILE__ + contains !----------------------------------------------------------------------- @@ -76,7 +79,7 @@ subroutine getfil (fulpath, locfn, iflag) write(iulog,'(a)')'(GETFIL): full pathname is '//trim(fulpath) write(iulog,'(a)')'(GETFIL): local filename has zero length' end if - call shr_sys_abort + call shr_sys_abort('GETFIL: local filename has zero length', file=sourcefile, line=__LINE__) else if (masterproc) then write(iulog,'(a)')'(GETFIL): attempting to find local file ',trim(locfn) @@ -105,7 +108,7 @@ subroutine getfil (fulpath, locfn, iflag) write(iulog,'(a)')'(GETFIL): failed getting file from full path: '//fulpath end if if (iflag==0) then - call shr_sys_abort ('GETFIL: FAILED to get '//trim(fulpath)) + call shr_sys_abort ('GETFIL: FAILED to get '//trim(fulpath), file=sourcefile, line=__LINE__) else RETURN endif @@ -131,7 +134,7 @@ subroutine opnfil (locfn, iun, form) if (len_trim(locfn) == 0) then write(iulog,*)'(OPNFIL): local filename has zero length' - call shr_sys_abort + call shr_sys_abort('OPNFIL: local filename has zero length', file=sourcefile, line=__LINE__) endif if (form=='u' .or. form=='U') then ft = 'unformatted' @@ -142,7 +145,7 @@ subroutine opnfil (locfn, iun, form) if (ioe /= 0) then write(iulog,*)'(OPNFIL): failed to open file ',trim(locfn), & & ' on unit ',iun,' ierr=',ioe - call shr_sys_abort + call shr_sys_abort('OPNFIL: failed to open '//trim(locfn), rc=ioe, file=sourcefile, line=__LINE__) else if ( masterproc )then write(iulog,*)'(OPNFIL): Successfully opened file ',trim(locfn), & & ' on unit= ',iun From a469536d3f7b289299f19c6d39ed9b02d042bb50 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 8 Nov 2025 17:31:37 -0700 Subject: [PATCH 067/112] Add private subroutines to set and log information on the control logic, as well as some asserts to ensure things are correct, the logic isn't right yet, but I think I need a unit tester to get it right more easily and faster --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 158 +++++++++++++++--- 1 file changed, 131 insertions(+), 27 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index bc1d069176..e28769e536 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -130,18 +130,103 @@ subroutine CIsoAtmReadNML( NLFilename ) call shr_mpi_bcast( stream_year_last_atm_c13, mpicom ) call shr_mpi_bcast( stream_model_year_align_atm_c13, mpicom ) - ! Do some error checking of input namelist items + ! Do some error checking of input namelist items, set control flags, and write to the log call CIsoCheckNMLInputs() - ! Decide if C14/C13 streams are going to be used or the old method - if ( trim(stream_fldfilename_atm_c13) /= '' ) then - use_c13_streams = .true. + call CIsoSetControl() + call CIsoLogControl() + + end subroutine CIsoAtmReadNML + + !----------------------------------------------------------------------- + subroutine CIsoSetControl() + ! Set control settings based on the namelist inputs + ! Also do some assert checks to make sure other settings are as expected + ! + if ( use_c13_timeseries )then + ! Decide if C14/C13 streams are going to be used or the old method + if ( len_trim(stream_fldfilename_atm_c13) /= 0 ) then + use_c13_streams = .true. + else + call shr_assert( len_trim(atm_c13_filename) /= 0 , & + msg="ERROR: use_c13_timeseries is true but atm_c13_filename is blank", file=sourcefile, line=__LINE__) + call shr_assert( .not. use_c13_streams , & + msg="ERROR: stream_fldfilename_atm_c13 is blank but use_c13_streams is not TRUE", file=sourcefile, line=__LINE__) + end if + else + call shr_assert( .not. use_c13_streams , & + msg="ERROR: use_c13_timeseries is false, but use_c13_streams is TRUE", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c13_filename) == 0 , & + msg="ERROR: use_c13_timeseries is false but atm_c13_filename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0 , & + msg="ERROR: use_c13_timeseries is false but stream_fldfilename_atm_c13 is NOT blank", file=sourcefile, line=__LINE__) end if - if ( trim(stream_fldfilename_atm_c14) /= '' ) then - use_c14_streams = .true. + if ( use_c14_bombspike )then + if ( len_trim(stream_fldfilename_atm_c14) /= 0 ) then + use_c14_streams = .true. + else + call shr_assert( len_trim(atm_c14_filename) /= 0 , & + msg="ERROR: use_c14_bombspike is true but atm_c14_filename is blank", file=sourcefile, line=__LINE__) + call shr_assert( .not. use_c14_streams , & + msg="ERROR: stream_fldfilename_atm_c14 is blank but use_c14_streams is not TRUE", & + file=sourcefile, line=__LINE__) + end if + else + call shr_assert( .not. use_c14_streams , & + msg="ERROR: use_c14_bombspike is false, but use_c14_streams is TRUE", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c14_filename) == 0, & + msg="ERROR: use_c14_bombspike is false but atm_c14_filename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0 , & + msg="ERROR: use_c14_bombspike is false but stream_fldfilename_atm_c14 is NOT blank", & + file=sourcefile, line=__LINE__) end if - end subroutine CIsoAtmReadNML + end subroutine CIsoSetControl + + !----------------------------------------------------------------------- + subroutine CIsoLogControl() + ! Log namelist and control settings to output to display what behavior will be + ! + if ( use_c13_timeseries )then + if ( use_c13_streams ) then + call shr_assert( len_trim(stream_fldfilename_atm_c13) /= 0 , & + msg="use_c13_streams is TRUE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C13 atmospheric data will be read in using the streams method from file: '// & + trim(stream_fldfilename_atm_c13) + else if ( len_trim(atm_c13_filename) /= 0 ) then + write(iulog,*) 'C13 atmospheric data will be read in using the CMIP6 time series method from file: '// & + trim(atm_c13_filename) + else + call endrun(msg="use_c13_timeseries is true but use_c13_streams=FALSE and atm_c13_filename is blank", & + file=sourcefile, line=__LINE__) + end if + else + call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0 , & + msg="use_c13_timeseries is FALSE but stream_fldfilename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c13_filename) == 0 , & + msg="use_c13_timeseries is FALSE but stream_fldfilename is NOT blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C13 atmospheric data will be the global constant pre-industrial level' + end if + if ( use_c14_bombspike )then + if ( use_c14_streams ) then + write(iulog,*) 'C14 atmospheric data will be read in using the streams method from file: '// & + trim(stream_fldfilename_atm_c14) + else if ( len_trim(atm_c14_filename) /= 0 ) then + write(iulog,*) 'C14 atmospheric data will be read in using the CMIP6 time series method from file: '// & + trim(atm_c14_filename) + else + call endrun(msg="use_c14_bombspike is true but use_c14_streams=FALSE and atm_c14_filename is blank", & + file=sourcefile, line=__LINE__) + end if + else + call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0 , & + msg="use_c14_timeseries is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c14_filename) == 0 , & + msg="use_c14_timeseries is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C14 atmospheric data will be global constant pre-industrial level' + end if + + end subroutine CIsoLogControl !----------------------------------------------------------------------- subroutine CIsoCheckNMLInputs() @@ -156,17 +241,17 @@ subroutine CIsoCheckNMLInputs() if ( .not. use_c13 )then call shr_assert( .not. use_c13_timeseries, & msg="ERROR: use_c13 is false but use_c13_timeseries is true", file=sourcefile, line=__LINE__) - call shr_assert( trim(atm_c13_filename) == '', & + call shr_assert( len_trim(atm_c13_filename) == 0, & msg="ERROR: use_c13 is false but atm_c13_filename is set", file=sourcefile, line=__LINE__) - call shr_assert( trim(stream_fldfilename_atm_c13) == '', & + call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0, & msg="ERROR: use_c13 is false but stream_fldfilename_atm_c13 is set", file=sourcefile, line=__LINE__) end if if ( .not. use_c14 )then call shr_assert( .not. use_c14_bombspike, & msg="ERROR: use_c14 is false but use_c14_bombspike is true", file=sourcefile, line=__LINE__) - call shr_assert( trim(atm_c14_filename) == '', & + call shr_assert( len_trim(atm_c14_filename) == 0, & msg="ERROR: use_c14 is false but atm_c14_filename is set", file=sourcefile, line=__LINE__) - call shr_assert( trim(stream_fldfilename_atm_c14) == '', & + call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0, & msg="ERROR: use_c14 is false but stream_fldfilename_atm_c14 is set", file=sourcefile, line=__LINE__) end if @@ -174,26 +259,28 @@ subroutine CIsoCheckNMLInputs() ! Check C14 stream namelist inputs ! if ( use_c14_bombspike ) then - ! This is actually allowed, if you want to use the constant method - ! TODO: Remove this commented code - !if ( trim(atm_c14_filename) == '' .and. trim(stream_fldfilename_atm_c14) == '' ) then - ! call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are blank", file=sourcefile, line=__LINE__) - !end if - if ( trim(atm_c14_filename) /= '' .and. trim(stream_fldfilename_atm_c14) /= '' ) then - call endrun(msg="use_c14_bombspike is true but both stream_fldfilename_atm_c14 and stream_fldfilename_atm_c14 are set", file=sourcefile, line=__LINE__) + if ( len_trim(atm_c14_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c14) /= 0 ) then + call endrun(msg="use_c14_bombspike TRUE but both stream_fldfilename_atm_c14/stream_fldfilename_atm_c14 are set", & + file=sourcefile, line=__LINE__) + end if + else + if ( len_trim(atm_c14_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c14) /= 0 ) then + call endrun(msg="use_c14_bombspike TRUE but stream_fldfilename_atm_c14/stream_fldfilename_atm_c14 are set", & + file=sourcefile, line=__LINE__) end if end if ! ! Check C13 stream namelist inputs ! if ( use_c13_timeseries ) then - ! This is actually allowed, if you want to use the constant method - ! TODO: Remove this commented code - !if ( trim(atm_c13_filename) == '' .and. trim(stream_fldfilename_atm_c13) == '' ) then - !call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are blank", file=sourcefile, line=__LINE__) - !end if - if ( trim(atm_c13_filename) /= '' .and. trim(stream_fldfilename_atm_c13) /= '' ) then - call endrun(msg="use_c13_bombspike is true but both stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 are set", file=sourcefile, line=__LINE__) + if ( len_trim(atm_c13_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c13) /= 0 ) then + call endrun(msg="use_c13_bombspike TRUE but stream_fldfilename_atm_c13/stream_fldfilename_atm_c13 aren't blank", & + file=sourcefile, line=__LINE__) + end if + else + if ( len_trim(atm_c13_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c13) /= 0 ) then + call endrun(msg="use_c13_bombspike is false but stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 aren't blank", & + file=sourcefile, line=__LINE__) end if end if @@ -347,17 +434,22 @@ subroutine C14_init_BombSpike( bounds ) if ( use_c14_bombspike )then if ( use_c14_streams )then + write(iulog,*) 'Read in atmospheric C14 data from streams' call C14StreamsInit( bounds ) RETURN end if - - call getfil(atm_c14_filename, locfn, 0) + if ( .not. use_c14_streams .and. len_trim(atm_c14_filename) == 0 )then + write(iulog,*) 'Use constant preindustrial atmospheric C14 data' + RETURN + end if if ( masterproc ) then write(iulog, *) 'C14_init_BombSpike: preparing to open file:' write(iulog, *) trim(locfn) endif + call getfil(atm_c14_filename, locfn, 0) + call ncd_pio_openfile (ncid, trim(locfn), 0) call ncd_inqdlen(ncid,dimid,ntim,'time') @@ -409,6 +501,10 @@ subroutine C14StreamsInit( bounds ) ! !LOCAL VARIABLES: !----------------------------------------------------------------------- + if ( masterproc ) then + write(iulog, *) 'C14StreamsInit: Initializing C14 streams with file:' + write(iulog, *) trim(stream_fldfilename_atm_c14) + end if ! Streams method call atm_c14_stream%Init( bounds, & fldfilename=stream_fldfilename_atm_c14, & @@ -458,6 +554,10 @@ subroutine C13TimeSeries( bounds, atm2lnd_inst ) call C13Streams( bounds ) RETURN end if + if ( .not. use_c13_streams .and. len_trim(atm_c13_filename) == 0 )then + write(iulog,*) 'Use constant preindustrial atmospheric C13 data' + RETURN + end if ! get current date call get_curr_date(yr, mon, day, tod) dateyear = real(yr) + get_curr_yearfrac() @@ -635,6 +735,10 @@ subroutine C13StreamsInit( bounds ) ! !LOCAL VARIABLES: !----------------------------------------------------------------------- + if ( masterproc ) then + write(iulog, *) 'C13StreamsInit: Initializing C13 streams with file:' + write(iulog, *) trim(stream_fldfilename_atm_c13) + end if ! Streams method call atm_c13_stream%Init( bounds, & fldfilename=stream_fldfilename_atm_c13, & From 82878c92b3eb390d93b73f900558634a599bcdb6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 8 Nov 2025 18:37:45 -0700 Subject: [PATCH 068/112] Make some subroutines public so they can be unit tested --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index e28769e536..28cc8d41d4 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -38,7 +38,11 @@ module CIsoAtmTimeseriesMod ! ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file - private:: CIsoCheckNMLInputs ! Check that the namelist inputs are valid + + ! Private subroutines only made public for unit testing + public:: CIsoCheckNMLInputs ! Check that the namelist inputs are valid + public:: CIsoSetControl ! Set the control variables for Carbon Isotopes + public:: CIsoLogControl ! Write out the control settings to the logfile type(atm_delta_c13_stream_type), private :: atm_c13_stream ! Atmospheric C13 stream object type(atm_delta_c14_stream_type), private :: atm_c14_stream ! Atmospheric C14 stream object From c55449eacb6a5db048cbe541888061a82ca1f78c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 8 Nov 2025 18:41:46 -0700 Subject: [PATCH 069/112] Start adding a unit tester for the CIsoAtmTimeSeries logic --- .../CIsoAtmTimeSeries_test/CMakeLists.txt | 10 ++++ .../test_CIsoAtmTimeSeries.pf | 49 +++++++++++++++++++ src/biogeochem/test/CMakeLists.txt | 1 + 3 files changed, 60 insertions(+) create mode 100644 src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt create mode 100644 src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt new file mode 100644 index 0000000000..c373e4c821 --- /dev/null +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt @@ -0,0 +1,10 @@ +set (pfunit_sources + test_CIsoAtmTimeSeries.pf) + +add_pfunit_ctest(CIsoAtmTimeSeries + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share + # I don't think we need esmf here + #LINK_LIBRARIES clm csm_share esmf + #EXTRA_FINALIZE unittest_finalize_esmf + #EXTRA_USE unittestInitializeAndFinalize) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf new file mode 100644 index 0000000000..860211cc9c --- /dev/null +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -0,0 +1,49 @@ +module test_CIsoAtmTimeSeries + + ! Tests of CNCIsoAtmTimeSeriesReadMod + + use funit + use CIsoAtmTimeSeriesReadMod + use shr_kind_mod , only : r8 => shr_kind_r8 + use clm_varctl, only : use_c13, use_c14, use_c13_timeseries, use_c14_bombspike + + implicit none + + @TestCase + type, extends(TestCase) :: TestCIsoAtmTimeSeries + contains + procedure :: setUp + procedure :: tearDown + end type TestCIsoAtmTimeSeries + +contains + + subroutine setUp(this) + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c13 = .true. + use_c14 = .true. + use_c13_timeseries = .true. + use_c14_bombspike = .true. + ! Set these filenames to /dev/null as it's a file guaranteed to exist on Linnix systems + atm_c13_filename = '/dev/null' + atm_c14_filename = '/dev/null' + + end subroutine setUp + + subroutine tearDown(this) + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + end subroutine tearDown + + @Test + subroutine check_both_timeseries_on(this) + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_on + +end module test_CIsoAtmTimeSeries diff --git a/src/biogeochem/test/CMakeLists.txt b/src/biogeochem/test/CMakeLists.txt index 2ebe27c76f..e47e2429b6 100644 --- a/src/biogeochem/test/CMakeLists.txt +++ b/src/biogeochem/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(Species_test) +add_subdirectory(CIsoAtmTimeSeries_test) add_subdirectory(CNVegComputeSeed_test) add_subdirectory(CNPhenology_test) add_subdirectory(Latbaset_test) From 09e4ca9070df529d8c8dcdf1a05e30537f528152 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 00:04:51 -0700 Subject: [PATCH 070/112] Move getptr part from AtmCarbonIsotopeStreamType to the base class, to make it easier for unit testing and isolate the use of dshr and streams to that layer, also add more to the unit tests so will work --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 12 ++---------- src/biogeochem/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index 8b3b1bc59f..a943220740 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -4,8 +4,6 @@ module AtmCarbonIsotopeStreamType use abortutils , only : endrun use decompMod , only : bounds_type use CTSMForce2DStreamBaseType, only : ctsm_force_2DStream_base_type - use dshr_methods_mod , only : dshr_fldbun_getfldptr - use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU implicit none private @@ -111,10 +109,7 @@ subroutine C13Interp( this, bounds ) integer :: rc ! error return code ! Get pointer for stream data that is time and spatially interpolated to model time and grid - call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=varname_c13, fldptr1=dataptr1d, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then - call endrun( 'Error getting field pointer for '//varname_c13//' from stream data', file=sourcefile, line=__LINE__ ) - end if + call this%GetPtr1D( varname_c13, dataptr1d ) do g = bounds%begg, bounds%endg this%atm_delta_c13(g) = dataptr1d(g) @@ -164,10 +159,7 @@ subroutine C14Interp( this, bounds ) integer :: rc ! error return code ! Get pointer for stream data that is time and spatially interpolated to model time and grid - call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=varname_c14, fldptr1=dataptr1d, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then - call endrun( 'Error getting field pointer for '//varname_c14//' from stream data', file=sourcefile, line=__LINE__ ) - end if + call this%GetPtr1D( varname_c14, dataptr1d ) do g = bounds%begg, bounds%endg this%atm_delta_c14(g) = dataptr1d(g) diff --git a/src/biogeochem/CMakeLists.txt b/src/biogeochem/CMakeLists.txt index 3da0a2eab6..393e4e4db2 100644 --- a/src/biogeochem/CMakeLists.txt +++ b/src/biogeochem/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND clm_sources CNVegNitrogenStateType.F90 CNVegNitrogenFluxType.F90 CNCIsoAtmTimeSeriesReadMod.F90 + AtmCarbonIsotopeStreamType.F90 CNVegComputeSeedMod.F90 FATESFireBase.F90 FATESFireDataMod.F90 From e18d35c89aecfaaaba7071a0118c40be74506deb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 00:05:07 -0700 Subject: [PATCH 071/112] Move getptr part from AtmCarbonIsotopeStreamType to the base class, to make it easier for unit testing and isolate the use of dshr and streams to that layer, also add more to the unit tests so will work --- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 24 +++- src/unit_test_stubs/utils/CMakeLists.txt | 1 + .../utils/CTSMForce2DStreamBaseType.F90 | 120 ++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index 4c87b2d205..96bf15f4e1 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -16,7 +16,7 @@ module CTSMForce2DStreamBaseType type, abstract, public :: ctsm_force_2DStream_base_type private - type(shr_strdata_type), public :: sdat ! Stream data type + type(shr_strdata_type) :: sdat ! Stream data type character(len=FL) :: stream_filename ! The stream data filename (also in sdat) character(len=CL) :: stream_name ! The stream name (also in sdat) contains @@ -27,6 +27,7 @@ module CTSMForce2DStreamBaseType procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method procedure, public, non_overridable :: CleanBase ! Clean method for the base type procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure, public :: GetPtr1D ! Get pointer to the 1D data array procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into output data end type ctsm_force_2DStream_base_type @@ -174,4 +175,25 @@ subroutine Advance(this) end if end subroutine Advance + subroutine GetPtr1D(this, fldname, dataptr1d) + ! Get the pointer to the 1D data array for the given field name + ! Uses: + use dshr_methods_mod , only : dshr_fldbun_getfldptr + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + character(*), intent(in) :: fldname ! field name to get pointer for + real(r8), pointer :: dataptr1d(:) ! Pointer to the 1D data + + ! Local variables + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=fldname, fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + call endrun( 'Error getting field pointer for '//trim(fldname)//' from stream data', file=sourcefile, line=__LINE__ ) + end if + + end subroutine GetPtr1D + + end module CTSMForce2DStreamBaseType diff --git a/src/unit_test_stubs/utils/CMakeLists.txt b/src/unit_test_stubs/utils/CMakeLists.txt index 4a8920af27..a61bffa9eb 100644 --- a/src/unit_test_stubs/utils/CMakeLists.txt +++ b/src/unit_test_stubs/utils/CMakeLists.txt @@ -10,6 +10,7 @@ list(APPEND clm_sources "${clm_genf90_sources}") list(APPEND clm_sources restUtilMod_stub.F90 + CTSMForce2DStreamBaseType.F90 perfMod_stub.F90 spmdMod_stub.F90 clmfates_paraminterfaceMod_stub.F90 diff --git a/src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 b/src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 new file mode 100644 index 0000000000..59e6a4d551 --- /dev/null +++ b/src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 @@ -0,0 +1,120 @@ +module CTSMForce2DStreamBaseType + + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL + use abortutils , only : endrun + use decompMod , only : bounds_type + use clm_varctl, only : FL => fname_len + + implicit none + private + + type, abstract, public :: ctsm_force_2DStream_base_type + private + character(len=FL) :: stream_filename ! The stream data filename (also in sdat) + character(len=CL) :: stream_name ! The stream name (also in sdat) + contains + + ! PUBLIC METHODS + procedure(Init_interface) , public, deferred :: Init + procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams, , store the g_to_ig index array + procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method + procedure, public, non_overridable :: CleanBase ! Clean method for the base type + procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure, public :: GetPtr1D ! Get pointer to the 1D data array + procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into output data + + end type ctsm_force_2DStream_base_type + + abstract interface + + subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + year_first, year_last, model_year_align ) + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + end subroutine Init_interface + + subroutine Clean_interface(this) + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + end subroutine Clean_interface + + subroutine Interp_interface(this, bounds) + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + end subroutine Interp_interface + + end interface + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + + contains + + subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + end subroutine InitBase + + subroutine CleanBase( this ) + class(ctsm_force_2DStream_base_type) , intent(inout) :: this + + call endrun('CTSMForce2DStreamBaseType: CleanBase method not implemented in stub') + + end subroutine CleanBase + + subroutine Advance(this) + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + + call endrun('CTSMForce2DStreamBaseType: Advance method not implemented in stub') + + end subroutine Advance + + subroutine GetPtr1D(this, fldname, dataptr1d) + ! Get the pointer to the 1D data array for the given field name + ! Uses: + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + character(*), intent(in) :: fldname ! field name to get pointer for + real(r8), pointer :: dataptr1d(:) ! Pointer to the 1D data + + end subroutine GetPtr1D + +end module CTSMForce2DStreamBaseType From fbdd596e79f15b48fb7dc36888fc583b7eea20bf Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 00:51:59 -0700 Subject: [PATCH 072/112] Get the new unit test working --- .../test/CIsoAtmTimeSeries_test/CMakeLists.txt | 13 +++++++------ .../test_CIsoAtmTimeSeries.pf | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt index c373e4c821..9a9885508c 100644 --- a/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt @@ -1,10 +1,11 @@ -set (pfunit_sources +set(pfunit_sources test_CIsoAtmTimeSeries.pf) add_pfunit_ctest(CIsoAtmTimeSeries TEST_SOURCES "${pfunit_sources}" - LINK_LIBRARIES clm csm_share - # I don't think we need esmf here - #LINK_LIBRARIES clm csm_share esmf - #EXTRA_FINALIZE unittest_finalize_esmf - #EXTRA_USE unittestInitializeAndFinalize) + LINK_LIBRARIES clm csm_share) + +# I don't think we need esmf here +# LINK_LIBRARIES clm csm_share esmf +# EXTRA_FINALIZE unittest_finalize_esmf +# EXTRA_USE unittestInitializeAndFinalize) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf index 860211cc9c..a9ce09c3e2 100644 --- a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -3,9 +3,9 @@ module test_CIsoAtmTimeSeries ! Tests of CNCIsoAtmTimeSeriesReadMod use funit - use CIsoAtmTimeSeriesReadMod + use CIsoAtmTimeSeriesMod use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varctl, only : use_c13, use_c14, use_c13_timeseries, use_c14_bombspike + use clm_varctl, only : use_c13, use_c14 implicit none @@ -25,7 +25,7 @@ contains use_c14 = .true. use_c13_timeseries = .true. use_c14_bombspike = .true. - ! Set these filenames to /dev/null as it's a file guaranteed to exist on Linnix systems + ! Set these filenames to /dev/null as it's a file guaranteed to exist on Linux systems atm_c13_filename = '/dev/null' atm_c14_filename = '/dev/null' From 51f5680c47e458cec63642773b04efdbd2c66024 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 13:47:36 -0700 Subject: [PATCH 073/112] Add CIsoSetNMLInputs for unit testing, and add unit tests for all the valid combinations and make sure the checking works --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 32 ++++++++++++ .../test_CIsoAtmTimeSeries.pf | 52 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 28cc8d41d4..f31246a9c4 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -41,6 +41,7 @@ module CIsoAtmTimeseriesMod ! Private subroutines only made public for unit testing public:: CIsoCheckNMLInputs ! Check that the namelist inputs are valid + public:: CIsoSetNMLInputs ! Set the namelist inputs for unit testing public:: CIsoSetControl ! Set the control variables for Carbon Isotopes public:: CIsoLogControl ! Write out the control settings to the logfile @@ -290,6 +291,37 @@ subroutine CIsoCheckNMLInputs() end subroutine CIsoCheckNMLInputs + !----------------------------------------------------------------------- + subroutine CIsoSetNMLInputs( stream_fldfilename_atm_c13_in, stream_fldfilename_atm_c14_in, & + use_c13_streams_in, use_c14_streams_in ) + ! + ! !DESCRIPTION: + ! Set the namelist inputs for unit testing + ! + ! Arguments: + character(len=*), intent(in), optional :: stream_fldfilename_atm_c13_in + character(len=*), intent(in), optional :: stream_fldfilename_atm_c14_in + logical, intent(in), optional :: use_c13_streams_in + logical, intent(in), optional :: use_c14_streams_in + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + if ( present(stream_fldfilename_atm_c13_in) ) then + stream_fldfilename_atm_c13 = stream_fldfilename_atm_c13_in + end if + if ( present(stream_fldfilename_atm_c14_in) ) then + stream_fldfilename_atm_c14 = stream_fldfilename_atm_c14_in + end if + if ( present(use_c13_streams_in) ) then + use_c13_streams = use_c13_streams_in + end if + if ( present(use_c14_streams_in) ) then + use_c14_streams = use_c14_streams_in + end if + + end subroutine CIsoSetNMLInputs + + !----------------------------------------------------------------------- subroutine C14BombSpike( bounds ) ! diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf index a9ce09c3e2..95cfa82b44 100644 --- a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -38,6 +38,7 @@ contains @Test subroutine check_both_timeseries_on(this) + ! Check that it works when both timeseries are on without using streams class(TestCIsoAtmTimeSeries), intent(inout) :: this call CIsoCheckNMLInputs() @@ -46,4 +47,55 @@ contains end subroutine check_both_timeseries_on + @Test + subroutine check_both_timeseries_streams_on(this) + ! Check that it works when both timeseries are on and using streams + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & + stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_streams_on + + @Test + subroutine check_both_timeseries_off(this) + ! Check that it works when both timeseries are off + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + use_c13_timeseries = .false. + use_c14_bombspike = .false. + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & + stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_off + + @Test + subroutine check_ciso_off(this) + ! Check that it works when both carbon isotopes are off + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + use_c13_timeseries = .false. + use_c14_bombspike = .false. + use_c13 = .false. + use_c14 = .false. + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & + stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_ciso_off + end module test_CIsoAtmTimeSeries From 1e2e4ca5e8fc7d6f2ec4f3146ce78515e9222d5e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 15:14:14 -0700 Subject: [PATCH 074/112] Clarify one error statement --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index f31246a9c4..e110434f8b 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -270,7 +270,7 @@ subroutine CIsoCheckNMLInputs() end if else if ( len_trim(atm_c14_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c14) /= 0 ) then - call endrun(msg="use_c14_bombspike TRUE but stream_fldfilename_atm_c14/stream_fldfilename_atm_c14 are set", & + call endrun(msg="use_c14_bombspike false but stream_fldfilename_atm_c14 or stream_fldfilename_atm_c14 is set", & file=sourcefile, line=__LINE__) end if end if @@ -279,12 +279,12 @@ subroutine CIsoCheckNMLInputs() ! if ( use_c13_timeseries ) then if ( len_trim(atm_c13_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c13) /= 0 ) then - call endrun(msg="use_c13_bombspike TRUE but stream_fldfilename_atm_c13/stream_fldfilename_atm_c13 aren't blank", & + call endrun(msg="use_c13_timeseries TRUE but stream_fldfilename_atm_c13/stream_fldfilename_atm_c13 are set", & file=sourcefile, line=__LINE__) end if else if ( len_trim(atm_c13_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c13) /= 0 ) then - call endrun(msg="use_c13_bombspike is false but stream_fldfilename_atm_c13 and stream_fldfilename_atm_c13 aren't blank", & + call endrun(msg="use_c13_timeseries is false but stream_fldfilename_atm_c13 or stream_fldfilename_atm_c13 are set", & file=sourcefile, line=__LINE__) end if end if From 65e257f366b5132bdd137934194d4b60181bf4bd Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 9 Nov 2025 15:15:46 -0700 Subject: [PATCH 075/112] Add 10 new abort on error tests for all the combinations I could think of, these mostly fail so need to be reconciled to get the errors working correctly --- .../test_CIsoAtmTimeSeries.pf | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf index 95cfa82b44..570253deba 100644 --- a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -16,6 +16,8 @@ module test_CIsoAtmTimeSeries procedure :: tearDown end type TestCIsoAtmTimeSeries + character(len=100) :: expected_msg + contains subroutine setUp(this) @@ -98,4 +100,172 @@ contains end subroutine check_ciso_off + @Test + subroutine abort_if_both_c13_plain_and_stream_timeseries_files_set(this) + ! Check that it aborts if both the plain and stream C13 timeseries files are set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + @assertTrue(use_c13_timeseries) + atm_c13_filename = 'plain_c13_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_timeseries_file' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is TRUE but both stream_fldfilename_atm_c13 and atm_c13_filename are set" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_set + + @Test + subroutine abort_if_both_c14_plain_and_stream_timeseries_files_set(this) + ! Check that it aborts if both the plain and stream C14 timeseries files are set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + atm_c14_filename = 'plain_c14_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_timeseries_file' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike is TRUE but both stream_fldfilename_atm_c14 and atm_c14_filename are set" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_set + + @Test + subroutine abort_if_both_c13_plain_and_stream_timeseries_files_blank(this) + ! Check that it aborts if both the plain and stream C13 timeseries files are blank + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + @assertTrue(use_c13_timeseries) + atm_c13_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is TRUE but neither stream_fldfilename_atm_c13 nor atm_c13_filename are set" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_blank + + @Test + subroutine abort_if_both_c14_plain_and_stream_timeseries_files_blank(this) + ! Check that it aborts if both the plain and stream C14 timeseries files are blank + class(TestCIsoAtmTimeSeries), intent(inout) :: this + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike is TRUE but neither stream_fldfilename_atm_c14 nor atm_c14_filename are set" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_blank + + @Test + subroutine abort_if_c13_timeseries_off_and_plain_timeseries_file_set(this) + ! Check that it aborts if c13 timeseries off, but the plain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + use_c13_timeseries = .false. + @assertFalse(use_c13_timeseries) + atm_c13_filename = 'plain_c13_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is false but either stream_fldfilename_atm_c13 or atm_c13_filename is set and should NOT be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_timeseries_off_and_plain_timeseries_file_set + + @Test + subroutine abort_if_c13_timeseries_off_and_stream_timeseries_file_set(this) + ! Check that it aborts if c13 timeseries off, but the streamplain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + use_c13_timeseries = .false. + @assertFalse(use_c13_timeseries) + atm_c13_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_filename ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is false but either stream_fldfilename_atm_c13 or atm_c13_filename is set and should NOT be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_timeseries_off_and_stream_timeseries_file_set + + @Test + subroutine abort_if_c14_bombspike_off_and_plain_timeseries_file_set(this) + ! Check that it aborts if c14 timeseries off, but the pain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + integer :: i + + ! This should be true whether C14 is on or off + do i = 1, 2 + if ( i == 1 ) then + @assertTrue(use_c14) + else + use_c14 = .false. + end if + use_c14_bombspike = .false. + atm_c14_filename = 'plain_c14_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike is false but either stream_fldfilename_atm_c14 or atm_c14_filename is set and should NOT be" + @assertExceptionRaised(expected_msg) + end do + + end subroutine abort_if_c14_bombspike_off_and_plain_timeseries_file_set + + @Test + subroutine abort_if_c14_bombspike_off_and_stream_timeseries_file_set(this) + ! Check that it aborts if c14 timeseries off, but the stream timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + integer :: i + + ! This should be true whether C14 is on or off + do i = 1, 2 + if ( i == 1 ) then + @assertTrue(use_c14) + else + use_c14 = .false. + end if + use_c14_bombspike = .false. + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_filename' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike is false but either stream_fldfilename_atm_c14 or atm_c14_filename is set and should NOT be" + @assertExceptionRaised(expected_msg) + end do + + end subroutine abort_if_c14_bombspike_off_and_stream_timeseries_file_set + + @Test + subroutine abort_if_c13_off_but_c13_timeseries_on(this) + ! Check that it aborts if c13 is off, but the c13 timeseries is on + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c13 = .false. + use_c13_timeseries = .true. + atm_c13_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13 is false but use_c13_timeseries is TRUE (use_c13_timeseries can only be TRUE if use_c13 is TRUE)" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_off_but_c13_timeseries_on + + @Test + subroutine abort_if_c14_off_but_c14_bombspike_on(this) + ! Check that it aborts if c14 is off, but the c14 bombspike is on + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c14 = .false. + @assertFalse(use_c14) + use_c14_bombspike = .true. + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14 is false but use_c14_bombspike is TRUE (use_c14_bombspike can only be TRUE if use_c14 is TRUE)" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c14_off_but_c14_bombspike_on + end module test_CIsoAtmTimeSeries From bebcf4a0d07b903a92e8daaa52f5a0965aa2d8e7 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 10:18:37 -0700 Subject: [PATCH 076/112] Make sure use_c13/c14_streams is set correctly at initialization, remove some uneeded shr_Asserts, clarify error messages, change some asserts into endrun calls, get all of the logic working --- src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index e110434f8b..736924fd4f 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -153,12 +153,14 @@ subroutine CIsoSetControl() if ( len_trim(stream_fldfilename_atm_c13) /= 0 ) then use_c13_streams = .true. else + use_c13_streams = .false. call shr_assert( len_trim(atm_c13_filename) /= 0 , & msg="ERROR: use_c13_timeseries is true but atm_c13_filename is blank", file=sourcefile, line=__LINE__) call shr_assert( .not. use_c13_streams , & msg="ERROR: stream_fldfilename_atm_c13 is blank but use_c13_streams is not TRUE", file=sourcefile, line=__LINE__) end if else + use_c13_streams = .false. call shr_assert( .not. use_c13_streams , & msg="ERROR: use_c13_timeseries is false, but use_c13_streams is TRUE", file=sourcefile, line=__LINE__) call shr_assert( len_trim(atm_c13_filename) == 0 , & @@ -170,6 +172,7 @@ subroutine CIsoSetControl() if ( len_trim(stream_fldfilename_atm_c14) /= 0 ) then use_c14_streams = .true. else + use_c14_streams = .false. call shr_assert( len_trim(atm_c14_filename) /= 0 , & msg="ERROR: use_c14_bombspike is true but atm_c14_filename is blank", file=sourcefile, line=__LINE__) call shr_assert( .not. use_c14_streams , & @@ -177,6 +180,7 @@ subroutine CIsoSetControl() file=sourcefile, line=__LINE__) end if else + use_c14_streams = .false. call shr_assert( .not. use_c14_streams , & msg="ERROR: use_c14_bombspike is false, but use_c14_streams is TRUE", file=sourcefile, line=__LINE__) call shr_assert( len_trim(atm_c14_filename) == 0, & @@ -225,9 +229,9 @@ subroutine CIsoLogControl() end if else call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0 , & - msg="use_c14_timeseries is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + msg="use_c14_bombspike is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) call shr_assert( len_trim(atm_c14_filename) == 0 , & - msg="use_c14_timeseries is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + msg="use_c14_bombspike is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) write(iulog,*) 'C14 atmospheric data will be global constant pre-industrial level' end if @@ -244,20 +248,19 @@ subroutine CIsoCheckNMLInputs() !----------------------------------------------------------------------- ! When carbon isotopes are off nothing should be set if ( .not. use_c13 )then - call shr_assert( .not. use_c13_timeseries, & - msg="ERROR: use_c13 is false but use_c13_timeseries is true", file=sourcefile, line=__LINE__) - call shr_assert( len_trim(atm_c13_filename) == 0, & - msg="ERROR: use_c13 is false but atm_c13_filename is set", file=sourcefile, line=__LINE__) - call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0, & - msg="ERROR: use_c13 is false but stream_fldfilename_atm_c13 is set", file=sourcefile, line=__LINE__) + if ( use_c13_timeseries ) then + call endrun( msg="use_c13 is false but use_c13_timeseries is TRUE " // & + "(use_c13_timeseries can only be TRUE if use_c13 is TRUE)", file=sourcefile, line=__LINE__) + return + end if end if if ( .not. use_c14 )then - call shr_assert( .not. use_c14_bombspike, & - msg="ERROR: use_c14 is false but use_c14_bombspike is true", file=sourcefile, line=__LINE__) - call shr_assert( len_trim(atm_c14_filename) == 0, & - msg="ERROR: use_c14 is false but atm_c14_filename is set", file=sourcefile, line=__LINE__) - call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0, & - msg="ERROR: use_c14 is false but stream_fldfilename_atm_c14 is set", file=sourcefile, line=__LINE__) + if ( use_c14_bombspike ) then + call endrun( msg="use_c14 is false but use_c14_bombspike is TRUE " // & + "(use_c14_bombspike can only be TRUE if use_c14 is TRUE)", & + file=sourcefile, line=__LINE__) + return + end if end if ! @@ -265,13 +268,20 @@ subroutine CIsoCheckNMLInputs() ! if ( use_c14_bombspike ) then if ( len_trim(atm_c14_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c14) /= 0 ) then - call endrun(msg="use_c14_bombspike TRUE but both stream_fldfilename_atm_c14/stream_fldfilename_atm_c14 are set", & + call endrun(msg="use_c14_bombspike TRUE but both atm_c14_filename AND stream_fldfilename_atm_c14 are set and only one should be", & + file=sourcefile, line=__LINE__) + return + end if + if ( len_trim(atm_c14_filename) == 0 .and. len_trim(stream_fldfilename_atm_c14) == 0 ) then + call endrun(msg="use_c14_bombspike TRUE but neither atm_c14_filename nor stream_fldfilename_atm_c14 are set and one or the other needs to be", & file=sourcefile, line=__LINE__) + return end if else if ( len_trim(atm_c14_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c14) /= 0 ) then - call endrun(msg="use_c14_bombspike false but stream_fldfilename_atm_c14 or stream_fldfilename_atm_c14 is set", & + call endrun(msg="use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be", & file=sourcefile, line=__LINE__) + return end if end if ! @@ -279,13 +289,20 @@ subroutine CIsoCheckNMLInputs() ! if ( use_c13_timeseries ) then if ( len_trim(atm_c13_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c13) /= 0 ) then - call endrun(msg="use_c13_timeseries TRUE but stream_fldfilename_atm_c13/stream_fldfilename_atm_c13 are set", & + call endrun(msg="use_c13_timeseries TRUE but both atm_c13_filename AND stream_fldfilename_atm_c13 are set and only one should be", & file=sourcefile, line=__LINE__) + return + end if + if ( len_trim(atm_c13_filename) == 0 .and. len_trim(stream_fldfilename_atm_c13) == 0 ) then + call endrun(msg="use_c13_timeseries TRUE but neither atm_c13_filename nor stream_fldfilename_atm_c13 are set and one or the other needs to be", & + file=sourcefile, line=__LINE__) + return end if else if ( len_trim(atm_c13_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c13) /= 0 ) then - call endrun(msg="use_c13_timeseries is false but stream_fldfilename_atm_c13 or stream_fldfilename_atm_c13 are set", & + call endrun(msg="use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be", & file=sourcefile, line=__LINE__) + return end if end if From 24407f0cfa6f0d93ea1b54dbcdb31fa193ad931d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 10:19:32 -0700 Subject: [PATCH 077/112] Update so that the unit tests all work now --- .../test_CIsoAtmTimeSeries.pf | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf index 570253deba..01866b7617 100644 --- a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -16,7 +16,7 @@ module test_CIsoAtmTimeSeries procedure :: tearDown end type TestCIsoAtmTimeSeries - character(len=100) :: expected_msg + character(len=200) :: expected_msg contains @@ -73,8 +73,8 @@ contains atm_c14_filename = '' use_c13_timeseries = .false. use_c14_bombspike = .false. - call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & - stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', & + stream_fldfilename_atm_c14_in = ' ' ) call CIsoCheckNMLInputs() call CIsoSetControl() call CIsoLogControl() @@ -92,8 +92,8 @@ contains use_c14_bombspike = .false. use_c13 = .false. use_c14 = .false. - call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & - stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', & + stream_fldfilename_atm_c14_in = ' ' ) call CIsoCheckNMLInputs() call CIsoSetControl() call CIsoLogControl() @@ -108,9 +108,12 @@ contains @assertTrue(use_c13) @assertTrue(use_c13_timeseries) atm_c13_filename = 'plain_c13_timeseries_file' - call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_timeseries_file' ) + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + @assertTrue(len_trim(atm_c14_filename) /= 0) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_timeseries_file', stream_fldfilename_atm_c14_in = ' ') call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c13_timeseries is TRUE but both stream_fldfilename_atm_c13 and atm_c13_filename are set" + expected_msg = "ABORTED: use_c13_timeseries TRUE but both atm_c13_filename AND stream_fldfilename_atm_c13 are set and only one should be" @assertExceptionRaised(expected_msg) end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_set @@ -125,7 +128,7 @@ contains atm_c14_filename = 'plain_c14_timeseries_file' call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_timeseries_file' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c14_bombspike is TRUE but both stream_fldfilename_atm_c14 and atm_c14_filename are set" + expected_msg = "ABORTED: use_c14_bombspike TRUE but both atm_c14_filename AND stream_fldfilename_atm_c14 are set and only one should be" @assertExceptionRaised(expected_msg) end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_set @@ -138,9 +141,12 @@ contains @assertTrue(use_c13) @assertTrue(use_c13_timeseries) atm_c13_filename = ' ' - call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + @assertTrue(len_trim(atm_c14_filename) /= 0) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', stream_fldfilename_atm_c14_in = ' ') call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c13_timeseries is TRUE but neither stream_fldfilename_atm_c13 nor atm_c13_filename are set" + expected_msg = "ABORTED: use_c13_timeseries TRUE but neither atm_c13_filename nor stream_fldfilename_atm_c13 are set and one or the other needs to be" @assertExceptionRaised(expected_msg) end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_blank @@ -154,7 +160,7 @@ contains atm_c14_filename = ' ' call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c14_bombspike is TRUE but neither stream_fldfilename_atm_c14 nor atm_c14_filename are set" + expected_msg = "ABORTED: use_c14_bombspike TRUE but neither atm_c14_filename nor stream_fldfilename_atm_c14 are set and one or the other needs to be" @assertExceptionRaised(expected_msg) end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_blank @@ -170,7 +176,7 @@ contains atm_c13_filename = 'plain_c13_timeseries_file' call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c13_timeseries is false but either stream_fldfilename_atm_c13 or atm_c13_filename is set and should NOT be" + expected_msg = "ABORTED: use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be" @assertExceptionRaised(expected_msg) end subroutine abort_if_c13_timeseries_off_and_plain_timeseries_file_set @@ -186,7 +192,7 @@ contains atm_c13_filename = ' ' call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_filename ' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c13_timeseries is false but either stream_fldfilename_atm_c13 or atm_c13_filename is set and should NOT be" + expected_msg = "ABORTED: use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be" @assertExceptionRaised(expected_msg) end subroutine abort_if_c13_timeseries_off_and_stream_timeseries_file_set @@ -208,7 +214,7 @@ contains atm_c14_filename = 'plain_c14_timeseries_file' call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c14_bombspike is false but either stream_fldfilename_atm_c14 or atm_c14_filename is set and should NOT be" + expected_msg = "ABORTED: use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be" @assertExceptionRaised(expected_msg) end do @@ -231,7 +237,7 @@ contains atm_c14_filename = ' ' call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_filename' ) call CIsoCheckNMLInputs() - expected_msg = "ABORTED: use_c14_bombspike is false but either stream_fldfilename_atm_c14 or atm_c14_filename is set and should NOT be" + expected_msg = "ABORTED: use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be" @assertExceptionRaised(expected_msg) end do From 5b95a90107b2fd95cb73291ae7a814376c98f891 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 10:44:34 -0700 Subject: [PATCH 078/112] Point to share branch update --- .gitmodules | 7 +++++-- share | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2abc02aa48..3d3d7499f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -99,8 +99,11 @@ fxDONOTUSEurl = https://github.com/ESCOMP/CDEPS.git [submodule "share"] path = share -url = https://github.com/ESCOMP/CESM_share -fxtag = share1.1.9 +#url = https://github.com/ESCOMP/CESM_share +url = https://github.com/ekluzek/CESM_share +#fxtag = share1.1.9 +#fxtag = add_unittest_abort_file_and_line +fxtag = 6f5d09335d709afcf0d43f34ff8d33be1abe3325 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/CESM_share diff --git a/share b/share index 14338bef3f..6f5d09335d 160000 --- a/share +++ b/share @@ -1 +1 @@ -Subproject commit 14338bef3fa604d49160e376257264db1d3313e5 +Subproject commit 6f5d09335d709afcf0d43f34ff8d33be1abe3325 From 9c59a5547e4d96b21f418519aaa4a01ad5f16882 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 15:14:32 -0700 Subject: [PATCH 079/112] Move the setting of SRCROOT up higher so can be used to set the paths for share and cmeps, this is needed in order to find ESMF, I meant with @billsacks after CSEG to show the problem and figure out a solution --- src/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ae5f4cc4ea..bc925b7dd1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,16 +3,16 @@ cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY}) include(CIME_initial_setup) -#list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../tools/mksurfdata_esmf/cmake") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../components/cmeps/cmake") +set(SRCROOT "${CIMEROOT}/..") +set(CLM_ROOT "..") + +list(APPEND CMAKE_MODULE_PATH "${SRCROOT}/share/cmake") +list(APPEND CMAKE_MODULE_PATH "${SRCROOT}/components/cmeps/cmake") project(clm_tests Fortran C) include(CIME_utils) -set(SRCROOT "${CIMEROOT}/..") -set(CLM_ROOT "..") # find needed external packages # NetCDF is required -- because PIO and NetCDF are required by the standard default ESMF libraries From 618c2a5db842a9ede591a47bb05e6bfc1fd8c757 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 15:22:09 -0700 Subject: [PATCH 080/112] Add the FUNITCTSM test to the prealpha testlist --- cime_config/testdefs/testlist_clm.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 4e9a8c222e..d463be13e8 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -4399,6 +4399,7 @@ + From fffa9596d1bd211048fe3d287d0b53f878185236 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 21:10:27 -0700 Subject: [PATCH 081/112] Update share to tagged one with the change I put in --- .gitmodules | 7 ++----- share | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3d3d7499f9..69ace57fc3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -99,11 +99,8 @@ fxDONOTUSEurl = https://github.com/ESCOMP/CDEPS.git [submodule "share"] path = share -#url = https://github.com/ESCOMP/CESM_share -url = https://github.com/ekluzek/CESM_share -#fxtag = share1.1.9 -#fxtag = add_unittest_abort_file_and_line -fxtag = 6f5d09335d709afcf0d43f34ff8d33be1abe3325 +url = https://github.com/ESCOMP/CESM_share +fxtag = share1.1.15 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/CESM_share diff --git a/share b/share index 6f5d09335d..ec0475f05b 160000 --- a/share +++ b/share @@ -1 +1 @@ -Subproject commit 6f5d09335d709afcf0d43f34ff8d33be1abe3325 +Subproject commit ec0475f05bde376bab73c74d09450549ec746d86 From 07a73490a4dc82c27ff94aedb92c5246c7f4971c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 22:36:18 -0700 Subject: [PATCH 082/112] Need another query of use_c13_timeseries after it's set by add_default --- bld/CLMBuildNamelist.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index a4ac04b94f..9221ab9cde 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3788,6 +3788,7 @@ sub setup_logic_c_isotope { my $atm_c13_filename = $nl->get_value('atm_c13_filename'); if ( &value_is_true($use_c13) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c13_timeseries', 'use_c13'=>$use_c13 ); + $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); if ( &value_is_true($use_c13_timeseries) ) { if ( defined($stream_fldfilename_atm_c13) ) { setup_logic_c13_streams($opts, $nl_flags, $definition, $defaults, $nl); From 2e8be576b76b0faf69ac62ea3023605aa3cac211 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 11 Nov 2025 22:46:24 -0700 Subject: [PATCH 083/112] Do the same for use_c14_bombspike, so that namelists aren't different other than the addition of the new carbon isotopes namelist --- bld/CLMBuildNamelist.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 9221ab9cde..1071ec1e72 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3762,6 +3762,7 @@ sub setup_logic_c_isotope { my $atm_c14_filename = $nl->get_value('atm_c14_filename'); if ( &value_is_true($use_c14) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c14_bombspike', 'use_c14'=>$use_c14 ); + $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); if ( &value_is_true($use_c14_bombspike) ) { if ( defined($stream_fldfilename_atm_c14) ) { setup_logic_c14_streams($opts, $nl_flags, $definition, $defaults, $nl); From dc4355c241da5c373ca85f4470d317bc3a596e4e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 14:06:41 -0700 Subject: [PATCH 084/112] Add testmods to compare the Carbon isotope cmip6 and cmip7 data --- .../clm/ciso_cmip7_monthly_2013Start/include_user_mods | 1 + .../clm/ciso_cmip7_monthly_2013Start/user_nl_clm | 6 ++++++ .../clm/ciso_monthly_2013Start/include_user_mods | 1 + .../testmods_dirs/clm/ciso_monthly_2013Start/shell_commands | 1 + .../testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm | 3 +++ 5 files changed, 12 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods new file mode 100644 index 0000000000..5f7ca15ec0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods @@ -0,0 +1 @@ +../ciso_monthly_2013Start diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm new file mode 100644 index 0000000000..4dac396c9f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm @@ -0,0 +1,6 @@ + stream_fldfilename_atm_c13 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' + stream_fldfilename_atm_c14 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' + stream_year_first_atm_c14 = 2013 + model_year_align_atm_c14 = 2013 + stream_year_first_atm_c13 = 2013 + model_year_align_atm_c13 = 2013 diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods new file mode 100644 index 0000000000..2cc5720115 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods @@ -0,0 +1 @@ +../ciso_monthly diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands new file mode 100644 index 0000000000..035842f982 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands @@ -0,0 +1 @@ +./xmlchange RUN_STARTDATE=2013-01-01 diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm new file mode 100644 index 0000000000..b0129f7f6e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm @@ -0,0 +1,3 @@ +! Add C13/C14 output to validate the incoming data +hist_fincl1 += 'RC13_CANAIR', 'RC14_CANAIR' +hist_fincl2 = 'RC13_CANAIR', 'RC14_CANAIR' From f68556b0dca9ec46613274341fedb91168e5ca69 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 14:13:45 -0700 Subject: [PATCH 085/112] Add tests for carbon isotope datasets --- cime_config/testdefs/testlist_clm.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index d463be13e8..64c9274b3e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -195,6 +195,25 @@ + + + + + + + + + + + + + + + + + + + From c1fc04112b15dfadacff99eaac89b2c2239fb9f9 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 17:03:19 -0700 Subject: [PATCH 086/112] Add descriptions and formatting of the base class --- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 110 ++++++++++++++---- 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 index 96bf15f4e1..174fa3554e 100644 --- a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -1,5 +1,17 @@ module CTSMForce2DStreamBaseType +! +! Description: +! +! Base module to handle 2D streams in CTSM. Specific streams extend this object +! for the details needed to handle a specific stream file. +! +! Having this base type allows the ESMF specific streams implementation to be isolated +! from the CTSM code. This allows the streams code this is based on to change in one place. +! It also makes it easier to unit-test extensions of this type as they become pretty standard +! CTSM code and there is a unit-tester stub for this code. +! + #include "shr_assert.h" use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU @@ -14,6 +26,9 @@ module CTSMForce2DStreamBaseType implicit none private + !----------------------------------------------------------------------- + ! Base 2D streams type + !----------------------------------------------------------------------- type, abstract, public :: ctsm_force_2DStream_base_type private type(shr_strdata_type) :: sdat ! Stream data type @@ -21,21 +36,32 @@ module CTSMForce2DStreamBaseType character(len=CL) :: stream_name ! The stream name (also in sdat) contains - ! PUBLIC METHODS - procedure(Init_interface) , public, deferred :: Init - procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams, , store the g_to_ig index array - procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method - procedure, public, non_overridable :: CleanBase ! Clean method for the base type - procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date - procedure, public :: GetPtr1D ! Get pointer to the 1D data array - procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into output data + ! PUBLIC METHODS + procedure(Init_interface) , public, deferred :: Init ! Initiale the extended type + procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams + procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method + procedure, public, non_overridable :: CleanBase ! Clean method for the base type + procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure, public :: GetPtr1D ! Get pointer to the 1D data array + procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into CTSM data end type ctsm_force_2DStream_base_type + !----------------------------------------------------------------------- + !----------------------------------------------------------------------- + ! Interfaces that will be deferred to the extended type + !----------------------------------------------------------------------- abstract interface + !----------------------------------------------------------------------- + subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) + ! Description: + ! + ! Initialize the specific stream type that extends the base type + ! Normally the extended type will call the InitBase as well as doing other initialization needed + ! ! Uses: use decompMod , only : bounds_type import :: ctsm_force_2DStream_base_type @@ -54,7 +80,12 @@ subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalg integer, intent(in) :: model_year_align ! align yearFirst with this model year end subroutine Init_interface + !----------------------------------------------------------------------- + subroutine Clean_interface(this) + ! Description: + ! Clean up any memory allocated in the specific stream type that extends the base type + ! Normally the extended type will call the CleanBase method as well as other things needed. ! Uses: import :: ctsm_force_2DStream_base_type ! @@ -62,7 +93,13 @@ subroutine Clean_interface(this) class(ctsm_force_2DStream_base_type), intent(inout) :: this end subroutine Clean_interface + !----------------------------------------------------------------------- + subroutine Interp_interface(this, bounds) + ! Description: + ! Get the current time data from the streams and put it into the data of the extension. + ! What this looks like may vary with the streams extension, but in general it will use + ! The GetPtr1D method to get the streams data. ! Uses: use decompMod , only : bounds_type import :: ctsm_force_2DStream_base_type @@ -73,14 +110,24 @@ subroutine Interp_interface(this, bounds) end subroutine Interp_interface end interface + !----------------------------------------------------------------------- - character(len=*), parameter, private :: sourcefile = & - __FILE__ + character(len=*), parameter, private :: sourcefile = & + __FILE__ - contains + !----------------------------------------------------------------------- + contains + !----------------------------------------------------------------------- - subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & - year_first, year_last, model_year_align ) + !----------------------------------------------------------------------- + + subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! + ! Description: + ! + ! Initialization of the base type. Extended types will normally call this as part of their initialization. + ! ! Uses: use lnd_comp_shr , only : mesh, model_clock use dshr_strdata_mod , only : shr_strdata_init_from_inline @@ -138,9 +185,16 @@ subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tin write(iulog,*) ' Streams initialization failing for ', trim(name), ' stream file = ', trim(fldfilename) call endrun( 'CTSM forcing Streams initialization failing', file=sourcefile, line=__LINE__ ) end if - end subroutine InitBase + end subroutine InitBase - subroutine CleanBase( this ) + !----------------------------------------------------------------------- + + subroutine CleanBase( this ) + ! Description: + ! Clean up any memory in the base type as needed. + ! Normally types that extend this base type will call this as part of their clean operation + ! + ! Arguments: class(ctsm_force_2DStream_base_type) , intent(inout) :: this integer :: ierr ! error code @@ -148,9 +202,16 @@ subroutine CleanBase( this ) ! Currently no data to deallocate other than the stream data type ! The stream data type doesn't have a clean method right now ! So doing a few things manually here - end subroutine CleanBase + end subroutine CleanBase - subroutine Advance(this) + !----------------------------------------------------------------------- + + subroutine Advance(this) + ! + ! Description: + ! + ! Advance the stream to the current time-step + ! ! Uses: use clm_time_manager , only : get_curr_date use dshr_strdata_mod , only : shr_strdata_advance @@ -173,10 +234,18 @@ subroutine Advance(this) write(iulog,*) ' Streams advance failing for ', trim(this%stream_name), ' stream file = ', trim(this%stream_filename) call endrun( 'CTSM forcing Streams advance failing', file=sourcefile, line=__LINE__ ) end if - end subroutine Advance + end subroutine Advance + + !----------------------------------------------------------------------- - subroutine GetPtr1D(this, fldname, dataptr1d) + subroutine GetPtr1D(this, fldname, dataptr1d) + ! + ! Description: + ! ! Get the pointer to the 1D data array for the given field name + ! Normally stream extensions will use this in the Interp method to + ! save the stream data locally. + ! ! Uses: use dshr_methods_mod , only : dshr_fldbun_getfldptr ! Arguments: @@ -193,7 +262,8 @@ subroutine GetPtr1D(this, fldname, dataptr1d) call endrun( 'Error getting field pointer for '//trim(fldname)//' from stream data', file=sourcefile, line=__LINE__ ) end if - end subroutine GetPtr1D + end subroutine GetPtr1D + !----------------------------------------------------------------------- end module CTSMForce2DStreamBaseType From d42e5ecca2995b2cf9504ce41ac8d005ca26822b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 20:27:44 -0700 Subject: [PATCH 087/112] Add the stream_ prefix to the model_alignt_year for atm_c13/c14 --- bld/namelist_files/namelist_definition_ctsm.xml | 4 ++-- .../clm/ciso_cmip7_monthly_2013Start/user_nl_clm | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index f88ab82625..7a032b901f 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1821,7 +1821,7 @@ First year to loop over for atmospheric C14 isotope delta data Last year to loop over for data atmospheric C14 isotope delta data - Simulation year that aligns with stream_year_first_atm_c14 value @@ -1841,7 +1841,7 @@ First year to loop over for atmospheric C13 isotope delta data Last year to loop over for data atmospheric C13 isotope delta data - Simulation year that aligns with stream_year_first_atm_c13 value diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm index 4dac396c9f..df0189c2e6 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm @@ -1,6 +1,6 @@ stream_fldfilename_atm_c13 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' stream_fldfilename_atm_c14 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' stream_year_first_atm_c14 = 2013 - model_year_align_atm_c14 = 2013 + stream_model_year_align_atm_c14 = 2013 stream_year_first_atm_c13 = 2013 - model_year_align_atm_c13 = 2013 + stream_model_year_align_atm_c13 = 2013 From 9d05b806f21a41dddc9f3be4964d72041a8a31f2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 20:32:54 -0700 Subject: [PATCH 088/112] Add code comments and description --- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 117 +++++++++++++----- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 index a943220740..0a49ac8061 100644 --- a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -1,4 +1,10 @@ module AtmCarbonIsotopeStreamType + ! + ! Description: + ! + ! This extends the stream base type to implement streams for atmospheric + ! Carbon isotope ratios that are read in from streams datasets (delta C13 and delta C14). + ! use shr_kind_mod , only : r8 => shr_kind_r8 use clm_varctl , only : iulog use abortutils , only : endrun @@ -8,6 +14,9 @@ module AtmCarbonIsotopeStreamType implicit none private + !----------------------------------------------------------------------- + ! Atmospheric Delta C13 Stream Type + !----------------------------------------------------------------------- character(len=*), parameter :: varname_c13 = 'delta13co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type private @@ -15,18 +24,21 @@ module AtmCarbonIsotopeStreamType contains ! Public Methods - procedure, public :: C13Init - procedure, public :: Init => C13Init - procedure, public :: C13Interp - procedure, public :: Interp => C13Interp - procedure, public :: C13ClassClean - procedure, public :: Clean => C13ClassClean - final :: C13TypeClean + procedure, public :: C13Init ! C13 initialization + procedure, public :: Init => C13Init ! Generic name for the initialization + procedure, public :: C13Interp ! C13 Interp method to fill the local data array + procedure, public :: Interp => C13Interp ! Generic name for the Interp method + procedure, public :: C13ClassClean ! C13 clean method as a class method + procedure, public :: Clean => C13ClassClean ! Generic name for the clean method + final :: C13TypeClean ! This clean method may be called by the compiler when the type goes out of scope ! Private methods - procedure, private :: C13InitAllocate + procedure, private :: C13InitAllocate ! Allocate the local C13 data end type atm_delta_c13_stream_type + !----------------------------------------------------------------------- + ! Atmospheric Delta C14 Stream Type + !----------------------------------------------------------------------- character(len=*), parameter :: varname_c14 = 'Delta14co2_in_air' type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type private @@ -34,28 +46,34 @@ module AtmCarbonIsotopeStreamType contains ! Public Methods - procedure, public :: C14Init - procedure, public :: Init => C14Init - procedure, public :: C14Interp - procedure, public :: Interp => C14Interp - procedure, public :: C14ClassClean - procedure, public :: Clean => C14ClassClean - final :: C14TypeClean + procedure, public :: C14Init ! C14 initialization + procedure, public :: Init => C14Init ! Generic name for the initialization + procedure, public :: C14Interp ! C14 Interp method to fill the local data array + procedure, public :: Interp => C14Interp ! Generic name for the Interp method + procedure, public :: C14ClassClean ! C14 clean method as a class method + procedure, public :: Clean => C14ClassClean ! Generic name for the clean method + final :: C14TypeClean ! This clean method may be called by the compiler when the type goes out of scope ! Private methods - procedure, private :: C14InitAllocate + procedure, private :: C14InitAllocate ! Allocate the local C14 data end type atm_delta_c14_stream_type character(len=*), parameter, private :: sourcefile = & __FILE__ + !----------------------------------------------------------------------- contains + !----------------------------------------------------------------------- + + !------------------------------------------------------------------------------------- subroutine C13Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) - ! Uses: + ! + ! Initialize the atmospheric delta C13 stream type + ! ! Arguments: - class(atm_delta_c13_stream_type), intent(inout) :: this + class(atm_delta_c13_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) @@ -71,11 +89,14 @@ subroutine C13Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxm year_first=year_first, year_last=year_last, model_year_align=model_year_align ) call this%C13InitAllocate( bounds ) - end subroutine C13Init + end subroutine C13Init + + !------------------------------------------------------------------------------------- subroutine C13InitAllocate( this, bounds ) + ! Allocate memory for the delta C13 data array use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) - class(atm_delta_c13_stream_type), intent(inout) :: this + class(atm_delta_c13_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds integer :: begg, endg @@ -84,23 +105,35 @@ subroutine C13InitAllocate( this, bounds ) allocate( this%atm_delta_c13( bounds%begg : bounds%endg ) ); this%atm_delta_c13 = nan end subroutine C13InitAllocate + !------------------------------------------------------------------------------------- + subroutine C13ClassClean( this ) - class(atm_delta_c13_stream_type), intent(inout) :: this + ! Clean up memory for the C13 stream type as a class method + class(atm_delta_c13_stream_type), intent(inout) :: this call C13TypeClean( this ) end subroutine C13ClassClean + !------------------------------------------------------------------------------------- + subroutine C13TypeClean( this ) - type(atm_delta_c13_stream_type), intent(inout) :: this + ! Clean up memory for the C13 stream type for this specific type + type(atm_delta_c13_stream_type), intent(inout) :: this deallocate( this%atm_delta_c13 ) call this%CleanBase() end subroutine C13TypeClean + !------------------------------------------------------------------------------------- + subroutine C13Interp( this, bounds ) - class(atm_delta_c13_stream_type), intent(inout) :: this + ! + ! Fill the local CTSM grid delta C13 array with data from the stream + ! + ! Arguments + class(atm_delta_c13_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds ! Local Variables @@ -114,13 +147,17 @@ subroutine C13Interp( this, bounds ) do g = bounds%begg, bounds%endg this%atm_delta_c13(g) = dataptr1d(g) end do - end subroutine C13Interp + end subroutine C13Interp + + !------------------------------------------------------------------------------------- - subroutine C14Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + subroutine C14Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & year_first, year_last, model_year_align ) - ! Uses: + ! + ! Initialize the atmospheric delta C14 stream type + ! ! Arguments: - class(atm_delta_c14_stream_type), intent(inout) :: this + class(atm_delta_c14_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) @@ -136,11 +173,15 @@ subroutine C14Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxm year_first=year_first, year_last=year_last, model_year_align=model_year_align ) call this%C14InitAllocate( bounds ) - end subroutine C14Init + end subroutine C14Init + + !------------------------------------------------------------------------------------- subroutine C14InitAllocate( this, bounds ) + ! Allocate memory for the delta C14 data array use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) - class(atm_delta_c14_stream_type), intent(inout) :: this + ! Arguments + class(atm_delta_c14_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds integer :: begg, endg @@ -149,8 +190,13 @@ subroutine C14InitAllocate( this, bounds ) allocate( this%atm_delta_c14( bounds%begg : bounds%endg ) ); this%atm_delta_c14 = nan end subroutine C14InitAllocate + !------------------------------------------------------------------------------------- + subroutine C14Interp( this, bounds ) - class(atm_delta_c14_stream_type), intent(inout) :: this + ! Fill the local CTSM grid delta C13 array with data from the stream + ! + ! Arguments: + class(atm_delta_c14_stream_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds ! Local Variables @@ -166,20 +212,27 @@ subroutine C14Interp( this, bounds ) end do end subroutine C14Interp + !------------------------------------------------------------------------------------- + subroutine C14ClassClean( this ) - class(atm_delta_c14_stream_type), intent(inout) :: this + ! Clean up memory for the C14 stream type as a class method + class(atm_delta_c14_stream_type), intent(inout) :: this call C14TypeClean( this ) end subroutine C14ClassClean + !------------------------------------------------------------------------------------- subroutine C14TypeClean( this ) - type(atm_delta_c14_stream_type), intent(inout) :: this + ! Clean up memory for the C14 stream type for this specific type + type(atm_delta_c14_stream_type), intent(inout) :: this deallocate( this%atm_delta_c14 ) call this%CleanBase() end subroutine C14TypeClean + !------------------------------------------------------------------------------------- + end module AtmCarbonIsotopeStreamType \ No newline at end of file From 87f44dcf9e287d05a9180d82eca00174f0bc6168 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 12 Nov 2025 21:08:22 -0700 Subject: [PATCH 089/112] Move the stream base stub to the location it should be under share_esmf --- src/unit_test_stubs/share_esmf/CMakeLists.txt | 1 + .../{utils => share_esmf}/CTSMForce2DStreamBaseType.F90 | 0 src/unit_test_stubs/utils/CMakeLists.txt | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename src/unit_test_stubs/{utils => share_esmf}/CTSMForce2DStreamBaseType.F90 (100%) diff --git a/src/unit_test_stubs/share_esmf/CMakeLists.txt b/src/unit_test_stubs/share_esmf/CMakeLists.txt index 368601dcc8..c68bb17a98 100644 --- a/src/unit_test_stubs/share_esmf/CMakeLists.txt +++ b/src/unit_test_stubs/share_esmf/CMakeLists.txt @@ -1,6 +1,7 @@ list(APPEND clm_sources ExcessIceStreamType.F90 FireDataBaseType.F90 + CTSMForce2DStreamBaseType.F90 laiStreamMod.F90 PrigentRoughnessStreamType.F90 ZenderSoilErodStreamType.F90 diff --git a/src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 b/src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 similarity index 100% rename from src/unit_test_stubs/utils/CTSMForce2DStreamBaseType.F90 rename to src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 diff --git a/src/unit_test_stubs/utils/CMakeLists.txt b/src/unit_test_stubs/utils/CMakeLists.txt index a61bffa9eb..4a8920af27 100644 --- a/src/unit_test_stubs/utils/CMakeLists.txt +++ b/src/unit_test_stubs/utils/CMakeLists.txt @@ -10,7 +10,6 @@ list(APPEND clm_sources "${clm_genf90_sources}") list(APPEND clm_sources restUtilMod_stub.F90 - CTSMForce2DStreamBaseType.F90 perfMod_stub.F90 spmdMod_stub.F90 clmfates_paraminterfaceMod_stub.F90 From 18982cca44f5010d20a011b1d1f982ca537b8c49 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 13 Nov 2025 14:57:02 -0700 Subject: [PATCH 090/112] Update ChangeLog/Sum --- doc/ChangeLog | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 64 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index a2131b269b..f540a5931a 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,67 @@ =============================================================== +Tag name: ctsm5.3.085 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Thu Nov 13 02:41:24 PM MST 2025 +One-line Summary: Merge b4b-dev to master + +Purpose and description of changes +---------------------------------- + + PRs + #3581 by Bill Sacks: Generalize some paths so unit testing works in a CESM checkout, previously assumed standalone CTSM checkout. + #3561 by Erik Kluzek: Start adding streams infrastructure for carbon isotopes. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Resolves #2837 + Resolves #3502 + Design notes in #3546 + Some work on #3346 + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: + No + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/3609 + +=============================================================== +=============================================================== Tag name: ctsm5.3.084 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) Date: Fri 31 Oct 2025 01:12:26 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index cf6a49e8fd..34fae7bc15 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.085 slevis 11/13/2025 Merge b4b-dev to master ctsm5.3.084 erik 10/31/2025 Merge b4b-dev to master ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases From 290c38df55d36f4691a3a7eb57bff4ae04e761eb Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Fri, 14 Nov 2025 11:11:50 -0700 Subject: [PATCH 091/112] Final ChangeLog/Sum --- doc/ChangeLog | 2 +- doc/ChangeSum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index f540a5931a..655b56af19 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.3.085 Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) -Date: Thu Nov 13 02:41:24 PM MST 2025 +Date: Fri Nov 14 11:08:54 AM MST 2025 One-line Summary: Merge b4b-dev to master Purpose and description of changes diff --git a/doc/ChangeSum b/doc/ChangeSum index 34fae7bc15..135ae3ac13 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.3.085 slevis 11/13/2025 Merge b4b-dev to master + ctsm5.3.085 slevis 11/14/2025 Merge b4b-dev to master ctsm5.3.084 erik 10/31/2025 Merge b4b-dev to master ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases From 1d8b770de71d919d7aa931999d01ba5a7f17f1a7 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 19 Nov 2025 12:09:01 -0700 Subject: [PATCH 092/112] Change namelist_defaults so lii2 tests pick up correct finidats --- bld/namelist_files/namelist_defaults_ctsm.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 46f3c3eeaf..4358ff0ec5 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1326,6 +1326,12 @@ attributes from the config_cache.xml file (with keys converted to upper-case). lnd_tuning_mode="clm5_0_GSWP3v1" use_init_interp=".true." >lnd/clm2/initdata_esmf/ctsm5.3/clmi.interp_from.I1850Clm50BgcCrop-ciso.1366-01-01.0.9x1.25_gx1v7_simyr1850_c240223.nc +lnd/clm2/initdata_esmf/ctsm5.4/clmi.f09_interp_from.ctsm5.4.CMIP7_ciso_ctsm5.3.075_f09_124_pSASU.clm2.r.0161_c251118.nc + Date: Wed, 19 Nov 2025 12:10:12 -0700 Subject: [PATCH 093/112] Remove VEGWP[LN,PD] from restarts for lii2 tests to pass --- src/biogeophys/CanopyStateType.F90 | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/biogeophys/CanopyStateType.F90 b/src/biogeophys/CanopyStateType.F90 index 81dc3947b1..744ba0d8f0 100644 --- a/src/biogeophys/CanopyStateType.F90 +++ b/src/biogeophys/CanopyStateType.F90 @@ -653,18 +653,6 @@ subroutine Restart(this, bounds, ncid, flag) scale_by_thickness=.false., & interpinic_flag='interp', readvar=readvar, data=this%vegwp_patch) - call restartvar(ncid=ncid, flag=flag, varname='VEGWPLN', xtype=ncd_double, & - dim1name='pft', dim2name='vegwcs', switchdim=.false., & - long_name='vegetation water matric potential for sun/sha canopy,xyl,root at local noon', units='mm', & - scale_by_thickness=.false., & - interpinic_flag='skip', readvar=readvar, data=this%vegwp_ln_patch) - - call restartvar(ncid=ncid, flag=flag, varname='VEGWPPD', xtype=ncd_double, & - dim1name='pft', dim2name='vegwcs', switchdim=.false., & - long_name='predawn vegetation water matric potential for sun/sha canopy,xyl,root', units='mm', & - scale_by_thickness=.false., & - interpinic_flag='skip', readvar=readvar, data=this%vegwp_pd_patch) - end if end subroutine Restart From aaf6f82bc8c8b7cb44138df83aff3e34f86fe9ee Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 20 Nov 2025 15:12:55 -0700 Subject: [PATCH 094/112] Put VEGWP[LN,PD] back on restart (= revert e684a9b) + rm from history --- src/biogeophys/CanopyStateType.F90 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/biogeophys/CanopyStateType.F90 b/src/biogeophys/CanopyStateType.F90 index 744ba0d8f0..66804e1549 100644 --- a/src/biogeophys/CanopyStateType.F90 +++ b/src/biogeophys/CanopyStateType.F90 @@ -296,11 +296,11 @@ subroutine InitHistory(this, bounds) this%vegwp_ln_patch(begp:endp,:) = spval call hist_addfld2d (fname='VEGWPLN', units='mm', type2d='nvegwcs', & avgflag='A', long_name='vegetation water matric potential for sun/sha canopy,xyl,root at local noon', & - ptr_patch=this%vegwp_ln_patch, default='active') + ptr_patch=this%vegwp_ln_patch, default='inactive') this%vegwp_pd_patch(begp:endp,:) = spval call hist_addfld2d (fname='VEGWPPD', units='mm', type2d='nvegwcs', avgflag='A', & long_name='predawn vegetation water matric potential for sun/sha canopy,xyl,root', & - ptr_patch=this%vegwp_pd_patch, default='active') + ptr_patch=this%vegwp_pd_patch, default='inactive') end if end subroutine InitHistory @@ -653,6 +653,18 @@ subroutine Restart(this, bounds, ncid, flag) scale_by_thickness=.false., & interpinic_flag='interp', readvar=readvar, data=this%vegwp_patch) + call restartvar(ncid=ncid, flag=flag, varname='VEGWPLN', xtype=ncd_double, & + dim1name='pft', dim2name='vegwcs', switchdim=.false., & + long_name='vegetation water matric potential for sun/sha canopy,xyl,root at local noon', units='mm', & + scale_by_thickness=.false., & + interpinic_flag='skip', readvar=readvar, data=this%vegwp_ln_patch) + + call restartvar(ncid=ncid, flag=flag, varname='VEGWPPD', xtype=ncd_double, & + dim1name='pft', dim2name='vegwcs', switchdim=.false., & + long_name='predawn vegetation water matric potential for sun/sha canopy,xyl,root', units='mm', & + scale_by_thickness=.false., & + interpinic_flag='skip', readvar=readvar, data=this%vegwp_pd_patch) + end if end subroutine Restart From 8db2e534fca32625e0e2cc9827f58ac9d1f784be Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Fri, 21 Nov 2025 13:08:01 -0700 Subject: [PATCH 095/112] Rm use_init_interp=.T. from files clmi.f09_interp... & clmi.f19_twice... --- bld/namelist_files/namelist_defaults_ctsm.xml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 4358ff0ec5..627f1d54d0 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1329,7 +1329,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). lnd/clm2/initdata_esmf/ctsm5.4/clmi.f09_interp_from.ctsm5.4.CMIP7_ciso_ctsm5.3.075_f09_124_pSASU.clm2.r.0161_c251118.nc @@ -1436,13 +1436,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). lnd/clm2/initdata_esmf/ctsm5.4/clmi.f19_twiceinterp_from.I1850Clm50BgcCrop-ciso.1366-01-01.0.9x1.25_gx1v7_simyr1850_c251030.nc - -lnd/clm2/initdata_esmf/ctsm5.4/clmi.f19_twiceinterp_from.I1850Clm50BgcCrop-ciso.1366-01-01.0.9x1.25_gx1v7_simyr1850_c251030.nc From a5136fc7d35ff34d9f1fb1e799c01dcb06aa86af Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Fri, 21 Nov 2025 17:24:00 -0700 Subject: [PATCH 096/112] File path and name updated to ctsm5.4 values for unit test to PASS --- python/ctsm/test/test_unit_subset_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ctsm/test/test_unit_subset_data.py b/python/ctsm/test/test_unit_subset_data.py index a127a282e0..81b4fb3281 100755 --- a/python/ctsm/test/test_unit_subset_data.py +++ b/python/ctsm/test/test_unit_subset_data.py @@ -107,7 +107,7 @@ def test_inputdata_setup_files_basic(self): files = setup_files(self.args, self.defaults, self.cesmroot, testing=True) self.assertEqual( files["fsurf_in"], - "surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc", + "surfdata_0.9x1.25_hist_2000_16pfts_c251022.nc", "fsurf_in filename not whats expected", ) self.assertEqual( @@ -117,7 +117,7 @@ def test_inputdata_setup_files_basic(self): ) self.assertEqual( files["main_dir"], - "/glade/campaign/cesm/cesmdata/cseg/inputdata", + "/glade/campaign/cesm/cesmdata/inputdata", "main_dir directory not whats expected", ) From f4f35f94599871561d819591c2b6ba15b30331ec Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 24 Nov 2025 10:02:06 -0700 Subject: [PATCH 097/112] Update to ctsm5.3.085 --- .github/workflows/docs-build-and-deploy.yml | 4 + .gitmodules | 4 +- bld/CLMBuildNamelist.pm | 75 +- .../namelist_definition_ctsm.xml | 45 + bld/unit_testers/build-namelist_test.pl | 26 +- cime_config/testdefs/ExpectedTestFails.xml | 9 + cime_config/testdefs/testlist_clm.xml | 20 + .../include_user_mods | 1 + .../ciso_cmip7_monthly_2013Start/user_nl_clm | 6 + .../ciso_monthly_2013Start/include_user_mods | 1 + .../clm/ciso_monthly_2013Start/shell_commands | 1 + .../clm/ciso_monthly_2013Start/user_nl_clm | 3 + doc/ChangeLog | 223 +++++ doc/ChangeSum | 3 + .../using-clm-tools/paramfile-tools.md | 7 +- python/ctsm/param_utils/paramfile_shared.py | 4 +- python/ctsm/param_utils/set_paramfile.py | 11 +- python/ctsm/test/test_sys_set_paramfile.py | 96 ++- .../ctsm/test/test_unit_paramfile_shared.py | 57 ++ python/ctsm/test/test_unit_set_paramfile.py | 8 +- share | 2 +- src/CMakeLists.txt | 27 +- src/biogeochem/AtmCarbonIsotopeStreamType.F90 | 238 ++++++ src/biogeochem/CMakeLists.txt | 1 + src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 | 801 +++++++++++++++--- .../CIsoAtmTimeSeries_test/CMakeLists.txt | 11 + .../test_CIsoAtmTimeSeries.pf | 277 ++++++ src/biogeochem/test/CMakeLists.txt | 1 + src/biogeophys/CanopyFluxesMod.F90 | 12 +- src/biogeophys/PhotosynthesisMod.F90 | 57 +- .../share_esmf/CTSMForce2DStreamBaseType.F90 | 269 ++++++ src/cpl/share_esmf/FireDataBaseType.F90 | 2 + .../share_esmf/PrigentRoughnessStreamType.F90 | 2 + src/cpl/share_esmf/cropcalStreamMod.F90 | 1 + src/cpl/share_esmf/ndepStreamMod.F90 | 4 + src/fates | 2 +- src/main/clm_initializeMod.F90 | 10 +- src/main/clm_varcon.F90 | 10 +- src/main/clm_varctl.F90 | 18 +- .../SoilBiogeochemCompetitionMod.F90 | 12 +- src/unit_test_stubs/share_esmf/CMakeLists.txt | 1 + .../share_esmf/CTSMForce2DStreamBaseType.F90 | 120 +++ src/utils/clmfates_interfaceMod.F90 | 32 +- src/utils/fileutils.F90 | 11 +- 44 files changed, 2245 insertions(+), 280 deletions(-) create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands create mode 100644 cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm create mode 100755 python/ctsm/test/test_unit_paramfile_shared.py create mode 100644 src/biogeochem/AtmCarbonIsotopeStreamType.F90 create mode 100644 src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt create mode 100644 src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf create mode 100644 src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 create mode 100644 src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 diff --git a/.github/workflows/docs-build-and-deploy.yml b/.github/workflows/docs-build-and-deploy.yml index a802a0743a..55ad033ed7 100644 --- a/.github/workflows/docs-build-and-deploy.yml +++ b/.github/workflows/docs-build-and-deploy.yml @@ -33,6 +33,10 @@ concurrency: jobs: build-and-deploy: + + # Only run on upstream repository + if: ${{ github.repository == 'ESCOMP/CTSM' }} + environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.gitmodules b/.gitmodules index f0e6ed2ef6..7d566e63ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,7 @@ [submodule "fates"] path = src/fates url = https://github.com/NGEET/fates -fxtag = sci.1.87.2_api.41.0.0 +fxtag = sci.1.88.0_api.42.0.0 fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/fates @@ -100,7 +100,7 @@ fxDONOTUSEurl = https://github.com/ESCOMP/CDEPS.git [submodule "share"] path = share url = https://github.com/ESCOMP/CESM_share -fxtag = share1.1.9 +fxtag = share1.1.15 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/CESM_share diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index d83eaf088d..2dea2fcdb2 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3770,32 +3770,58 @@ sub setup_logic_c_isotope { $log->warning("use_c14 is ONLY scientifically validated with the bgc=BGC configuration" ); } } + my $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); + my $stream_fldfilename_atm_c14 = $nl->get_value('stream_fldfilename_atm_c14'); + my $atm_c14_filename = $nl->get_value('atm_c14_filename'); if ( &value_is_true($use_c14) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c14_bombspike', 'use_c14'=>$use_c14 ); - my $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); + $use_c14_bombspike = $nl->get_value('use_c14_bombspike'); if ( &value_is_true($use_c14_bombspike) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c14_filename', - 'use_c14'=>$use_c14, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c14_bombspike'=>$nl->get_value('use_c14_bombspike'), - 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + if ( defined($stream_fldfilename_atm_c14) ) { + setup_logic_c14_streams($opts, $nl_flags, $definition, $defaults, $nl); + } else { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c14_filename', + 'use_c14'=>$use_c14, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c14_bombspike'=>$nl->get_value('use_c14_bombspike'), + 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + } + $stream_fldfilename_atm_c14 = $nl->get_value('stream_fldfilename_atm_c14'); + $atm_c14_filename = $nl->get_value('atm_c14_filename'); + if ( defined($stream_fldfilename_atm_c14) && defined($atm_c14_filename) ) { + $log->fatal_error("Both stream_fldfilename_atm_c14 and atm_c14_filename set, only one should be set"); + } } } else { - if ( defined($nl->get_value('use_c14_bombspike')) || - defined($nl->get_value('atm_c14_filename')) ) { - $log->fatal_error("use_c14 is FALSE and use_c14_bombspike or atm_c14_filename set"); + if ( defined($use_c14_bombspike) || + defined($stream_fldfilename_atm_c14) || + defined($atm_c14_filename) ) { + $log->fatal_error("use_c14 is FALSE and use_c14_bombspike, stream_fldfilename_atm_c14 or atm_c14_filename set"); } } + my $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); + my $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); + my $atm_c13_filename = $nl->get_value('atm_c13_filename'); if ( &value_is_true($use_c13) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_c13_timeseries', 'use_c13'=>$use_c13 ); - my $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); + $use_c13_timeseries = $nl->get_value('use_c13_timeseries'); if ( &value_is_true($use_c13_timeseries) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c13_filename', - 'use_c13'=>$use_c13, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c13_timeseries'=>$nl->get_value('use_c13_timeseries'), - 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + if ( defined($stream_fldfilename_atm_c13) ) { + setup_logic_c13_streams($opts, $nl_flags, $definition, $defaults, $nl); + } else { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'atm_c13_filename', + 'use_c13'=>$use_c13, 'use_cn'=>$nl_flags->{'use_cn'}, 'use_c13_timeseries'=>$nl->get_value('use_c13_timeseries'), + 'ssp_rcp'=>$nl_flags->{'ssp_rcp'} ); + } + $stream_fldfilename_atm_c13 = $nl->get_value('stream_fldfilename_atm_c13'); + $atm_c13_filename = $nl->get_value('atm_c13_filename'); + if ( defined($stream_fldfilename_atm_c13) && defined($atm_c13_filename) ) { + $log->fatal_error("Both stream_fldfilename_atm_c13 and atm_c13_filename set, only one should be set"); + } } } else { if ( defined($nl->get_value('use_c13_timeseries')) || + defined($nl->get_value('stream_fldfilename_atm_c13')) || defined($nl->get_value('atm_c13_filename')) ) { - $log->fatal_error("use_c13 is FALSE and use_c13_timeseries or atm_c13_filename set"); + $log->fatal_error("use_c13 is FALSE and use_c13_timeseries, stream_fldfilename_atm_c13 or atm_c13_filename set"); } } } else { @@ -3803,8 +3829,10 @@ sub setup_logic_c_isotope { &value_is_true($use_c14) || &value_is_true($nl->get_value('use_c14_bombspike')) || defined($nl->get_value('atm_c14_filename')) || + defined($nl->get_value('stream_fldfilename_atm_c14')) || &value_is_true($nl->get_value('use_c13_timeseries')) || - defined($nl->get_value('atm_c13_filename')) ) { + defined($nl->get_value('atm_c13_filename')) || + defined($nl->get_value('stream_fldfilename_atm_c13')) ) { $log->fatal_error("bgc=sp and C isotope namelist variables were set, both can't be used at the same time"); } } @@ -3812,6 +3840,24 @@ sub setup_logic_c_isotope { #------------------------------------------------------------------------------- +sub setup_logic_c13_streams { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + # + # C13 stream file settings + # +} + +#------------------------------------------------------------------------------- + +sub setup_logic_c14_streams { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + # + # C14 stream file settings + # +} + +#------------------------------------------------------------------------------- + sub setup_logic_nitrogen_deposition { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; @@ -5334,6 +5380,9 @@ sub write_output_files { push @groups, "clm_canopy_inparm"; push @groups, "prigentroughness"; push @groups, "zendersoilerod"; + if ( &value_is_true($nl_flags->{'use_cn'}) ) { + push @groups, "carbon_isotope_streams"; + } if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 69a243bd27..7a032b901f 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1806,6 +1806,51 @@ Mapping method from Nitrogen deposition input file to the model resolution none = no interpolation + + + +First year to loop over for atmospheric C14 isotope delta data + + + +Last year to loop over for data atmospheric C14 isotope delta data + + + +Simulation year that aligns with stream_year_first_atm_c14 value + + + +Filename of input stream data for atmospheric C14 isotope delta data + + + +First year to loop over for atmospheric C13 isotope delta data + + + +Last year to loop over for data atmospheric C13 isotope delta data + + + +Simulation year that aligns with stream_year_first_atm_c13 value + + + +Filename of input stream data for atmospheric C13 isotope delta data + + diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 725a5defff..2795ef6f76 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3396; +my $ntests = 3402; if ( defined($opts{'compare'}) ) { $ntests += 2061; @@ -821,6 +821,30 @@ sub cat_and_create_namelistinfile { namelst=>"use_c14_bombspike=.true.", phys=>"clm5_0", }, + "bombspike file and stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c14=TRUE use_c14_bombspike=.true. stream_fldfilename_atm_c14='/dev/null', atm_c14_filename='/dev/null'", + phys=>"clm6_0", + }, + "c13 file and stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c13_timeseries=.true. stream_fldfilename_atm_c13='/dev/null', atm_c13_filename='/dev/null'", + phys=>"clm6_0", + }, + "c13 off, but stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c13=.false. stream_fldfilename_atm_c13='/dev/null'", + phys=>"clm6_0", + }, + "c14 off, but stream" =>{ options=>"-bgc bgc -envxml_dir .", + namelst=>"use_c14=.false. stream_fldfilename_atm_c14='/dev/null'", + phys=>"clm6_0", + }, + "sp, but c13 stream" =>{ options=>"-bgc sp -envxml_dir .", + namelst=>"stream_fldfilename_atm_c13='/dev/null'", + phys=>"clm6_0", + }, + "sp, but c14 stream" =>{ options=>"-bgc sp -envxml_dir .", + namelst=>"stream_fldfilename_atm_c14='/dev/null'", + phys=>"clm6_0", + }, "lightres no cn" =>{ options=>"-bgc sp -envxml_dir . -light_res 360x720", namelst=>"", phys=>"clm5_0", diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 5d4d51e123..d21fc39c42 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -247,6 +247,15 @@ + + FAIL diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 913580c77b..15e395fe6d 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -195,6 +195,25 @@ + + + + + + + + + + + + + + + + + + + @@ -4445,6 +4464,7 @@ + diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods new file mode 100644 index 0000000000..5f7ca15ec0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/include_user_mods @@ -0,0 +1 @@ +../ciso_monthly_2013Start diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm new file mode 100644 index 0000000000..df0189c2e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cmip7_monthly_2013Start/user_nl_clm @@ -0,0 +1,6 @@ + stream_fldfilename_atm_c13 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C13_CMIP7_global_1700-2023_yearly_v3.0_c251013.nc' + stream_fldfilename_atm_c14 = '$DIN_LOC_ROOT/lnd/clm2/isotopes/ctsmforc.Graven.atm_delta_C14_CMIP7_4x1_global_1700-2023_yearly_v3.0_c251013.nc' + stream_year_first_atm_c14 = 2013 + stream_model_year_align_atm_c14 = 2013 + stream_year_first_atm_c13 = 2013 + stream_model_year_align_atm_c13 = 2013 diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods new file mode 100644 index 0000000000..2cc5720115 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/include_user_mods @@ -0,0 +1 @@ +../ciso_monthly diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands new file mode 100644 index 0000000000..035842f982 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/shell_commands @@ -0,0 +1 @@ +./xmlchange RUN_STARTDATE=2013-01-01 diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm new file mode 100644 index 0000000000..b0129f7f6e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_monthly_2013Start/user_nl_clm @@ -0,0 +1,3 @@ +! Add C13/C14 output to validate the incoming data +hist_fincl1 += 'RC13_CANAIR', 'RC14_CANAIR' +hist_fincl2 = 'RC13_CANAIR', 'RC14_CANAIR' diff --git a/doc/ChangeLog b/doc/ChangeLog index 551de3ae12..655b56af19 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,227 @@ =============================================================== +Tag name: ctsm5.3.085 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Fri Nov 14 11:08:54 AM MST 2025 +One-line Summary: Merge b4b-dev to master + +Purpose and description of changes +---------------------------------- + + PRs + #3581 by Bill Sacks: Generalize some paths so unit testing works in a CESM checkout, previously assumed standalone CTSM checkout. + #3561 by Erik Kluzek: Start adding streams infrastructure for carbon isotopes. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Resolves #2837 + Resolves #3502 + Design notes in #3546 + Some work on #3346 + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: + No + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/3609 + +=============================================================== +=============================================================== +Tag name: ctsm5.3.084 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Fri 31 Oct 2025 01:12:26 PM MDT +One-line Summary: Merge b4b-dev to master + +Purpose and description of changes +---------------------------------- + +Bring changes on b4b-dev to master + +- Fix FUNIT testing on Mac's +- Some fixes to set_paramfile +- Don't auto build documentation on personal forks, only on ESCOMP + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Fixes #3571 -- set_paramfile ordering of PFT's from user doesn't have to match paramfile order + Fixes #3559 -- Correct Ndims error + Fixes #3369 -- Docs build and deploy only runs on ESCOMP not personal forks + +Notes of particular relevance for users +--------------------------------------- + +Changes to documentation: + Some changes to set_paramfile documentation + +Notes of particular relevance for developers: +--------------------------------------------- + +Testing summary: regular +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - OK + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: No bit-for-bit + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + -- #3577 unit testing on Mac + -- #3572 set_paramfile ordering + -- #3560 Fix Ndim error in set_paramfile + -- #3557 doc/build/run/deploy only on ESCOMP + +=============================================================== +=============================================================== +Tag name: ctsm5.3.083 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) +Date: Wed 29 Oct 2025 03:35:50 PM MDT +One-line Summary: Changes to coupling of supplementation status with FATES. + +Purpose and description of changes +---------------------------------- + +Supplementation status is now passed to FATES as a run-time boundary condition. This set of changes is synchronized with FATES-side changes that were oriented around how supplementation status impacts whether or not fine-root proportions are allowed to change during CNP runs. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? No + +Bugs fixed +---------- + +No bugs were fixed. + +Notes of particular relevance for users +--------------------------------------- + +When CTSM-FATES has nutrient coupling, note that FATES plants will default to not changing L2FR when supplementing nitrogen. + +Caveats for users (e.g., need to interpolate initial conditions): None + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): None + +Changes made to namelist defaults (e.g., changed parameter values): None + +Changes to the datasets (e.g., parameter, surface or initial files): None + +Changes to documentation: None necessary. + +Substantial timing or memory changes: None + +Notes of particular relevance for developers: +--------------------------------------------- + +We had been passing a stealth namelist variable from CTSM to FATES, it gave FATES information about what types of mineralized nutrient were available (ie NO3 or NH4), but FATES has never used this information, so it was removed. + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): None + +Changes to tests or testing: None + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: No answer changes with one exception. Some FATES-specific variables related to radiation diagnostics and error tracking changed with updating the FATES tag. These were diagnostic only changes, and not related to FATES internals changing in any way. + +ther details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/CTSM/pull/3348 + + +=============================================================== +=============================================================== Tag name: ctsm5.3.082 Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) Date: Fri 24 Oct 2025 11:17:41 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index e19584ae9d..135ae3ac13 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,8 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.085 slevis 11/14/2025 Merge b4b-dev to master + ctsm5.3.084 erik 10/31/2025 Merge b4b-dev to master + ctsm5.3.083 rgknox 10/29/2025 Changes to coupling of supplementation status with FATES. ctsm5.3.082 slevis 10/24/2025 Update to CMIP7 population density file for non-SSP cases ctsm5.3.081 erik 10/22/2025 Change defaults for when Carbon isotopes are turned on, and turn on irrigate for Sp/Bgc cases for clm6_0 historical transient cases ctsm5.3.080 samrabin 10/16/2025 Merge b4b-dev to master diff --git a/doc/source/users_guide/using-clm-tools/paramfile-tools.md b/doc/source/users_guide/using-clm-tools/paramfile-tools.md index 71d881e362..aaee301bcb 100644 --- a/doc/source/users_guide/using-clm-tools/paramfile-tools.md +++ b/doc/source/users_guide/using-clm-tools/paramfile-tools.md @@ -57,9 +57,14 @@ Change a one-dimensional parameter (`mimics_fmet` has the `segment` dimension, l tools/param_utils/set_paramfile -i paramfile.nc -o output.nc mimics_fmet=0.1,0.2,0.3,0.4 ``` +Change a one-dimensional parameter to be all one value (`mxmat` has the `pft` dimension, length 79): +```bash +tools/param_utils/set_paramfile -i paramfile.nc -o output.nc mxmat=360 +``` + Change a parameter for specific PFTs: ```bash -tools/param_utils/set_paramfile -i paramfile.nc -o output.nc -p needleleaf_evergreen_temperate_tree,c4_grass medlynintercept=99.9,100.1 medlynslope=2.99,1.99 +tools/param_utils/set_paramfile -i paramfile.nc -o output.nc -p needleleaf_evergreen_temperate_tree,c4_grass medlynintercept=99.9,100.1 medlynslope=2.99,1.99 mxmat=199 ``` Set a parameter to the fill value: diff --git a/python/ctsm/param_utils/paramfile_shared.py b/python/ctsm/param_utils/paramfile_shared.py index b9860be0db..d12773dbdb 100644 --- a/python/ctsm/param_utils/paramfile_shared.py +++ b/python/ctsm/param_utils/paramfile_shared.py @@ -85,7 +85,9 @@ def get_selected_pft_indices(selected_pfts, pft_names): list of int Indices of selected PFTs. """ - indices = [i for i, name in enumerate(pft_names) if name in selected_pfts] + if isinstance(selected_pfts, str): + selected_pfts = [selected_pfts] + indices = [pft_names.index(pft) for pft in selected_pfts] return indices diff --git a/python/ctsm/param_utils/set_paramfile.py b/python/ctsm/param_utils/set_paramfile.py index 2ceb52069b..6efa491846 100644 --- a/python/ctsm/param_utils/set_paramfile.py +++ b/python/ctsm/param_utils/set_paramfile.py @@ -130,7 +130,7 @@ def check_correct_ndims(da, new_value, throw_error=False): """ expected = da.ndim actual = np.array(new_value).ndim - is_ndim_correct = expected == actual + is_ndim_correct = actual in (0, expected) # If actual 0, apply it to all if throw_error and not is_ndim_correct: raise RuntimeError(f"Incorrect N dims: Expected {expected}, got {actual}") return is_ndim_correct @@ -323,11 +323,12 @@ def apply_new_value_to_parameter(args, ds_out, var, new_value, var_encoding, *, # Ensure that any NaNs are replaced with the fill value new_value = _replace_nans_with_fill(var_encoding, new_value, chg=chg) - # This can be needed if, e.g., you're selecting and changing just one PFT + # This can be needed if, (a) you're selecting and changing just one PFT or (b) you're changing + # all values in a dimensioned parameter to match one value. if ds_out[var].values.ndim > 0 and new_value.ndim == 0: - new_value = np.atleast_1d(new_value) - - ds_out[var].values = new_value + ds_out[var].values[:] = new_value + else: + ds_out[var].values = new_value return ds_out diff --git a/python/ctsm/test/test_sys_set_paramfile.py b/python/ctsm/test/test_sys_set_paramfile.py index 7d82a32302..f65c8e0d50 100755 --- a/python/ctsm/test/test_sys_set_paramfile.py +++ b/python/ctsm/test/test_sys_set_paramfile.py @@ -121,8 +121,11 @@ def test_set_paramfile_changeparams_scalar_errors_given_list(self): with self.assertRaisesRegex(RuntimeError, "Incorrect N dims"): sp.main() - def test_set_paramfile_changeparam_1d_errors_given_scalar(self): - """Test that set_paramfile errors if given a scalar for a 1-d parameter""" + def test_set_paramfile_changeparam_1d_given_scalar(self): + """ + Test that set_paramfile works correctly if given a scalar for a 1-d parameter. We want it + to set all members of the 1d array to the given scalar. + """ output_path = os.path.join(self.tempdir, "output.nc") sys.argv = [ "set_paramfile", @@ -130,10 +133,53 @@ def test_set_paramfile_changeparam_1d_errors_given_scalar(self): PARAMFILE, "-o", output_path, - "xl=0.724", + "mxmat=1987", ] - with self.assertRaisesRegex(RuntimeError, "Incorrect N dims"): - sp.main() + sp.main() + self.assertTrue(os.path.exists(output_path)) + ds_in = open_paramfile(PARAMFILE) + ds_out = open_paramfile(output_path) + + for var in ds_in.variables: + # Check that all variables/coords are equal except the ones we changed, which should be + # set to what we asked + if var == "mxmat": + self.assertTrue(np.all(ds_out[var].values == 1987)) + else: + self.assertTrue(are_paramfile_dataarrays_identical(ds_in[var], ds_out[var])) + + def test_set_paramfile_changeparam_1d_given_scalar_and_pftlist(self): + """ + Test that set_paramfile works correctly if given a scalar for a 1-d parameter. We want it + to set all members of the 1d array to the given scalar. As + test_set_paramfile_changeparam_1d_given_scalar, but here we give a pft list. + """ + output_path = os.path.join(self.tempdir, "output.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output_path, + "-p", + "temperate_corn,irrigated_temperate_corn", + "mxmat=1987", + ] + sp.main() + self.assertTrue(os.path.exists(output_path)) + ds_in = open_paramfile(PARAMFILE) + ds_out = open_paramfile(output_path) + + for var in ds_in.variables: + # Check that all variables/coords are equal except the ones we changed, which should be + # set to what we asked + if var == "mxmat": + # First, check that they weren't 1987 before + self.assertFalse(np.any(ds_in[var].values[17:18] == 1987)) + # Now check that they are 1987 + self.assertTrue(np.all(ds_out[var].values[17:18] == 1987)) + else: + self.assertTrue(are_paramfile_dataarrays_identical(ds_in[var], ds_out[var])) def test_set_paramfile_changeparams_scalar_double(self): """Test that set_paramfile can copy to a new file with some scalar double params changed""" @@ -732,6 +778,46 @@ def test_set_paramfile_double_ok_given_int(self): ds_out = open_paramfile(output_path) self.assertFalse(sp.is_integer(ds_out[param_name].values)) + def test_set_paramfile_pft_order(self): + """ + Test that set_paramfile gives the same result regardless of the order you specify the PFTs + """ + + # First order + pfts_to_include = ["rice", "irrigated_rice"] + output0_path = os.path.join(self.tempdir, "output0.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output0_path, + "-p", + ",".join(pfts_to_include), + "mxmat=100,200", + ] + sp.main() + + # Reverse order + pfts_to_include.reverse() + output1_path = os.path.join(self.tempdir, "output1.nc") + sys.argv = [ + "set_paramfile", + "-i", + PARAMFILE, + "-o", + output1_path, + "-p", + ",".join(pfts_to_include), + "mxmat=200,100", + ] + sp.main() + + # These files should be identical + ds0 = open_paramfile(output0_path) + ds1 = open_paramfile(output1_path) + self.assertTrue(ds0.equals(ds1)) + if __name__ == "__main__": unit_testing.setup_for_tests() diff --git a/python/ctsm/test/test_unit_paramfile_shared.py b/python/ctsm/test/test_unit_paramfile_shared.py new file mode 100755 index 0000000000..e0adebc7c2 --- /dev/null +++ b/python/ctsm/test/test_unit_paramfile_shared.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +"""Unit tests for paramfile_shared""" + +import unittest + +from ctsm import unit_testing + +from ctsm.param_utils import paramfile_shared as ps + +# Allow names that pylint doesn't like, because otherwise I find it hard +# to make readable unit test names +# pylint: disable=invalid-name + + +class TestUnitGetSelectedPftIndices(unittest.TestCase): + """Unit tests of get_selected_pft_indices""" + + def test_get_selected_pft_indices_1strselected_onlyinlist(self): + """Check get_selected_pft_indices() given the only one in the list, as a string""" + selected_pfts = "rice" + pft_names = ["rice"] + result = ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + self.assertListEqual(result, [0]) + + def test_get_selected_pft_indices_1selected_onlyinlist(self): + """Check get_selected_pft_indices() given the only one in the list, as a list""" + selected_pfts = ["rice"] + pft_names = ["rice"] + result = ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + self.assertListEqual(result, [0]) + + def test_get_selected_pft_indices_2selected_sameorder(self): + """Check get_selected_pft_indices() given 2 selected in the same order as the list""" + pft_names = ["rice", "irrigated_rice"] + result = ps.get_selected_pft_indices(selected_pfts=pft_names, pft_names=pft_names) + self.assertListEqual(result, [0, 1]) + + def test_get_selected_pft_indices_2selected_difforder(self): + """Check get_selected_pft_indices() given 2 selected NOT in the same order as the list""" + pft_names = ["rice", "irrigated_rice"] + result = ps.get_selected_pft_indices( + selected_pfts=list(reversed(pft_names)), pft_names=pft_names + ) + self.assertListEqual(result, [1, 0]) + + def test_get_selected_pft_indices_missing_valueerror(self): + """Check get_selected_pft_indices() given selected pft NOT in the list""" + selected_pfts = ["wheat"] + pft_names = ["rice", "irrigated_rice"] + with self.assertRaises(ValueError): + ps.get_selected_pft_indices(selected_pfts=selected_pfts, pft_names=pft_names) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_set_paramfile.py b/python/ctsm/test/test_unit_set_paramfile.py index 6b091f8967..13be015c67 100755 --- a/python/ctsm/test/test_unit_set_paramfile.py +++ b/python/ctsm/test/test_unit_set_paramfile.py @@ -65,14 +65,14 @@ def test_checkcorrectndims_0d_int_np(self): self.assertTrue(sp.check_correct_ndims(da, np.int32(1))) def test_checkcorrectndims_1d_int(self): - """Check False when given a standard int for a 0d parameter""" + """Check True when given a standard int for a 0d parameter""" da = xr.DataArray(data=[1, 2]) - self.assertFalse(sp.check_correct_ndims(da, 1)) + self.assertTrue(sp.check_correct_ndims(da, 1)) def test_checkcorrectndims_1d_int_np(self): - """Check False when given a numpy int for a 0d parameter""" + """Check True when given a numpy int for a 0d parameter""" da = xr.DataArray(data=[1, 2]) - self.assertFalse(sp.check_correct_ndims(da, np.int32(1))) + self.assertTrue(sp.check_correct_ndims(da, np.int32(1))) def test_checkcorrectndims_0d_list(self): """Check False when given a list for a 0d parameter""" diff --git a/share b/share index 14338bef3f..ec0475f05b 160000 --- a/share +++ b/share @@ -1 +1 @@ -Subproject commit 14338bef3fa604d49160e376257264db1d3313e5 +Subproject commit ec0475f05bde376bab73c74d09450549ec746d86 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2682775ca5..bc925b7dd1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,17 +1,18 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY}) include(CIME_initial_setup) -#list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../tools/mksurfdata_esmf/cmake") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../share/cmake") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../components/cmeps/cmake") +set(SRCROOT "${CIMEROOT}/..") +set(CLM_ROOT "..") + +list(APPEND CMAKE_MODULE_PATH "${SRCROOT}/share/cmake") +list(APPEND CMAKE_MODULE_PATH "${SRCROOT}/components/cmeps/cmake") project(clm_tests Fortran C) include(CIME_utils) -set(CLM_ROOT "..") # find needed external packages # NetCDF is required -- because PIO and NetCDF are required by the standard default ESMF libraries @@ -38,15 +39,15 @@ link_libraries(${ESMF_INTERFACE_LINK_LIBRARIES}) # done first, so that in case of name collisions, the CLM versions take # precedence (when there are two files with the same name, the one added later # wins). -add_subdirectory(${CLM_ROOT}/share/src csm_share) -add_subdirectory(${CLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) +add_subdirectory(${SRCROOT}/share/src csm_share) +add_subdirectory(${SRCROOT}/share/unit_test_stubs/util csm_share_stubs) # Add files needed from CMEPS list ( APPEND drv_sources_needed - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_expr_parser_mod.F90 - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_fire_emis_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_expr_parser_mod.F90 + ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_fire_emis_mod.F90 ) # Add CLM source directories @@ -93,10 +94,10 @@ add_library(csm_share ${share_sources} ${drv_sources_needed}) declare_generated_dependencies(csm_share "${share_genf90_sources}") add_library(clm ${clm_sources}) declare_generated_dependencies(clm "${clm_genf90_sources}") -add_dependencies(clm csm_share esmf) +add_dependencies(clm csm_share ESMF) # We need to look for header files here, in order to pick up shr_assert.h -include_directories(${CLM_ROOT}/share/include) +include_directories(${SRCROOT}/share/include) # Tell cmake to look for libraries & mod files here, because this is where we built libraries include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/biogeochem/AtmCarbonIsotopeStreamType.F90 b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 new file mode 100644 index 0000000000..0a49ac8061 --- /dev/null +++ b/src/biogeochem/AtmCarbonIsotopeStreamType.F90 @@ -0,0 +1,238 @@ +module AtmCarbonIsotopeStreamType + ! + ! Description: + ! + ! This extends the stream base type to implement streams for atmospheric + ! Carbon isotope ratios that are read in from streams datasets (delta C13 and delta C14). + ! + use shr_kind_mod , only : r8 => shr_kind_r8 + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + use CTSMForce2DStreamBaseType, only : ctsm_force_2DStream_base_type + + implicit none + private + + !----------------------------------------------------------------------- + ! Atmospheric Delta C13 Stream Type + !----------------------------------------------------------------------- + character(len=*), parameter :: varname_c13 = 'delta13co2_in_air' + type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c13_stream_type + private + real(r8), public, allocatable :: atm_delta_c13(:) ! delta C13 data array + contains + + ! Public Methods + procedure, public :: C13Init ! C13 initialization + procedure, public :: Init => C13Init ! Generic name for the initialization + procedure, public :: C13Interp ! C13 Interp method to fill the local data array + procedure, public :: Interp => C13Interp ! Generic name for the Interp method + procedure, public :: C13ClassClean ! C13 clean method as a class method + procedure, public :: Clean => C13ClassClean ! Generic name for the clean method + final :: C13TypeClean ! This clean method may be called by the compiler when the type goes out of scope + ! Private methods + procedure, private :: C13InitAllocate ! Allocate the local C13 data + + end type atm_delta_c13_stream_type + + !----------------------------------------------------------------------- + ! Atmospheric Delta C14 Stream Type + !----------------------------------------------------------------------- + character(len=*), parameter :: varname_c14 = 'Delta14co2_in_air' + type, public, extends(ctsm_force_2DStream_base_type) :: atm_delta_c14_stream_type + private + real(r8), public, allocatable :: atm_delta_c14(:) ! delta c14 data array + contains + + ! Public Methods + procedure, public :: C14Init ! C14 initialization + procedure, public :: Init => C14Init ! Generic name for the initialization + procedure, public :: C14Interp ! C14 Interp method to fill the local data array + procedure, public :: Interp => C14Interp ! Generic name for the Interp method + procedure, public :: C14ClassClean ! C14 clean method as a class method + procedure, public :: Clean => C14ClassClean ! Generic name for the clean method + final :: C14TypeClean ! This clean method may be called by the compiler when the type goes out of scope + ! Private methods + procedure, private :: C14InitAllocate ! Allocate the local C14 data + + end type atm_delta_c14_stream_type + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + + !----------------------------------------------------------------------- + contains + !----------------------------------------------------------------------- + + !------------------------------------------------------------------------------------- + + subroutine C13Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + year_first, year_last, model_year_align ) + ! + ! Initialize the atmospheric delta C13 stream type + ! + ! Arguments: + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + call this%InitBase( bounds, varnames = (/ varname_c13 /), fldfilename=fldfilename, meshfile=meshfile, & + mapalgo=mapalgo, tintalgo=tintalgo, taxmode=taxmode, name=varname_c13, & + year_first=year_first, year_last=year_last, model_year_align=model_year_align ) + call this%C13InitAllocate( bounds ) + + end subroutine C13Init + + !------------------------------------------------------------------------------------- + + subroutine C13InitAllocate( this, bounds ) + ! Allocate memory for the delta C13 data array + use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + integer :: begg, endg + + begg = bounds%begg; endg = bounds%endg + allocate( this%atm_delta_c13( bounds%begg : bounds%endg ) ); this%atm_delta_c13 = nan + end subroutine C13InitAllocate + + !------------------------------------------------------------------------------------- + + subroutine C13ClassClean( this ) + ! Clean up memory for the C13 stream type as a class method + class(atm_delta_c13_stream_type), intent(inout) :: this + + call C13TypeClean( this ) + + end subroutine C13ClassClean + + !------------------------------------------------------------------------------------- + + subroutine C13TypeClean( this ) + ! Clean up memory for the C13 stream type for this specific type + type(atm_delta_c13_stream_type), intent(inout) :: this + + deallocate( this%atm_delta_c13 ) + call this%CleanBase() + + end subroutine C13TypeClean + + !------------------------------------------------------------------------------------- + + subroutine C13Interp( this, bounds ) + ! + ! Fill the local CTSM grid delta C13 array with data from the stream + ! + ! Arguments + class(atm_delta_c13_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + ! Local Variables + integer :: g + real(r8), pointer :: dataptr1d(:) + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call this%GetPtr1D( varname_c13, dataptr1d ) + + do g = bounds%begg, bounds%endg + this%atm_delta_c13(g) = dataptr1d(g) + end do + end subroutine C13Interp + + !------------------------------------------------------------------------------------- + + subroutine C14Init( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + year_first, year_last, model_year_align ) + ! + ! Initialize the atmospheric delta C14 stream type + ! + ! Arguments: + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + call this%InitBase( bounds, varnames = (/ varname_c14 /), fldfilename=fldfilename, meshfile=meshfile, & + mapalgo=mapalgo, tintalgo=tintalgo, taxmode=taxmode, name=varname_c14, & + year_first=year_first, year_last=year_last, model_year_align=model_year_align ) + call this%C14InitAllocate( bounds ) + + end subroutine C14Init + + !------------------------------------------------------------------------------------- + + subroutine C14InitAllocate( this, bounds ) + ! Allocate memory for the delta C14 data array + use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + ! Arguments + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + integer :: begg, endg + + begg = bounds%begg; endg = bounds%endg + allocate( this%atm_delta_c14( bounds%begg : bounds%endg ) ); this%atm_delta_c14 = nan + end subroutine C14InitAllocate + + !------------------------------------------------------------------------------------- + + subroutine C14Interp( this, bounds ) + ! Fill the local CTSM grid delta C13 array with data from the stream + ! + ! Arguments: + class(atm_delta_c14_stream_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + + ! Local Variables + integer :: g + real(r8), pointer :: dataptr1d(:) + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call this%GetPtr1D( varname_c14, dataptr1d ) + + do g = bounds%begg, bounds%endg + this%atm_delta_c14(g) = dataptr1d(g) + end do + end subroutine C14Interp + + !------------------------------------------------------------------------------------- + + subroutine C14ClassClean( this ) + ! Clean up memory for the C14 stream type as a class method + class(atm_delta_c14_stream_type), intent(inout) :: this + + call C14TypeClean( this ) + + end subroutine C14ClassClean + + !------------------------------------------------------------------------------------- + + subroutine C14TypeClean( this ) + ! Clean up memory for the C14 stream type for this specific type + type(atm_delta_c14_stream_type), intent(inout) :: this + + deallocate( this%atm_delta_c14 ) + call this%CleanBase() + + end subroutine C14TypeClean + + !------------------------------------------------------------------------------------- + +end module AtmCarbonIsotopeStreamType \ No newline at end of file diff --git a/src/biogeochem/CMakeLists.txt b/src/biogeochem/CMakeLists.txt index 3da0a2eab6..393e4e4db2 100644 --- a/src/biogeochem/CMakeLists.txt +++ b/src/biogeochem/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND clm_sources CNVegNitrogenStateType.F90 CNVegNitrogenFluxType.F90 CNCIsoAtmTimeSeriesReadMod.F90 + AtmCarbonIsotopeStreamType.F90 CNVegComputeSeedMod.F90 FATESFireBase.F90 FATESFireDataMod.F90 diff --git a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 index 529a547e88..736924fd4f 100644 --- a/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 +++ b/src/biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 @@ -1,22 +1,27 @@ module CIsoAtmTimeseriesMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! Module for transient atmospheric boundary to the c13 and c14 codes ! ! !USES: - use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL use clm_time_manager , only : get_curr_date, get_curr_yearfrac - use clm_varcon , only : c14ratio, secspday + use clm_varcon , only : c13ratio, c14ratio, secspday use shr_const_mod , only : SHR_CONST_PDB ! Ratio of C13/C12 - use clm_varctl , only : iulog + use clm_varctl , only : iulog, use_c13, use_c14 use abortutils , only : endrun use spmdMod , only : masterproc use shr_log_mod , only : errMsg => shr_log_errMsg + use AtmCarbonIsotopeStreamType, only : atm_delta_c13_stream_type, atm_delta_c14_stream_type + use decompMod , only : bounds_type ! implicit none private ! ! !PUBLIC MEMBER FUNCTIONS: + public:: CIsoAtmReadNML ! Read namelist for atmospheric C14/C13 isotope time series public:: C14BombSpike ! Time series for C14 data public:: C14_init_BombSpike ! Initialize C14 data series and read data in public:: C13Timeseries ! Time series for C13 data @@ -27,91 +32,439 @@ module CIsoAtmTimeseriesMod character(len=256) , public :: atm_c14_filename = ' ' ! file name of C14 input data logical , public :: use_c13_timeseries = .false. ! do we use time-varying atmospheric C13? character(len=256) , public :: atm_c13_filename = ' ' ! file name of C13 input data - integer, parameter , public :: nsectors_c14 = 3 ! Number of latitude sectors the C14 data has + real(r8), allocatable, public, protected :: rc14_atm_grc(:) ! Ratio of C14 C12 data on gridcell + real(r8), allocatable, public, protected :: rc13_atm_grc(:) ! Ratio of C13 C12 data on gridcell ! ! !PRIVATE MEMBER FUNCTIONS: private:: check_units ! Check the units of the data on the input file + ! Private subroutines only made public for unit testing + public:: CIsoCheckNMLInputs ! Check that the namelist inputs are valid + public:: CIsoSetNMLInputs ! Set the namelist inputs for unit testing + public:: CIsoSetControl ! Set the control variables for Carbon Isotopes + public:: CIsoLogControl ! Write out the control settings to the logfile + + type(atm_delta_c13_stream_type), private :: atm_c13_stream ! Atmospheric C13 stream object + type(atm_delta_c14_stream_type), private :: atm_c14_stream ! Atmospheric C14 stream object + ! !PRIVATE TYPES: + integer, parameter , private :: nsectors_c14 = 3 ! Number of latitude sectors the C14 data has real(r8), allocatable, private :: atm_c14file_time(:) ! time for C14 data - real(r8), allocatable, private :: atm_delta_c14(:,:) ! Delta C14 data + real(r8), allocatable, private :: atm_delta_c14(:,:) ! Delta C14 data (time,nsectors) + real(r8), allocatable, private :: atm_delta_c14_grc(:) ! Delta C14 data on gridcell real(r8), allocatable, private :: atm_c13file_time(:) ! time for C13 data - real(r8), allocatable, private :: atm_delta_c13(:) ! Delta C13 data + real(r8), allocatable, private :: atm_delta_c13(:) ! Delta C13 data (time) + real(r8), allocatable, private :: atm_delta_c13_grc(:) ! Delta C13 data on gridcell real(r8), parameter :: time_axis_offset = 1850.0_r8 ! Offset in years of time on file + logical, private :: use_c13_streams = .false. ! By default read in the CMIP6 file format for C13 + logical, private :: use_c14_streams = .false. ! By default read in the CMIP6 file format for C14 + + ! Private data for the control namelist: + character(len=CL), private :: stream_fldfilename_atm_c14 = ' ' + character(len=CL), private :: stream_fldfilename_atm_c13 = ' ' + integer, private :: stream_year_first_atm_c14 = 1850 + integer, private :: stream_year_last_atm_c14 = 2023 + integer, private :: stream_model_year_align_atm_c14 = 1850 + integer, private :: stream_year_first_atm_c13 = 1850 + integer, private :: stream_year_last_atm_c13 = 2023 + integer, private :: stream_model_year_align_atm_c13 = 1850 + character(len=CL), private :: stream_mapalgo_atm_c14 = 'nn' + character(len=CL), private :: stream_tintalgo_atm_c14 = 'linear' + character(len=CL), private :: stream_taxmode_atm_c14 = 'extend' + character(len=CL), private :: stream_mapalgo_atm_c13 = 'nn' + character(len=CL), private :: stream_tintalgo_atm_c13 = 'linear' + character(len=CL), private :: stream_taxmode_atm_c13 = 'extend' + character(len=*), parameter, private :: sourcefile = & - __FILE__ + __FILE__ !----------------------------------------------------------------------- contains !----------------------------------------------------------------------- - subroutine C14BombSpike( rc14_atm ) + subroutine CIsoAtmReadNML( NLFilename ) + ! + ! !DESCRIPTION: + ! Read in the namelist for atmospheric C14/C13 isotope time series + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use spmdMod , only : masterproc, mpicom + use shr_mpi_mod, only : shr_mpi_bcast + + ! Arguments: + character(len=*), intent(in) :: NLFilename ! Namelist filename to read + + ! !LOCAL VARIABLES: + integer :: ierr ! error code + integer :: unitn ! unit for namelist file + character(len=*), parameter :: nml_name = 'carbon_isotope_streams' ! MUST agree with name in namelist and read + + namelist /carbon_isotope_streams/ stream_fldfilename_atm_c14, & + stream_fldfilename_atm_c13, stream_year_first_atm_c14, & + stream_year_last_atm_c14, stream_model_year_align_atm_c14, & + stream_year_first_atm_c13, stream_year_last_atm_c13, & + stream_model_year_align_atm_c13 + + ! Read in the namelist on the main task + if (masterproc) then + open( newunit=unitn, file=trim(NLFilename), status='old', iostat=ierr ) + write(iulog,*) 'Read in '//nml_name//' namelist' + call shr_nl_find_group_name(unitn, nml_name, status=ierr) + if (ierr == 0) then + read(unitn, nml=carbon_isotope_streams, iostat=ierr) + if (ierr /= 0) then + call endrun(msg="ERROR reading "//nml_name//"namelist", file=sourcefile, line=__LINE__) + return + end if + else + call endrun(msg="ERROR could NOT find "//nml_name//"namelist", file=sourcefile, line=__LINE__) + return + end if + close( unitn ) + end if + ! Broadcast namelist values to all tasks + call shr_mpi_bcast( stream_fldfilename_atm_c14, mpicom ) + call shr_mpi_bcast( stream_year_first_atm_c14, mpicom ) + call shr_mpi_bcast( stream_year_last_atm_c14, mpicom ) + call shr_mpi_bcast( stream_model_year_align_atm_c14, mpicom ) + call shr_mpi_bcast( stream_fldfilename_atm_c13, mpicom ) + call shr_mpi_bcast( stream_year_first_atm_c13, mpicom ) + call shr_mpi_bcast( stream_year_last_atm_c13, mpicom ) + call shr_mpi_bcast( stream_model_year_align_atm_c13, mpicom ) + + ! Do some error checking of input namelist items, set control flags, and write to the log + call CIsoCheckNMLInputs() + + call CIsoSetControl() + call CIsoLogControl() + + end subroutine CIsoAtmReadNML + + !----------------------------------------------------------------------- + subroutine CIsoSetControl() + ! Set control settings based on the namelist inputs + ! Also do some assert checks to make sure other settings are as expected + ! + if ( use_c13_timeseries )then + ! Decide if C14/C13 streams are going to be used or the old method + if ( len_trim(stream_fldfilename_atm_c13) /= 0 ) then + use_c13_streams = .true. + else + use_c13_streams = .false. + call shr_assert( len_trim(atm_c13_filename) /= 0 , & + msg="ERROR: use_c13_timeseries is true but atm_c13_filename is blank", file=sourcefile, line=__LINE__) + call shr_assert( .not. use_c13_streams , & + msg="ERROR: stream_fldfilename_atm_c13 is blank but use_c13_streams is not TRUE", file=sourcefile, line=__LINE__) + end if + else + use_c13_streams = .false. + call shr_assert( .not. use_c13_streams , & + msg="ERROR: use_c13_timeseries is false, but use_c13_streams is TRUE", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c13_filename) == 0 , & + msg="ERROR: use_c13_timeseries is false but atm_c13_filename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0 , & + msg="ERROR: use_c13_timeseries is false but stream_fldfilename_atm_c13 is NOT blank", file=sourcefile, line=__LINE__) + end if + if ( use_c14_bombspike )then + if ( len_trim(stream_fldfilename_atm_c14) /= 0 ) then + use_c14_streams = .true. + else + use_c14_streams = .false. + call shr_assert( len_trim(atm_c14_filename) /= 0 , & + msg="ERROR: use_c14_bombspike is true but atm_c14_filename is blank", file=sourcefile, line=__LINE__) + call shr_assert( .not. use_c14_streams , & + msg="ERROR: stream_fldfilename_atm_c14 is blank but use_c14_streams is not TRUE", & + file=sourcefile, line=__LINE__) + end if + else + use_c14_streams = .false. + call shr_assert( .not. use_c14_streams , & + msg="ERROR: use_c14_bombspike is false, but use_c14_streams is TRUE", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c14_filename) == 0, & + msg="ERROR: use_c14_bombspike is false but atm_c14_filename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0 , & + msg="ERROR: use_c14_bombspike is false but stream_fldfilename_atm_c14 is NOT blank", & + file=sourcefile, line=__LINE__) + end if + + end subroutine CIsoSetControl + + !----------------------------------------------------------------------- + subroutine CIsoLogControl() + ! Log namelist and control settings to output to display what behavior will be + ! + if ( use_c13_timeseries )then + if ( use_c13_streams ) then + call shr_assert( len_trim(stream_fldfilename_atm_c13) /= 0 , & + msg="use_c13_streams is TRUE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C13 atmospheric data will be read in using the streams method from file: '// & + trim(stream_fldfilename_atm_c13) + else if ( len_trim(atm_c13_filename) /= 0 ) then + write(iulog,*) 'C13 atmospheric data will be read in using the CMIP6 time series method from file: '// & + trim(atm_c13_filename) + else + call endrun(msg="use_c13_timeseries is true but use_c13_streams=FALSE and atm_c13_filename is blank", & + file=sourcefile, line=__LINE__) + end if + else + call shr_assert( len_trim(stream_fldfilename_atm_c13) == 0 , & + msg="use_c13_timeseries is FALSE but stream_fldfilename is NOT blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c13_filename) == 0 , & + msg="use_c13_timeseries is FALSE but stream_fldfilename is NOT blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C13 atmospheric data will be the global constant pre-industrial level' + end if + if ( use_c14_bombspike )then + if ( use_c14_streams ) then + write(iulog,*) 'C14 atmospheric data will be read in using the streams method from file: '// & + trim(stream_fldfilename_atm_c14) + else if ( len_trim(atm_c14_filename) /= 0 ) then + write(iulog,*) 'C14 atmospheric data will be read in using the CMIP6 time series method from file: '// & + trim(atm_c14_filename) + else + call endrun(msg="use_c14_bombspike is true but use_c14_streams=FALSE and atm_c14_filename is blank", & + file=sourcefile, line=__LINE__) + end if + else + call shr_assert( len_trim(stream_fldfilename_atm_c14) == 0 , & + msg="use_c14_bombspike is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + call shr_assert( len_trim(atm_c14_filename) == 0 , & + msg="use_c14_bombspike is FALSE but stream_fldfilename is blank", file=sourcefile, line=__LINE__) + write(iulog,*) 'C14 atmospheric data will be global constant pre-industrial level' + end if + + end subroutine CIsoLogControl + + !----------------------------------------------------------------------- + subroutine CIsoCheckNMLInputs() + ! + ! !DESCRIPTION: + ! Check that the namelist inputs are valid + ! + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + ! When carbon isotopes are off nothing should be set + if ( .not. use_c13 )then + if ( use_c13_timeseries ) then + call endrun( msg="use_c13 is false but use_c13_timeseries is TRUE " // & + "(use_c13_timeseries can only be TRUE if use_c13 is TRUE)", file=sourcefile, line=__LINE__) + return + end if + end if + if ( .not. use_c14 )then + if ( use_c14_bombspike ) then + call endrun( msg="use_c14 is false but use_c14_bombspike is TRUE " // & + "(use_c14_bombspike can only be TRUE if use_c14 is TRUE)", & + file=sourcefile, line=__LINE__) + return + end if + end if + + ! + ! Check C14 stream namelist inputs + ! + if ( use_c14_bombspike ) then + if ( len_trim(atm_c14_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c14) /= 0 ) then + call endrun(msg="use_c14_bombspike TRUE but both atm_c14_filename AND stream_fldfilename_atm_c14 are set and only one should be", & + file=sourcefile, line=__LINE__) + return + end if + if ( len_trim(atm_c14_filename) == 0 .and. len_trim(stream_fldfilename_atm_c14) == 0 ) then + call endrun(msg="use_c14_bombspike TRUE but neither atm_c14_filename nor stream_fldfilename_atm_c14 are set and one or the other needs to be", & + file=sourcefile, line=__LINE__) + return + end if + else + if ( len_trim(atm_c14_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c14) /= 0 ) then + call endrun(msg="use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be", & + file=sourcefile, line=__LINE__) + return + end if + end if + ! + ! Check C13 stream namelist inputs + ! + if ( use_c13_timeseries ) then + if ( len_trim(atm_c13_filename) /= 0 .and. len_trim(stream_fldfilename_atm_c13) /= 0 ) then + call endrun(msg="use_c13_timeseries TRUE but both atm_c13_filename AND stream_fldfilename_atm_c13 are set and only one should be", & + file=sourcefile, line=__LINE__) + return + end if + if ( len_trim(atm_c13_filename) == 0 .and. len_trim(stream_fldfilename_atm_c13) == 0 ) then + call endrun(msg="use_c13_timeseries TRUE but neither atm_c13_filename nor stream_fldfilename_atm_c13 are set and one or the other needs to be", & + file=sourcefile, line=__LINE__) + return + end if + else + if ( len_trim(atm_c13_filename) /= 0 .or. len_trim(stream_fldfilename_atm_c13) /= 0 ) then + call endrun(msg="use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be", & + file=sourcefile, line=__LINE__) + return + end if + end if + + end subroutine CIsoCheckNMLInputs + + !----------------------------------------------------------------------- + subroutine CIsoSetNMLInputs( stream_fldfilename_atm_c13_in, stream_fldfilename_atm_c14_in, & + use_c13_streams_in, use_c14_streams_in ) + ! + ! !DESCRIPTION: + ! Set the namelist inputs for unit testing + ! + ! Arguments: + character(len=*), intent(in), optional :: stream_fldfilename_atm_c13_in + character(len=*), intent(in), optional :: stream_fldfilename_atm_c14_in + logical, intent(in), optional :: use_c13_streams_in + logical, intent(in), optional :: use_c14_streams_in + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + if ( present(stream_fldfilename_atm_c13_in) ) then + stream_fldfilename_atm_c13 = stream_fldfilename_atm_c13_in + end if + if ( present(stream_fldfilename_atm_c14_in) ) then + stream_fldfilename_atm_c14 = stream_fldfilename_atm_c14_in + end if + if ( present(use_c13_streams_in) ) then + use_c13_streams = use_c13_streams_in + end if + if ( present(use_c14_streams_in) ) then + use_c14_streams = use_c14_streams_in + end if + + end subroutine CIsoSetNMLInputs + + + !----------------------------------------------------------------------- + subroutine C14BombSpike( bounds ) ! ! !DESCRIPTION: ! for transient simulation, read in an atmospheric timeseries file to impose bomb spike ! + use GridcellType , only : grc ! !ARGUMENTS: implicit none - real(r8), intent(out) :: rc14_atm(nsectors_c14) ! Ratio of C14 to C12 + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: yr, mon, day, tod ! year, month, day, time-of-day real(r8) :: dateyear ! Date converted to year real(r8) :: delc14o2_atm(nsectors_c14) ! C14 delta units - integer :: fp, p, nt ! Indices + real(r8) :: rc14_atm(nsectors_c14) ! C14 ratio C14 C12units + integer :: fp, p, nt, g ! Indices integer :: ind_below ! Time index below current time integer :: ntim_atm_ts ! Number of times on file real(r8) :: twt_1, twt_2 ! weighting fractions for interpolating integer :: l ! Loop index of sectors !----------------------------------------------------------------------- - ! get current date - call get_curr_date(yr, mon, day, tod) - dateyear = real(yr) + get_curr_yearfrac() - - ! find points in atm timeseries to interpolate between - ntim_atm_ts = size(atm_c14file_time) - ind_below = 0 - do nt = 1, ntim_atm_ts - if ((dateyear - time_axis_offset) >= atm_c14file_time(nt) ) then - ind_below = ind_below+1 - endif - end do + ! + ! If the bombspike timeseries file is being used, read the file in + ! + if ( use_c14_bombspike )then + + if ( use_c14_streams )then + call C14Streams( bounds ) + RETURN + end if + ! get current date + call get_curr_date(yr, mon, day, tod) + dateyear = real(yr) + get_curr_yearfrac() + + ! find points in atm timeseries to interpolate between + ntim_atm_ts = size(atm_c14file_time) + ind_below = 0 + do nt = 1, ntim_atm_ts + if ((dateyear - time_axis_offset) >= atm_c14file_time(nt) ) then + ind_below = ind_below+1 + endif + end do + + ! loop over lat bands to pass all three to photosynthesis + do l = 1,nsectors_c14 + ! interpolate between nearest two points in atm c14 timeseries + if (ind_below .eq. 0 ) then + delc14o2_atm(l) = atm_delta_c14(l,1) + elseif (ind_below .eq. ntim_atm_ts ) then + delc14o2_atm(l) = atm_delta_c14(l,ntim_atm_ts) + else + twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c14file_time(ind_below)) & + / (atm_c14file_time(ind_below+1)-atm_c14file_time(ind_below)))) + twt_1 = 1._r8 - twt_2 + delc14o2_atm(l) = atm_delta_c14(l,ind_below) * twt_1 + atm_delta_c14(l,ind_below+1) * twt_2 + endif + + ! change delta units to ratio + rc14_atm(l) = (delc14o2_atm(l) * 1.e-3_r8 + 1._r8) * c14ratio + end do + ! + ! When not using a time series file -- use the constant preindustrial value + ! + else + rc14_atm(:) = c14ratio + delc14o2_atm(:) = (rc14_atm(1)/c14ratio -1.0_r8)*1000.0_r8 + endif + ! + ! Now map to the gridcell from the sectors + ! - ! loop over lat bands to pass all three to photosynthesis - do l = 1,nsectors_c14 - ! interpolate between nearest two points in atm c14 timeseries - if (ind_below .eq. 0 ) then - delc14o2_atm(l) = atm_delta_c14(l,1) - elseif (ind_below .eq. ntim_atm_ts ) then - delc14o2_atm(l) = atm_delta_c14(l,ntim_atm_ts) + do g = bounds%begg, bounds%endg + ! determine latitute sector for radiocarbon bomb spike inputs + if ( grc%latdeg(g) >= 30._r8 ) then + l = 1 + else if ( grc%latdeg(g) >= -30._r8 ) then + l = 2 else - twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c14file_time(ind_below)) & - / (atm_c14file_time(ind_below+1)-atm_c14file_time(ind_below)))) - twt_1 = 1._r8 - twt_2 - delc14o2_atm(l) = atm_delta_c14(l,ind_below) * twt_1 + atm_delta_c14(l,ind_below+1) * twt_2 + l = 3 endif - - ! change delta units to ratio - rc14_atm(l) = (delc14o2_atm(l) * 1.e-3_r8 + 1._r8) * c14ratio + atm_delta_c14_grc(g) = delc14o2_atm(l) + rc14_atm_grc(g) = rc14_atm(l) end do - + end subroutine C14BombSpike !----------------------------------------------------------------------- - subroutine C14_init_BombSpike() + subroutine C14Streams( bounds ) + ! Description: + ! + ! Use the streams method to read in atmospheric C14 bomb spike data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + integer :: g ! Indices + + call atm_c14_stream%Advance( ) + call atm_c14_stream%Interp( bounds) + + do g = bounds%begg, bounds%endg + atm_delta_c14_grc(g) = atm_c14_stream%atm_delta_c14(g) + rc14_atm_grc(g) = (atm_delta_c14_grc(g) * 1.e-3_r8 + 1._r8) * c14ratio + end do + + end subroutine C14Streams + + !----------------------------------------------------------------------- + subroutine C14_init_BombSpike( bounds ) ! ! !DESCRIPTION: - ! read netcdf file containing a timeseries of atmospheric delta C14 values; save in module-level array + ! read netcdf file containing a timeseries of atmospheric delta C14 values; save in module-level array ! ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) + implicit none + ! Arguments: + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - implicit none character(len=256) :: locfn ! local file name type(file_desc_t) :: ncid ! netcdf id integer :: dimid,varid ! input netCDF id's @@ -122,116 +475,239 @@ subroutine C14_init_BombSpike() character(len=*), parameter :: vname = 'Delta14co2_in_air' ! Variable name on file !----------------------------------------------------------------------- - call getfil(atm_c14_filename, locfn, 0) - - if ( masterproc ) then - write(iulog, *) 'C14_init_BombSpike: preparing to open file:' - write(iulog, *) trim(locfn) - endif - - call ncd_pio_openfile (ncid, trim(locfn), 0) + ! Allocate the gridcell arrays + ! TODO: This should be below within the use_c14_bombspike if block + allocate(atm_delta_c14_grc(bounds%begg:bounds%endg)) + allocate(rc14_atm_grc(bounds%begg:bounds%endg)) + atm_delta_c14_grc(:) = nan + rc14_atm_grc(:) = nan + ! + ! If the bombspike timeseries file is being used, read the file in + ! + if ( use_c14_bombspike )then + + if ( use_c14_streams )then + write(iulog,*) 'Read in atmospheric C14 data from streams' + call C14StreamsInit( bounds ) + RETURN + end if + if ( .not. use_c14_streams .and. len_trim(atm_c14_filename) == 0 )then + write(iulog,*) 'Use constant preindustrial atmospheric C14 data' + RETURN + end if + + if ( masterproc ) then + write(iulog, *) 'C14_init_BombSpike: preparing to open file:' + write(iulog, *) trim(locfn) + endif - call ncd_inqdlen(ncid,dimid,ntim,'time') - call ncd_inqdlen(ncid,dimid,nsec,'sector') - if ( nsec /= nsectors_c14 )then - call endrun(msg="ERROR: number of sectors on file not what's expected"//errMsg(sourcefile, __LINE__)) + call getfil(atm_c14_filename, locfn, 0) + + call ncd_pio_openfile (ncid, trim(locfn), 0) + + call ncd_inqdlen(ncid,dimid,ntim,'time') + call ncd_inqdlen(ncid,dimid,nsec,'sector') + if ( nsec /= nsectors_c14 )then + call endrun(msg="ERROR: number of sectors on file not what's expected"//errMsg(sourcefile, __LINE__)) + end if + + !! allocate arrays based on size of netcdf timeseries + allocate(atm_c14file_time(ntim)) + allocate(atm_delta_c14(nsectors_c14,ntim)) + atm_delta_c14(:,:) = 0.0_r8 + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c14file_time, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) + end if + + call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c14, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) + end if + ! Check units + call check_units( ncid, vname, "Modern" ) + call ncd_pio_closefile(ncid) + + ! check to make sure that time dimension is well behaved + do t = 2, ntim + if ( atm_c14file_time(t) - atm_c14file_time(t-1) <= 0._r8 ) then + write(iulog, *) 'C14_init_BombSpike: error. time axis must be monotonically increasing' + call endrun(msg=errMsg(sourcefile, __LINE__)) + endif + end do end if - !! allocate arrays based on size of netcdf timeseries - allocate(atm_c14file_time(ntim)) - allocate(atm_delta_c14(nsectors_c14,ntim)) - atm_delta_c14(:,:) = 0.0_r8 - - call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c14file_time, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) - end if + end subroutine C14_init_BombSpike - call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c14, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) - end if - ! Check units - call check_units( ncid, vname, "Modern" ) - call ncd_pio_closefile(ncid) - - ! check to make sure that time dimension is well behaved - do t = 2, ntim - if ( atm_c14file_time(t) - atm_c14file_time(t-1) <= 0._r8 ) then - write(iulog, *) 'C14_init_BombSpike: error. time axis must be monotonically increasing' - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - end do - end subroutine C14_init_BombSpike + !----------------------------------------------------------------------- + subroutine C14StreamsInit( bounds ) + ! Description: + ! + ! Initialize the streams method to read in atmospheric C14 bomb spike data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + if ( masterproc ) then + write(iulog, *) 'C14StreamsInit: Initializing C14 streams with file:' + write(iulog, *) trim(stream_fldfilename_atm_c14) + end if + ! Streams method + call atm_c14_stream%Init( bounds, & + fldfilename=stream_fldfilename_atm_c14, & + meshfile= 'none', & + mapalgo=stream_mapalgo_atm_c14, & + tintalgo=stream_tintalgo_atm_c14, & + taxmode=stream_taxmode_atm_c14, & + year_first=stream_year_first_atm_c14, & + year_last=stream_year_last_atm_c14, & + model_year_align=stream_model_year_align_atm_c14 ) + call atm_c14_stream%Advance( ) + call atm_c14_stream%Interp( bounds ) + + end subroutine C14StreamsInit !----------------------------------------------------------------------- - subroutine C13TimeSeries( rc13_atm ) + subroutine C13TimeSeries( bounds, atm2lnd_inst ) ! ! !DESCRIPTION: ! for transient pulse simulation, impose a time-varying atm boundary condition ! + use GridcellType , only : grc + use clm_varcon , only : preind_atm_del13c + use atm2lndType, only : atm2lnd_Type ! !ARGUMENTS: implicit none - real(r8), intent(out) :: rc13_atm ! Ratio of C13 to C12 + type(bounds_type), intent(in) :: bounds + type(atm2lnd_Type), intent(in) :: atm2lnd_inst ! ! !LOCAL VARIABLES: + real(r8) :: rc13_atm ! Ratio of C13 to C12 integer :: yr, mon, day, tod ! year, month, day, time-of-day real(r8) :: dateyear ! date translated to year real(r8) :: delc13o2_atm ! Delta C13 - integer :: fp, p, nt ! Indices + integer :: fp, p, nt, g ! Indices integer :: ind_below ! Index of time in file before current time integer :: ntim_atm_ts ! Number of times on file real(r8) :: twt_1, twt_2 ! weighting fractions for interpolating !----------------------------------------------------------------------- - ! get current date - call get_curr_date(yr, mon, day, tod) - dateyear = real(yr) + get_curr_yearfrac() - - ! find points in atm timeseries to interpolate between - ntim_atm_ts = size(atm_c13file_time) - ind_below = 0 - do nt = 1, ntim_atm_ts - if ((dateyear - time_axis_offset) >= atm_c13file_time(nt) ) then - ind_below = ind_below+1 - endif - end do + ! + ! If the timeseries file is being used, read the file in + ! + if ( use_c13_timeseries )then + + if ( use_c13_streams )then + call C13Streams( bounds ) + RETURN + end if + if ( .not. use_c13_streams .and. len_trim(atm_c13_filename) == 0 )then + write(iulog,*) 'Use constant preindustrial atmospheric C13 data' + RETURN + end if + ! get current date + call get_curr_date(yr, mon, day, tod) + dateyear = real(yr) + get_curr_yearfrac() + + ! find points in atm timeseries to interpolate between + ntim_atm_ts = size(atm_c13file_time) + ind_below = 0 + do nt = 1, ntim_atm_ts + if ((dateyear - time_axis_offset) >= atm_c13file_time(nt) ) then + ind_below = ind_below+1 + endif + end do + + ! interpolate between nearest two points in atm c13 timeseries + ! cdknotes. for now and for simplicity, just use the northern hemisphere values (sector 1) + if (ind_below .eq. 0 ) then + delc13o2_atm = atm_delta_c13(1) + elseif (ind_below .eq. ntim_atm_ts ) then + delc13o2_atm = atm_delta_c13(ntim_atm_ts) + else + twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c13file_time(ind_below)) & + / (atm_c13file_time(ind_below+1)-atm_c13file_time(ind_below)))) + twt_1 = 1._r8 - twt_2 + delc13o2_atm = atm_delta_c13(ind_below) * twt_1 + atm_delta_c13(ind_below+1) * twt_2 + endif - ! interpolate between nearest two points in atm c13 timeseries - ! cdknotes. for now and for simplicity, just use the northern hemisphere values (sector 1) - if (ind_below .eq. 0 ) then - delc13o2_atm = atm_delta_c13(1) - elseif (ind_below .eq. ntim_atm_ts ) then - delc13o2_atm = atm_delta_c13(ntim_atm_ts) + ! + ! When not using a time series file -- use the constant value + ! else - twt_2 = min(1._r8, max(0._r8,((dateyear - time_axis_offset)-atm_c13file_time(ind_below)) & - / (atm_c13file_time(ind_below+1)-atm_c13file_time(ind_below)))) - twt_1 = 1._r8 - twt_2 - delc13o2_atm = atm_delta_c13(ind_below) * twt_1 + atm_delta_c13(ind_below+1) * twt_2 - endif + rc13_atm = c13ratio + delc13o2_atm = (rc13_atm/SHR_CONST_PDB - 1.0_r8)*1000.0_r8 + end if ! change delta units to ratio, put on patch loop rc13_atm = (delc13o2_atm * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB + ! + ! Copy to the gridcell arrays + ! + do g = bounds%begg, bounds%endg + + associate( & + forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) + forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) + ) + rc13_atm_grc(g) = rc13_atm + atm_delta_c13_grc(g) = delc13o2_atm + + ! Currently when C13 is fixed, it's dependent on CO2 levels and changes with pressure + ! NOTE: This duplicates code in lnd_import_export.F90 + if ( .not. use_c13_timeseries )then + rc13_atm_grc(g) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) + atm_delta_c13_grc(g) = (rc13_atm_grc(g) / SHR_CONST_PDB - 1.0_r8)*1000.0_r8 + end if + end associate + end do + end subroutine C13TimeSeries !----------------------------------------------------------------------- - subroutine C13_init_TimeSeries() + subroutine C13Streams( bounds ) + ! Description: + ! + ! Use the streams method to read in atmospheric C13 data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: g ! Indices + + call atm_c13_stream%Interp( bounds) + + do g = bounds%begg, bounds%endg + atm_delta_c13_grc(g) = atm_c13_stream%atm_delta_c13(g) + rc13_atm_grc(g) = (atm_delta_c13_grc(g) * 1.e-3_r8 + 1._r8) * SHR_CONST_PDB + end do + + end subroutine C13Streams + + !----------------------------------------------------------------------- + subroutine C13_init_TimeSeries( bounds ) ! ! !DESCRIPTION: - ! read netcdf file containing a timeseries of atmospheric delta C13 values; save in module-level array + ! read netcdf file containing a timeseries of atmospheric delta C13 values; save in module-level array ! ! !USES: use ncdio_pio , only : ncd_pio_openfile, ncd_pio_closefile, file_desc_t, ncd_inqdlen, ncd_io use fileutils , only : getfil + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) + implicit none ! + ! Arguments: + type(bounds_type), intent(in) :: bounds ! !LOCAL VARIABLES: - implicit none character(len=256) :: locfn ! local file name type(file_desc_t) :: ncid ! netcdf id integer :: dimid,varid ! input netCDF id's @@ -241,47 +717,96 @@ subroutine C13_init_TimeSeries() character(len=*), parameter :: vname = 'delta13co2_in_air' ! Variable name on file !----------------------------------------------------------------------- - call getfil(atm_c13_filename, locfn, 0) + ! TODO: This should be below within the use_c13_timeseries if block + ! Allocate the gridcell arrays + allocate(atm_delta_c13_grc(bounds%begg:bounds%endg) ) + allocate(rc13_atm_grc(bounds%begg:bounds%endg) ) + atm_delta_c13_grc(:) = nan + rc13_atm_grc(:) = nan + ! + ! If the timeseries file is being used, read the file in + ! + if ( use_c13_timeseries )then - if ( masterproc ) then - write(iulog, *) 'C13_init_TimeSeries: preparing to open file:' - write(iulog, *) trim(locfn) - endif + if ( use_c13_streams )then + call C13StreamsInit( bounds ) + RETURN + end if - call ncd_pio_openfile (ncid, trim(locfn), 0) + call getfil(atm_c13_filename, locfn, 0) - call ncd_inqdlen(ncid,dimid,ntim,'time') + if ( masterproc ) then + write(iulog, *) 'C13_init_TimeSeries: preparing to open file:' + write(iulog, *) trim(locfn) + endif - !! allocate arrays based on size of netcdf timeseries - allocate(atm_c13file_time(ntim)) - allocate(atm_delta_c13(ntim)) + call ncd_pio_openfile (ncid, trim(locfn), 0) - call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c13file_time, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) - end if + call ncd_inqdlen(ncid,dimid,ntim,'time') - call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c13, & - readvar=readvar) - if ( .not. readvar ) then - call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) - end if + !! allocate arrays based on size of netcdf timeseries + allocate(atm_c13file_time(ntim)) + allocate(atm_delta_c13(ntim)) - ! Check units - call check_units( ncid, vname, "VPDB" ) - call ncd_pio_closefile(ncid) - ! check to make sure that time dimension is well behaved - do t = 2, ntim - if ( atm_c13file_time(t) - atm_c13file_time(t-1) <= 0._r8 ) then - write(iulog, *) 'C13_init_TimeSeries: error. time axis must be monotonically increasing' - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - end do + call ncd_io(ncid=ncid, varname='time', flag='read', data=atm_c13file_time, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: time not on file"//errMsg(sourcefile, __LINE__)) + end if + + call ncd_io(ncid=ncid, varname=vname, flag='read', data=atm_delta_c13, & + readvar=readvar) + if ( .not. readvar ) then + call endrun(msg="ERROR: '//vname//' not on file"//errMsg(sourcefile, __LINE__)) + end if + + ! Check units + call check_units( ncid, vname, "VPDB" ) + call ncd_pio_closefile(ncid) + + ! check to make sure that time dimension is well behaved + do t = 2, ntim + if ( atm_c13file_time(t) - atm_c13file_time(t-1) <= 0._r8 ) then + write(iulog, *) 'C13_init_TimeSeries: error. time axis must be monotonically increasing' + call endrun(msg=errMsg(sourcefile, __LINE__)) + endif + end do + end if end subroutine C13_init_TimeSeries + !----------------------------------------------------------------------- + subroutine C13StreamsInit( bounds ) + ! Description: + ! + ! Initialize the streams method to read in atmospheric C13 data + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + + if ( masterproc ) then + write(iulog, *) 'C13StreamsInit: Initializing C13 streams with file:' + write(iulog, *) trim(stream_fldfilename_atm_c13) + end if + ! Streams method + call atm_c13_stream%Init( bounds, & + fldfilename=stream_fldfilename_atm_c13, & + meshfile= 'none', & + mapalgo=stream_mapalgo_atm_c13, & + tintalgo=stream_tintalgo_atm_c13, & + taxmode=stream_taxmode_atm_c13, & + year_first=stream_year_first_atm_c13, & + year_last=stream_year_last_atm_c13, & + model_year_align=stream_model_year_align_atm_c13 ) + call atm_c13_stream%Advance( ) + call atm_c13_stream%Interp( bounds ) + + end subroutine C13StreamsInit + !----------------------------------------------------------------------- subroutine check_units( ncid, vname, relativeto ) ! diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt new file mode 100644 index 0000000000..9a9885508c --- /dev/null +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/CMakeLists.txt @@ -0,0 +1,11 @@ +set(pfunit_sources + test_CIsoAtmTimeSeries.pf) + +add_pfunit_ctest(CIsoAtmTimeSeries + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) + +# I don't think we need esmf here +# LINK_LIBRARIES clm csm_share esmf +# EXTRA_FINALIZE unittest_finalize_esmf +# EXTRA_USE unittestInitializeAndFinalize) diff --git a/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf new file mode 100644 index 0000000000..01866b7617 --- /dev/null +++ b/src/biogeochem/test/CIsoAtmTimeSeries_test/test_CIsoAtmTimeSeries.pf @@ -0,0 +1,277 @@ +module test_CIsoAtmTimeSeries + + ! Tests of CNCIsoAtmTimeSeriesReadMod + + use funit + use CIsoAtmTimeSeriesMod + use shr_kind_mod , only : r8 => shr_kind_r8 + use clm_varctl, only : use_c13, use_c14 + + implicit none + + @TestCase + type, extends(TestCase) :: TestCIsoAtmTimeSeries + contains + procedure :: setUp + procedure :: tearDown + end type TestCIsoAtmTimeSeries + + character(len=200) :: expected_msg + +contains + + subroutine setUp(this) + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c13 = .true. + use_c14 = .true. + use_c13_timeseries = .true. + use_c14_bombspike = .true. + ! Set these filenames to /dev/null as it's a file guaranteed to exist on Linux systems + atm_c13_filename = '/dev/null' + atm_c14_filename = '/dev/null' + + end subroutine setUp + + subroutine tearDown(this) + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + end subroutine tearDown + + @Test + subroutine check_both_timeseries_on(this) + ! Check that it works when both timeseries are on without using streams + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_on + + @Test + subroutine check_both_timeseries_streams_on(this) + ! Check that it works when both timeseries are on and using streams + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = '/dev/null', & + stream_fldfilename_atm_c14_in = '/dev/null' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_streams_on + + @Test + subroutine check_both_timeseries_off(this) + ! Check that it works when both timeseries are off + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + use_c13_timeseries = .false. + use_c14_bombspike = .false. + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', & + stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_both_timeseries_off + + @Test + subroutine check_ciso_off(this) + ! Check that it works when both carbon isotopes are off + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + atm_c13_filename = '' + atm_c14_filename = '' + use_c13_timeseries = .false. + use_c14_bombspike = .false. + use_c13 = .false. + use_c14 = .false. + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', & + stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + call CIsoSetControl() + call CIsoLogControl() + + end subroutine check_ciso_off + + @Test + subroutine abort_if_both_c13_plain_and_stream_timeseries_files_set(this) + ! Check that it aborts if both the plain and stream C13 timeseries files are set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + @assertTrue(use_c13_timeseries) + atm_c13_filename = 'plain_c13_timeseries_file' + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + @assertTrue(len_trim(atm_c14_filename) /= 0) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_timeseries_file', stream_fldfilename_atm_c14_in = ' ') + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries TRUE but both atm_c13_filename AND stream_fldfilename_atm_c13 are set and only one should be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_set + + @Test + subroutine abort_if_both_c14_plain_and_stream_timeseries_files_set(this) + ! Check that it aborts if both the plain and stream C14 timeseries files are set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + atm_c14_filename = 'plain_c14_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_timeseries_file' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike TRUE but both atm_c14_filename AND stream_fldfilename_atm_c14 are set and only one should be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_set + + @Test + subroutine abort_if_both_c13_plain_and_stream_timeseries_files_blank(this) + ! Check that it aborts if both the plain and stream C13 timeseries files are blank + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + @assertTrue(use_c13_timeseries) + atm_c13_filename = ' ' + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + @assertTrue(len_trim(atm_c14_filename) /= 0) + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ', stream_fldfilename_atm_c14_in = ' ') + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries TRUE but neither atm_c13_filename nor stream_fldfilename_atm_c13 are set and one or the other needs to be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c13_plain_and_stream_timeseries_files_blank + + @Test + subroutine abort_if_both_c14_plain_and_stream_timeseries_files_blank(this) + ! Check that it aborts if both the plain and stream C14 timeseries files are blank + class(TestCIsoAtmTimeSeries), intent(inout) :: this + @assertTrue(use_c14) + @assertTrue(use_c14_bombspike) + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike TRUE but neither atm_c14_filename nor stream_fldfilename_atm_c14 are set and one or the other needs to be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_both_c14_plain_and_stream_timeseries_files_blank + + @Test + subroutine abort_if_c13_timeseries_off_and_plain_timeseries_file_set(this) + ! Check that it aborts if c13 timeseries off, but the plain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + use_c13_timeseries = .false. + @assertFalse(use_c13_timeseries) + atm_c13_filename = 'plain_c13_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_timeseries_off_and_plain_timeseries_file_set + + @Test + subroutine abort_if_c13_timeseries_off_and_stream_timeseries_file_set(this) + ! Check that it aborts if c13 timeseries off, but the streamplain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + @assertTrue(use_c13) + use_c13_timeseries = .false. + @assertFalse(use_c13_timeseries) + atm_c13_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = 'stream_c13_filename ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13_timeseries is false but either atm_c13_filename or stream_fldfilename_atm_c13 are set and neither should be" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_timeseries_off_and_stream_timeseries_file_set + + @Test + subroutine abort_if_c14_bombspike_off_and_plain_timeseries_file_set(this) + ! Check that it aborts if c14 timeseries off, but the pain timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + integer :: i + + ! This should be true whether C14 is on or off + do i = 1, 2 + if ( i == 1 ) then + @assertTrue(use_c14) + else + use_c14 = .false. + end if + use_c14_bombspike = .false. + atm_c14_filename = 'plain_c14_timeseries_file' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be" + @assertExceptionRaised(expected_msg) + end do + + end subroutine abort_if_c14_bombspike_off_and_plain_timeseries_file_set + + @Test + subroutine abort_if_c14_bombspike_off_and_stream_timeseries_file_set(this) + ! Check that it aborts if c14 timeseries off, but the stream timeseries file is set + class(TestCIsoAtmTimeSeries), intent(inout) :: this + integer :: i + + ! This should be true whether C14 is on or off + do i = 1, 2 + if ( i == 1 ) then + @assertTrue(use_c14) + else + use_c14 = .false. + end if + use_c14_bombspike = .false. + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = 'stream_c14_filename' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14_bombspike false but either atm_c14_filename or stream_fldfilename_atm_c14 is set and neither should be" + @assertExceptionRaised(expected_msg) + end do + + end subroutine abort_if_c14_bombspike_off_and_stream_timeseries_file_set + + @Test + subroutine abort_if_c13_off_but_c13_timeseries_on(this) + ! Check that it aborts if c13 is off, but the c13 timeseries is on + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c13 = .false. + use_c13_timeseries = .true. + atm_c13_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c13_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c13 is false but use_c13_timeseries is TRUE (use_c13_timeseries can only be TRUE if use_c13 is TRUE)" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c13_off_but_c13_timeseries_on + + @Test + subroutine abort_if_c14_off_but_c14_bombspike_on(this) + ! Check that it aborts if c14 is off, but the c14 bombspike is on + class(TestCIsoAtmTimeSeries), intent(inout) :: this + + use_c14 = .false. + @assertFalse(use_c14) + use_c14_bombspike = .true. + atm_c14_filename = ' ' + call CIsoSetNMLInputs( stream_fldfilename_atm_c14_in = ' ' ) + call CIsoCheckNMLInputs() + expected_msg = "ABORTED: use_c14 is false but use_c14_bombspike is TRUE (use_c14_bombspike can only be TRUE if use_c14 is TRUE)" + @assertExceptionRaised(expected_msg) + + end subroutine abort_if_c14_off_but_c14_bombspike_on + +end module test_CIsoAtmTimeSeries diff --git a/src/biogeochem/test/CMakeLists.txt b/src/biogeochem/test/CMakeLists.txt index 2ebe27c76f..e47e2429b6 100644 --- a/src/biogeochem/test/CMakeLists.txt +++ b/src/biogeochem/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(Species_test) +add_subdirectory(CIsoAtmTimeSeries_test) add_subdirectory(CNVegComputeSeed_test) add_subdirectory(CNPhenology_test) add_subdirectory(Latbaset_test) diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index afe665e6d1..3b0d990b21 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -13,7 +13,7 @@ module CanopyFluxesMod use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun - use clm_varctl , only : iulog, use_cn, use_lch4, use_c13, use_c14, use_cndv, use_fates, & + use clm_varctl , only : iulog, use_cn, use_lch4, use_c13, use_cndv, use_fates, & use_luna, use_hydrstress, use_biomass_heat_storage, z0param_method use clm_varpar , only : nlevgrnd, nlevsno, nlevcan, mxpft use pftconMod , only : pftcon @@ -229,7 +229,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, use clm_time_manager , only : get_step_size_real, get_prev_date, is_near_local_noon use clm_varcon , only : sb, cpair, hvap, vkc, grav, denice, c_to_b use clm_varcon , only : denh2o, tfrz, tlsai_crit, alpha_aero - use clm_varcon , only : c14ratio, spval + use clm_varcon , only : spval use clm_varcon , only : c_water, c_dry_biomass, c_to_b use clm_varcon , only : nu_param, cd1_param use perf_mod , only : t_startf, t_stopf @@ -354,7 +354,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, real(r8) :: err(bounds%begp:bounds%endp) ! balance error real(r8) :: erre ! balance error real(r8) :: co2(bounds%begp:bounds%endp) ! atmospheric co2 partial pressure (pa) - real(r8) :: c13o2(bounds%begp:bounds%endp) ! atmospheric c13o2 partial pressure (pa) real(r8) :: o2(bounds%begp:bounds%endp) ! atmospheric o2 partial pressure (pa) real(r8) :: svpts(bounds%begp:bounds%endp) ! saturation vapor pressure at t_veg (pa) real(r8) :: eah(bounds%begp:bounds%endp) ! canopy air vapor pressure (pa) @@ -479,7 +478,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, forc_u => atm2lnd_inst%forc_u_grc , & ! Input: [real(r8) (:) ] atmospheric wind speed in east direction (m/s) forc_v => atm2lnd_inst%forc_v_grc , & ! Input: [real(r8) (:) ] atmospheric wind speed in north direction (m/s) forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) - forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc , & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) forc_po2 => atm2lnd_inst%forc_po2_grc , & ! Input: [real(r8) (:) ] partial pressure o2 (Pa) tc_ref2m => humanindex_inst%tc_ref2m_patch , & ! Output: [real(r8) (:) ] 2 m height surface air temperature (C) @@ -961,10 +959,6 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, co2(p) = forc_pco2(g) o2(p) = forc_po2(g) - if ( use_c13 ) then - c13o2(p) = forc_pc13o2(g) - end if - ! Initialize flux profile nmozsgn(p) = 0 @@ -1645,7 +1639,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, ! Determine total photosynthesis - call PhotosynthesisTotal(fn, filterp, & + call PhotosynthesisTotal(bounds, fn, filterp, & atm2lnd_inst, canopystate_inst, photosyns_inst) ! Calculate water use efficiency diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index b88fb170c7..098144446d 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -1,4 +1,4 @@ -module PhotosynthesisMod +module PhotosynthesisMod #include "shr_assert.h" @@ -21,7 +21,6 @@ module PhotosynthesisMod use decompMod , only : bounds_type, subgrid_level_patch use QuadraticMod , only : quadratic use pftconMod , only : pftcon - use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries, nsectors_c14 use atm2lndType , only : atm2lnd_type use CanopyStateType , only : canopystate_type use CNVegnitrogenstateType, only : cnveg_nitrogenstate_type @@ -1257,7 +1256,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & use clm_varcon , only : rgas, tfrz, spval use GridcellType , only : grc use clm_time_manager , only : get_step_size_real, is_near_local_noon - use clm_varctl , only : cnallocate_carbon_only + use clm_varctl , only : allocate_carbon_only use clm_varctl , only : lnc_opt, reduce_dayl_factor, vcmax_opt use pftconMod , only : nbrdlf_dcd_tmp_shrub @@ -1642,7 +1641,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & if (.not. use_cn) then vcmax25top = vcmax25top * fnitr(patch%itype(p)) else - if ( CNAllocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) + if ( Allocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) end if else if (vcmax_opt == 3) then vcmax25top = ( i_vcad(patch%itype(p)) + s_vcad(patch%itype(p)) * lnc(p) ) * dayl_factor(p) @@ -2063,12 +2062,15 @@ subroutine Photosynthesis ( bounds, fn, filterp, & end subroutine Photosynthesis !------------------------------------------------------------------------------ - subroutine PhotosynthesisTotal (fn, filterp, & + subroutine PhotosynthesisTotal (bounds, fn, filterp, & atm2lnd_inst, canopystate_inst, photosyns_inst) ! ! Determine total photosynthesis ! + use CIsoAtmTimeseriesMod, only : C14BombSpike, C13TimeSeries + use CIsoAtmTimeseriesMod, only : rc13_atm_grc, rc14_atm_grc ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds integer , intent(in) :: fn ! size of pft filter integer , intent(in) :: filterp(fn) ! patch filter type(atm2lnd_type) , intent(in) :: atm2lnd_inst @@ -2078,13 +2080,10 @@ subroutine PhotosynthesisTotal (fn, filterp, & ! !LOCAL VARIABLES: integer :: f,fp,p,l,g ! indices - real(r8) :: rc14_atm(nsectors_c14), rc13_atm - integer :: sector_c14 !----------------------------------------------------------------------- associate( & forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) - forc_pc13o2 => atm2lnd_inst%forc_pc13o2_grc , & ! Input: [real(r8) (:) ] partial pressure c13o2 (Pa) forc_po2 => atm2lnd_inst%forc_po2_grc , & ! Input: [real(r8) (:) ] partial pressure o2 (Pa) laisun => canopystate_inst%laisun_patch , & ! Input: [real(r8) (:) ] sunlit leaf area @@ -2114,19 +2113,10 @@ subroutine PhotosynthesisTotal (fn, filterp, & fpsn_wp => photosyns_inst%fpsn_wp_patch & ! Output: [real(r8) (:) ] product-limited photosynthesis (umol CO2 /m**2 /s) ) - if ( use_c14 ) then - if (use_c14_bombspike) then - call C14BombSpike(rc14_atm) - else - rc14_atm(:) = c14ratio - end if - end if - - if ( use_c13 ) then - if (use_c13_timeseries) then - call C13TimeSeries(rc13_atm) - end if - end if + ! Get the current C13/C14 ratio in the atmosphere from timeseries data or the fixed values + ! These calls fill the data: rc13_atm_grc and rc14_atm_grc + if ( use_c14 ) call C14BombSpike(bounds) + if ( use_c13 ) call C13TimeSeries(bounds, atm2lnd_inst) do f = 1, fn p = filterp(f) @@ -2141,11 +2131,7 @@ subroutine PhotosynthesisTotal (fn, filterp, & if (use_cn) then if ( use_c13 ) then - if (use_c13_timeseries) then - rc13_canair(p) = rc13_atm - else - rc13_canair(p) = forc_pc13o2(g)/(forc_pco2(g) - forc_pc13o2(g)) - endif + rc13_canair(p) = rc13_atm_grc(g) rc13_psnsun(p) = rc13_canair(p)/alphapsnsun(p) rc13_psnsha(p) = rc13_canair(p)/alphapsnsha(p) c13_psnsun(p) = psnsun(p) * (rc13_psnsun(p)/(1._r8+rc13_psnsun(p))) @@ -2157,19 +2143,10 @@ subroutine PhotosynthesisTotal (fn, filterp, & endif if ( use_c14 ) then - ! determine latitute sector for radiocarbon bomb spike inputs - if ( grc%latdeg(g) .ge. 30._r8 ) then - sector_c14 = 1 - else if ( grc%latdeg(g) .ge. -30._r8 ) then - sector_c14 = 2 - else - sector_c14 = 3 - endif - - rc14_canair(p) = rc14_atm(sector_c14) + rc14_canair(p) = rc14_atm_grc(g) - c14_psnsun(p) = rc14_atm(sector_c14) * psnsun(p) - c14_psnsha(p) = rc14_atm(sector_c14) * psnsha(p) + c14_psnsun(p) = rc14_atm_grc(g) * psnsun(p) + c14_psnsha(p) = rc14_atm_grc(g) * psnsha(p) endif end if @@ -2750,7 +2727,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & use clm_varcon , only : rgas, tfrz, rpi, spval use GridcellType , only : grc use clm_time_manager , only : get_step_size_real, is_near_local_noon - use clm_varctl , only : cnallocate_carbon_only + use clm_varctl , only : allocate_carbon_only use clm_varctl , only : lnc_opt, reduce_dayl_factor, vcmax_opt use clm_varpar , only : nlevsoi use pftconMod , only : nbrdlf_dcd_tmp_shrub @@ -3267,7 +3244,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & if (.not. use_cn) then vcmax25top = vcmax25top * fnitr(patch%itype(p)) else - if ( CNAllocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) + if ( Allocate_Carbon_only() ) vcmax25top = vcmax25top * fnitr(patch%itype(p)) end if else if (vcmax_opt == 3) then vcmax25top = ( i_vcad(patch%itype(p)) + s_vcad(patch%itype(p)) * lnc(p) ) * dayl_factor(p) diff --git a/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 new file mode 100644 index 0000000000..174fa3554e --- /dev/null +++ b/src/cpl/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -0,0 +1,269 @@ +module CTSMForce2DStreamBaseType + +! +! Description: +! +! Base module to handle 2D streams in CTSM. Specific streams extend this object +! for the details needed to handle a specific stream file. +! +! Having this base type allows the ESMF specific streams implementation to be isolated +! from the CTSM code. This allows the streams code this is based on to change in one place. +! It also makes it easier to unit-test extensions of this type as they become pretty standard +! CTSM code and there is a unit-tester stub for this code. +! + +#include "shr_assert.h" + + use ESMF, only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU + use dshr_strdata_mod , only : shr_strdata_type + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL + use clm_varctl , only : iulog + use spmdMod , only : masterproc, mpicom, iam + use abortutils , only : endrun + use decompMod , only : bounds_type + use clm_varctl, only : FL => fname_len + + implicit none + private + + !----------------------------------------------------------------------- + ! Base 2D streams type + !----------------------------------------------------------------------- + type, abstract, public :: ctsm_force_2DStream_base_type + private + type(shr_strdata_type) :: sdat ! Stream data type + character(len=FL) :: stream_filename ! The stream data filename (also in sdat) + character(len=CL) :: stream_name ! The stream name (also in sdat) + contains + + ! PUBLIC METHODS + procedure(Init_interface) , public, deferred :: Init ! Initiale the extended type + procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams + procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method + procedure, public, non_overridable :: CleanBase ! Clean method for the base type + procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure, public :: GetPtr1D ! Get pointer to the 1D data array + procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into CTSM data + + end type ctsm_force_2DStream_base_type + !----------------------------------------------------------------------- + + !----------------------------------------------------------------------- + ! Interfaces that will be deferred to the extended type + !----------------------------------------------------------------------- + abstract interface + + !----------------------------------------------------------------------- + + subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + year_first, year_last, model_year_align ) + ! Description: + ! + ! Initialize the specific stream type that extends the base type + ! Normally the extended type will call the InitBase as well as doing other initialization needed + ! + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + end subroutine Init_interface + + !----------------------------------------------------------------------- + + subroutine Clean_interface(this) + ! Description: + ! Clean up any memory allocated in the specific stream type that extends the base type + ! Normally the extended type will call the CleanBase method as well as other things needed. + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + end subroutine Clean_interface + + !----------------------------------------------------------------------- + + subroutine Interp_interface(this, bounds) + ! Description: + ! Get the current time data from the streams and put it into the data of the extension. + ! What this looks like may vary with the streams extension, but in general it will use + ! The GetPtr1D method to get the streams data. + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + end subroutine Interp_interface + + end interface + !----------------------------------------------------------------------- + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + + !----------------------------------------------------------------------- + contains + !----------------------------------------------------------------------- + + !----------------------------------------------------------------------- + + subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! + ! Description: + ! + ! Initialization of the base type. Extended types will normally call this as part of their initialization. + ! + ! Uses: + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline + use decompMod , only : bounds_level_proc + use shr_log_mod , only : errMsg => shr_log_errMsg + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + ! Local variables + integer, parameter :: offset = 0 ! time offset in seconds of stream data + integer :: rc ! error return code + + ! Some error checking... + SHR_ASSERT( bounds%level == bounds_level_proc, "InitBase should have a processor bounds, so we can do some checking"//errMsg( sourcefile, __LINE__) ) + SHR_ASSERT( bounds%begg == 1, "Make sure the starting bounds index is 1 so we know the mapping to gridcells is correct"//errMsg( sourcefile, __LINE__) ) + if ( len(fldfilename) >= FL )then + call endrun( 'stream field filename is too long:'//trim(fldfilename), file=sourcefile, line=__LINE__ ) + end if + this%stream_filename = fldfilename + this%stream_name = name + call shr_strdata_init_from_inline(this%sdat, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock,& + model_mesh = mesh, & + stream_meshfile = trim(meshfile), & + stream_lev_dimname = 'null', & + stream_mapalgo = mapalgo, & + stream_filenames = (/trim(fldfilename)/), & + stream_fldlistFile = varnames, & + stream_fldListModel = varnames, & + stream_yearFirst = year_first, & + stream_yearLast = year_last, & + stream_yearAlign = model_year_align, & + stream_offset = offset, & + stream_taxmode = taxmode, & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = tintalgo, & + stream_name = name, & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + write(iulog,*) ' Streams initialization failing for ', trim(name), ' stream file = ', trim(fldfilename) + call endrun( 'CTSM forcing Streams initialization failing', file=sourcefile, line=__LINE__ ) + end if + end subroutine InitBase + + !----------------------------------------------------------------------- + + subroutine CleanBase( this ) + ! Description: + ! Clean up any memory in the base type as needed. + ! Normally types that extend this base type will call this as part of their clean operation + ! + ! Arguments: + class(ctsm_force_2DStream_base_type) , intent(inout) :: this + + integer :: ierr ! error code + + ! Currently no data to deallocate other than the stream data type + ! The stream data type doesn't have a clean method right now + ! So doing a few things manually here + end subroutine CleanBase + + !----------------------------------------------------------------------- + + subroutine Advance(this) + ! + ! Description: + ! + ! Advance the stream to the current time-step + ! + ! Uses: + use clm_time_manager , only : get_curr_date + use dshr_strdata_mod , only : shr_strdata_advance + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + ! !LOCAL VARIABLES: + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + integer :: rc ! Error return code + + ! Advance sdat stream + call get_curr_date(year, mon, day, sec) + mcdate = year*10000 + mon*100 + day + call shr_strdata_advance(this%sdat, ymd=mcdate, tod=sec, logunit=iulog, istr='CTSMForce2DStreamBase', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + write(iulog,*) ' Streams advance failing for ', trim(this%stream_name), ' stream file = ', trim(this%stream_filename) + call endrun( 'CTSM forcing Streams advance failing', file=sourcefile, line=__LINE__ ) + end if + end subroutine Advance + + !----------------------------------------------------------------------- + + subroutine GetPtr1D(this, fldname, dataptr1d) + ! + ! Description: + ! + ! Get the pointer to the 1D data array for the given field name + ! Normally stream extensions will use this in the Interp method to + ! save the stream data locally. + ! + ! Uses: + use dshr_methods_mod , only : dshr_fldbun_getfldptr + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + character(*), intent(in) :: fldname ! field name to get pointer for + real(r8), pointer :: dataptr1d(:) ! Pointer to the 1D data + + ! Local variables + integer :: rc ! error return code + + ! Get pointer for stream data that is time and spatially interpolated to model time and grid + call dshr_fldbun_getFldPtr(this%sdat%pstrm(1)%fldbun_model, fldname=fldname, fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=sourcefile)) then + call endrun( 'Error getting field pointer for '//trim(fldname)//' from stream data', file=sourcefile, line=__LINE__ ) + end if + + end subroutine GetPtr1D + + !----------------------------------------------------------------------- + +end module CTSMForce2DStreamBaseType diff --git a/src/cpl/share_esmf/FireDataBaseType.F90 b/src/cpl/share_esmf/FireDataBaseType.F90 index fd8b140c95..aa9395b770 100644 --- a/src/cpl/share_esmf/FireDataBaseType.F90 +++ b/src/cpl/share_esmf/FireDataBaseType.F90 @@ -338,6 +338,7 @@ subroutine hdm_interp( this, bounds) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%forc_hdm(g) = dataptr1d(ig) end do @@ -506,6 +507,7 @@ subroutine lnfm_interp(this, bounds ) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%forc_lnfm(g) = dataptr1d(ig) end do diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index afc65f2ece..158c75e434 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -1,5 +1,6 @@ module PrigentRoughnessStreamType +#include "shr_assert.h" !----------------------------------------------------------------------- ! !DESCRIPTION: @@ -151,6 +152,7 @@ subroutine Init(this, bounds, NLFilename) ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) this%prigent_rghn(g) = dataptr1d(ig) end do diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 591476e59e..df48ef5e98 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -480,6 +480,7 @@ subroutine cropcal_advance( bounds ) do g = begg,endg ig = ig+1 g_to_ig(g) = ig + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) end do end if diff --git a/src/cpl/share_esmf/ndepStreamMod.F90 b/src/cpl/share_esmf/ndepStreamMod.F90 index f8edd96ffc..860dd35641 100644 --- a/src/cpl/share_esmf/ndepStreamMod.F90 +++ b/src/cpl/share_esmf/ndepStreamMod.F90 @@ -1,5 +1,7 @@ module ndepStreamMod +#include "shr_assert.h" + !----------------------------------------------------------------------- ! !DESCRIPTION: ! Contains methods for reading in nitrogen deposition data file @@ -256,12 +258,14 @@ subroutine ndep_interp(bounds, atm2lnd_inst) dayspyr = get_curr_days_per_year( ) do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) atm2lnd_inst%forc_ndep_grc(g) = dataptr1d(ig) / (secspday * dayspyr) end do else ig = 0 do g = bounds%begg,bounds%endg ig = ig+1 + SHR_ASSERT_FL( ig == g, sourcefile, __LINE__ ) atm2lnd_inst%forc_ndep_grc(g) = dataptr1d(ig) end do end if diff --git a/src/fates b/src/fates index c1dfc21c50..5bb36cba29 160000 --- a/src/fates +++ b/src/fates @@ -1 +1 @@ -Subproject commit c1dfc21c505b5c8b29d4592b7d4a5e058239f6fb +Subproject commit 5bb36cba29b95295aca5aedc13f348ef1e001b99 diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 1c26b55cfd..4530fda860 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -157,6 +157,7 @@ subroutine initialize2(ni,nj, currtime) use clm_time_manager , only : get_curr_date, get_nstep, advance_timestep use clm_time_manager , only : timemgr_init, timemgr_restart_io, timemgr_restart, is_restart use CIsoAtmTimeseriesMod , only : C14_init_BombSpike, use_c14_bombspike, C13_init_TimeSeries, use_c13_timeseries + use CIsoAtmTimeseriesMod , only : CIsoAtmReadNML use DaylengthMod , only : InitDaylength use dynSubgridDriverMod , only : dynSubgrid_init use dynConsBiogeophysMod , only : dyn_hwcontent_set_baselines @@ -475,11 +476,12 @@ subroutine initialize2(ni,nj, currtime) ! Also do this for FATES see below call SatellitePhenologyInit(bounds_proc) end if - if ( use_c14 .and. use_c14_bombspike ) then - call C14_init_BombSpike() + if ( use_c13 .or. use_c14 ) call CIsoAtmReadNML( NLFilename ) + if ( use_c14 ) then + call C14_init_BombSpike( bounds_proc ) end if - if ( use_c13 .and. use_c13_timeseries ) then - call C13_init_TimeSeries() + if ( use_c13 ) then + call C13_init_TimeSeries( bounds_proc ) end if else ! FATES OR Satellite phenology diff --git a/src/main/clm_varcon.F90 b/src/main/clm_varcon.F90 index 985660142a..234b89c797 100644 --- a/src/main/clm_varcon.F90 +++ b/src/main/clm_varcon.F90 @@ -131,23 +131,23 @@ module clm_varcon !!! C13 real(r8), public, parameter :: preind_atm_del13c = -6.0_r8 ! preindustrial value for atmospheric del13C - real(r8), public, parameter :: preind_atm_ratio = SHR_CONST_PDB + (preind_atm_del13c * SHR_CONST_PDB)/1000.0_r8 ! 13C/12C + real(r8), private, parameter :: preind_atm_ratio = SHR_CONST_PDB + (preind_atm_del13c * SHR_CONST_PDB)/1000.0_r8 ! 13C/12C real(r8), public :: c13ratio = preind_atm_ratio/(1.0_r8+preind_atm_ratio) ! 13C/(12+13)C preind atmosphere ! typical del13C for C3 photosynthesis (permil, relative to PDB) - real(r8), public, parameter :: c3_del13c = -28._r8 + real(r8), private, parameter :: c3_del13c = -28._r8 ! typical del13C for C4 photosynthesis (permil, relative to PDB) - real(r8), public, parameter :: c4_del13c = -13._r8 + real(r8), private, parameter :: c4_del13c = -13._r8 ! isotope ratio (13c/12c) for C3 photosynthesis - real(r8), public, parameter :: c3_r1 = SHR_CONST_PDB + ((c3_del13c*SHR_CONST_PDB)/1000._r8) + real(r8), private, parameter :: c3_r1 = SHR_CONST_PDB + ((c3_del13c*SHR_CONST_PDB)/1000._r8) ! isotope ratio (13c/[12c+13c]) for C3 photosynthesis real(r8), public, parameter :: c3_r2 = c3_r1/(1._r8 + c3_r1) ! isotope ratio (13c/12c) for C4 photosynthesis - real(r8), public, parameter :: c4_r1 = SHR_CONST_PDB + ((c4_del13c*SHR_CONST_PDB)/1000._r8) + real(r8), private, parameter :: c4_r1 = SHR_CONST_PDB + ((c4_del13c*SHR_CONST_PDB)/1000._r8) ! isotope ratio (13c/[12c+13c]) for C4 photosynthesis real(r8), public, parameter :: c4_r2 = c4_r1/(1._r8 + c4_r1) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 9a0d2901b9..83133acf2b 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -11,8 +11,8 @@ module clm_varctl ! !PUBLIC MEMBER FUNCTIONS: implicit none public :: clm_varctl_set ! Set variables - public :: cnallocate_carbon_only_set - public :: cnallocate_carbon_only + public :: allocate_carbon_only_set + public :: allocate_carbon_only ! private save @@ -585,15 +585,15 @@ subroutine clm_varctl_set( caseid_in, ctitle_in, brnch_retain_casename_in, & end subroutine clm_varctl_set - ! Set module carbon_only flag - subroutine cnallocate_carbon_only_set(carbon_only_in) + ! Set module carbon_only flag (applies to both CN and FATES) + subroutine allocate_carbon_only_set(carbon_only_in) logical, intent(in) :: carbon_only_in carbon_only = carbon_only_in - end subroutine cnallocate_carbon_only_set + end subroutine allocate_carbon_only_set - ! Get module carbon_only flag - logical function CNAllocate_Carbon_only() - cnallocate_carbon_only = carbon_only - end function CNAllocate_Carbon_only + ! Get module carbon_only flag (applies to both CN and FATES) + logical function Allocate_Carbon_only() + allocate_carbon_only = carbon_only + end function Allocate_Carbon_only end module clm_varctl diff --git a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 index 57bc82984e..041f6ee740 100644 --- a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 @@ -76,7 +76,7 @@ subroutine readParams ( ncid ) type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id ! ! !LOCAL VARIABLES: - character(len=32) :: subname = 'CNAllocParamsType' + character(len=32) :: subname = 'readParams' character(len=100) :: errCode = '-Error reading in parameters file:' logical :: readv ! has variable been read in or not real(r8) :: tempr ! temporary to read in parameter @@ -130,7 +130,7 @@ subroutine SoilBiogeochemCompetitionInit ( bounds) ! !USES: use clm_varcon , only: secspday use clm_time_manager, only: get_step_size_real - use clm_varctl , only: iulog, cnallocate_carbon_only_set + use clm_varctl , only: iulog, allocate_carbon_only_set use shr_infnan_mod , only: nan => shr_infnan_nan, assignment(=) ! ! !ARGUMENTS: @@ -160,7 +160,7 @@ subroutine SoilBiogeochemCompetitionInit ( bounds) errMsg(sourcefile, __LINE__)) end select - call cnallocate_carbon_only_set(carbon_only) + call allocate_carbon_only_set(carbon_only) end subroutine SoilBiogeochemCompetitionInit @@ -175,7 +175,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu soilbiogeochem_nitrogenflux_inst,canopystate_inst) ! ! !USES: - use clm_varctl , only: cnallocate_carbon_only, iulog + use clm_varctl , only: allocate_carbon_only, iulog use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions use clm_varpar , only: i_cop_mic, i_oli_mic use clm_varcon , only: nitrif_n2o_loss_frac @@ -338,7 +338,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu fpi_vr(c,j) = 1.0_r8 actual_immob_vr(c,j) = potential_immob_vr(c,j) sminn_to_plant_vr(c,j) = plant_ndemand(c) * nuptake_prof(c,j) - else if ( cnallocate_carbon_only()) then !.or. & + else if ( allocate_carbon_only()) then !.or. & ! this code block controls the addition of N to sminn pool ! to eliminate any N limitation, when Carbon_Only is set. This lets the ! model behave essentially as a carbon-only model, but with the @@ -729,7 +729,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,nu ! eliminate N limitations, so there is still a diagnostic quantity ! that describes the degree of N limitation at steady-state. - if ( cnallocate_carbon_only()) then !.or. & + if ( allocate_carbon_only()) then !.or. & if ( fpi_no3_vr(c,j) + fpi_nh4_vr(c,j) < 1._r8 ) then fpi_nh4_vr(c,j) = 1.0_r8 - fpi_no3_vr(c,j) supplement_to_sminn_vr(c,j) = (potential_immob_vr(c,j) & diff --git a/src/unit_test_stubs/share_esmf/CMakeLists.txt b/src/unit_test_stubs/share_esmf/CMakeLists.txt index 368601dcc8..c68bb17a98 100644 --- a/src/unit_test_stubs/share_esmf/CMakeLists.txt +++ b/src/unit_test_stubs/share_esmf/CMakeLists.txt @@ -1,6 +1,7 @@ list(APPEND clm_sources ExcessIceStreamType.F90 FireDataBaseType.F90 + CTSMForce2DStreamBaseType.F90 laiStreamMod.F90 PrigentRoughnessStreamType.F90 ZenderSoilErodStreamType.F90 diff --git a/src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 b/src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 new file mode 100644 index 0000000000..59e6a4d551 --- /dev/null +++ b/src/unit_test_stubs/share_esmf/CTSMForce2DStreamBaseType.F90 @@ -0,0 +1,120 @@ +module CTSMForce2DStreamBaseType + + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL + use abortutils , only : endrun + use decompMod , only : bounds_type + use clm_varctl, only : FL => fname_len + + implicit none + private + + type, abstract, public :: ctsm_force_2DStream_base_type + private + character(len=FL) :: stream_filename ! The stream data filename (also in sdat) + character(len=CL) :: stream_name ! The stream name (also in sdat) + contains + + ! PUBLIC METHODS + procedure(Init_interface) , public, deferred :: Init + procedure, public, non_overridable :: InitBase ! Initialize and read data in the streams, , store the g_to_ig index array + procedure(Clean_interface), public, deferred :: Clean ! Clean and deallocate the object class method + procedure, public, non_overridable :: CleanBase ! Clean method for the base type + procedure, public, non_overridable :: Advance ! Advance the streams data to the current model date + procedure, public :: GetPtr1D ! Get pointer to the 1D data array + procedure(Interp_interface), public, deferred :: Interp ! method in extensions to turn stream data into output data + + end type ctsm_force_2DStream_base_type + + abstract interface + + subroutine Init_interface( this, bounds, fldfilename, meshfile, mapalgo, tintalgo, taxmode, & + year_first, year_last, model_year_align ) + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + end subroutine Init_interface + + subroutine Clean_interface(this) + ! Uses: + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + end subroutine Clean_interface + + subroutine Interp_interface(this, bounds) + ! Uses: + use decompMod , only : bounds_type + import :: ctsm_force_2DStream_base_type + ! + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + end subroutine Interp_interface + + end interface + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + + contains + + subroutine InitBase( this, bounds, varnames, fldfilename, meshfile, mapalgo, tintalgo, taxmode, name, & + year_first, year_last, model_year_align ) + ! Uses: + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + character(*), intent(in) :: varnames(:) ! variable names to read from stream file + character(*), intent(in) :: fldfilename ! stream data filename (full pathname) (single file) + ! NOTE: fldfilename could be expanded to an array if needed, but currently we only have one file + character(*), intent(in) :: meshfile ! full pathname to stream mesh file (none for global data) + character(*), intent(in) :: mapalgo ! stream mesh -> model mesh mapping type + character(*), intent(in) :: tintalgo ! time interpolation algorithm + character(*), intent(in) :: taxMode ! time axis mode + character(*), intent(in) :: name ! name of stream + integer, intent(in) :: year_first ! first year to use + integer, intent(in) :: year_last ! last year to use + integer, intent(in) :: model_year_align ! align yearFirst with this model year + + end subroutine InitBase + + subroutine CleanBase( this ) + class(ctsm_force_2DStream_base_type) , intent(inout) :: this + + call endrun('CTSMForce2DStreamBaseType: CleanBase method not implemented in stub') + + end subroutine CleanBase + + subroutine Advance(this) + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + + call endrun('CTSMForce2DStreamBaseType: Advance method not implemented in stub') + + end subroutine Advance + + subroutine GetPtr1D(this, fldname, dataptr1d) + ! Get the pointer to the 1D data array for the given field name + ! Uses: + ! Arguments: + class(ctsm_force_2DStream_base_type), intent(inout) :: this + character(*), intent(in) :: fldname ! field name to get pointer for + real(r8), pointer :: dataptr1d(:) ! Pointer to the 1D data + + end subroutine GetPtr1D + +end module CTSMForce2DStreamBaseType diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index da43b30b18..81f8a78b8e 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -80,6 +80,7 @@ module CLMFatesInterfaceMod use clm_varctl , only : use_lch4 use clm_varctl , only : fates_history_dimlevel use clm_varctl , only : nsrest, nsrBranch + use clm_varctl , only : Allocate_Carbon_only use clm_varcon , only : tfrz use clm_varcon , only : spval use clm_varcon , only : denice @@ -474,23 +475,6 @@ subroutine CLMFatesGlobals2() end if call set_fates_ctrlparms('use_tree_damage',ival=pass_tree_damage) - ! These may be in a non-limiting status (ie when supplements) - ! are added, but they are always allocated and cycled non-the less - ! FATES may want to interact differently with other models - ! that don't even have these arrays allocated. - ! FATES also checks that if NO3 is cycled in ELM, then - ! any plant affinity parameters are checked. - - if(use_nitrif_denitrif) then - call set_fates_ctrlparms('nitrogen_spec',ival=1) - else - call set_fates_ctrlparms('nitrogen_spec',ival=2) - end if - - ! Phosphorus is not tracked in CLM - call set_fates_ctrlparms('phosphorus_spec',ival=0) - - ! Pass spitfire mode values call set_fates_ctrlparms('spitfire_mode',ival=fates_spitfire_mode) call set_fates_ctrlparms('sf_nofire_def',ival=no_fire) @@ -1161,6 +1145,12 @@ subroutine dynamics_driv(this, nc, bounds_clump, & real(r8) :: s_node, smp_node ! local for relative water content and potential logical :: after_start_of_harvest_ts integer :: iharv + logical :: nitr_suppl ! true -> CLM is supplementing Nitrogen + logical, parameter :: phos_dummy_suppl = .true. ! true -> Phosphorus is NOT limited (i.e. supplemented) + ! This argument is needed for FATES + ! to specify if phosphorus is being + ! supplemented, Phosphorous is not limited in CLM + ! so we set it to TRUE !----------------------------------------------------------------------- ! --------------------------------------------------------------------------------- @@ -1342,10 +1332,16 @@ subroutine dynamics_driv(this, nc, bounds_clump, & end do + if(Allocate_Carbon_only())then + nitr_suppl = .true. + else + nitr_suppl = .false. + end if + ! Nutrient uptake fluxes have been accumulating with each short ! timestep, here, we unload them from the boundary condition ! structures into the cohort structures. - call UnPackNutrientAquisitionBCs(this%fates(nc)%sites, this%fates(nc)%bc_in) + call UnPackNutrientAquisitionBCs(this%fates(nc)%sites, this%fates(nc)%bc_in, nitr_suppl, phos_dummy_suppl) ! Distribute any seeds from neighboring gridcells into the current gridcell ! Global seed availability array populated by WrapGlobalSeedDispersal call diff --git a/src/utils/fileutils.F90 b/src/utils/fileutils.F90 index e7146468e7..f67cf91af0 100644 --- a/src/utils/fileutils.F90 +++ b/src/utils/fileutils.F90 @@ -21,6 +21,9 @@ module fileutils public :: getavu !Get next available Fortran unit number !----------------------------------------------------------------------- + character(len=*), parameter, private :: sourcefile = & + __FILE__ + contains !----------------------------------------------------------------------- @@ -76,7 +79,7 @@ subroutine getfil (fulpath, locfn, iflag) write(iulog,'(a)')'(GETFIL): full pathname is '//trim(fulpath) write(iulog,'(a)')'(GETFIL): local filename has zero length' end if - call shr_sys_abort + call shr_sys_abort('GETFIL: local filename has zero length', file=sourcefile, line=__LINE__) else if (masterproc) then write(iulog,'(a)')'(GETFIL): attempting to find local file ',trim(locfn) @@ -105,7 +108,7 @@ subroutine getfil (fulpath, locfn, iflag) write(iulog,'(a)')'(GETFIL): failed getting file from full path: '//fulpath end if if (iflag==0) then - call shr_sys_abort ('GETFIL: FAILED to get '//trim(fulpath)) + call shr_sys_abort ('GETFIL: FAILED to get '//trim(fulpath), file=sourcefile, line=__LINE__) else RETURN endif @@ -131,7 +134,7 @@ subroutine opnfil (locfn, iun, form) if (len_trim(locfn) == 0) then write(iulog,*)'(OPNFIL): local filename has zero length' - call shr_sys_abort + call shr_sys_abort('OPNFIL: local filename has zero length', file=sourcefile, line=__LINE__) endif if (form=='u' .or. form=='U') then ft = 'unformatted' @@ -142,7 +145,7 @@ subroutine opnfil (locfn, iun, form) if (ioe /= 0) then write(iulog,*)'(OPNFIL): failed to open file ',trim(locfn), & & ' on unit ',iun,' ierr=',ioe - call shr_sys_abort + call shr_sys_abort('OPNFIL: failed to open '//trim(locfn), rc=ioe, file=sourcefile, line=__LINE__) else if ( masterproc )then write(iulog,*)'(OPNFIL): Successfully opened file ',trim(locfn), & & ' on unit= ',iun From 560ad747f6f267b9544588c58a7e907f696bfaa8 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 14:23:11 -0700 Subject: [PATCH 098/112] User's Guide updates about splitting hX to hXa and hXi files --- .../running-single-points/supported-tower-sites.rst | 4 ++-- .../Running-with-custom-crop-calendars.rst | 6 +++--- .../setting-up-and-running-a-case/history_fields_fates.rst | 2 ++ .../history_fields_nofates.rst | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/source/users_guide/running-single-points/supported-tower-sites.rst b/doc/source/users_guide/running-single-points/supported-tower-sites.rst index 3e080a3ee1..db0e8e83fb 100644 --- a/doc/source/users_guide/running-single-points/supported-tower-sites.rst +++ b/doc/source/users_guide/running-single-points/supported-tower-sites.rst @@ -60,9 +60,9 @@ To run CTSM at a NEON site, change directories to where the run_tower tool is lo When a simulation completes, the data are stored in the archive directory under ``CTSM/tools/site_and_regional/archive``. In this directory you will find files that include data for every day of the simulation, as well as files that average model variables monthly. The output file names are automatically generated and are composed of the simulation name, which includes the site name, type of simulation (eg, ``transient``), and the date of simulated data. The tower simulations generate two types of files: -1) ``h0`` Variables that are averaged monthly. One file is available for every month of the simulation. These files include hundreds of variables. +1) ``h0a`` Variables that are averaged monthly. One file is available for every month of the simulation. These files include hundreds of variables. -2) ``h1`` Variables that are recorded every 30 minutes. Values are aggregated into one file for each day of the simulation. Each file includes 48 data points for selected variables. +2) ``h1a`` Variables that are recorded every 30 minutes. Values are aggregated into one file for each day of the simulation. Each file includes 48 data points for selected variables. ========================================= PLUMBER Tower Single Point Simulations diff --git a/doc/source/users_guide/running-special-cases/Running-with-custom-crop-calendars.rst b/doc/source/users_guide/running-special-cases/Running-with-custom-crop-calendars.rst index 22009add51..b03e73066b 100644 --- a/doc/source/users_guide/running-special-cases/Running-with-custom-crop-calendars.rst +++ b/doc/source/users_guide/running-special-cases/Running-with-custom-crop-calendars.rst @@ -59,18 +59,18 @@ In a GDD-generating run, crops are planted on the specified sowing dates and are generate_crop_gdds = .true. use_mxmat = .false. - ! (h0) Save default variables monthly instead of daily to save space + ! (h0a) Save default variables monthly instead of daily to save space hist_nhtfrq = 0 hist_mfilt = 12 - ! (h1) Annual outputs for GDD generation + ! (h1i) Annual outputs for GDD generation hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV' hist_nhtfrq(2) = 17520 hist_mfilt(2) = 999 hist_type1d_pertape(2) = 'PFTS' hist_dov2xy(2) = .false. - ! (h2) Daily outputs for GDD generation + ! (h2a) Daily outputs for GDD generation hist_fincl3 = 'GDDACCUM', 'GDDHARV' hist_nhtfrq(3) = -24 hist_mfilt(3) = 365 diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst index 84caf92465..6c037f707d 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst @@ -3,6 +3,8 @@ CTSM History Fields (fates) ============================= CAUTION: Not all variables are relevant / present for all CTSM cases. +NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history fields labeled with h0a, h1a, ... + Key flags used in this CTSM case: use_cn = F use_crop = F diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst index 67868f75b1..a38b8a9c9b 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst @@ -3,6 +3,8 @@ CTSM History Fields (nofates) ============================= CAUTION: Not all variables are relevant / present for all CTSM cases. +NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history fields labeled with h0a, h1a, ... + Key flags used in this CTSM case: use_cn = T use_crop = T From 93b0dd44b65a13fb6aad966fac2041547c5f11d6 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 15:34:38 -0700 Subject: [PATCH 099/112] Additional User's Guide updates about splitting hX to hXa and hXi files --- .../customizing-the-clm-namelist.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst index a4333a2b29..f4c28da9a8 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst @@ -145,7 +145,9 @@ There are two ways to change the averaging of output history fields. The first i - ``X`` Maximum, over the output interval. - ``M`` Minimum, over the output interval. -The default averaging depends on the specific fields, but for most fields is an average. A sample user namelist ``user_nl_clm`` making the monthly output fields all averages (except ``TSOI`` for the first two streams and ``FIRE`` for the 5th stream), and adding auxiliary file streams for instantaneous (6-hourly), maximum (daily), minimum (daily), and average (daily). For some of the fields we diverge from the per-tape value given and customize to some different type of optimization. +The default averaging depends on the specific fields, but for most fields is an average. A sample user namelist ``user_nl_clm`` follows making the monthly output fields all averages (except ``TSOI``), and adding auxiliary file streams for instantaneous (6-hourly), maximum (daily), minimum (daily), and average (daily). For some of the fields we diverge from the per-tape value given and customize to some different type of averaging. + +.. note:: As of ctsm5.4, history files that used to be labeled with hX (where X is an integer from 0 to 4 in the example) will be labeled with hXi and hXa (as separate files) to indicate instantaneous versus non-instantaneous (average, etc.) files. The change intends to prevent confusion associated with the time corresponding to instantaneous history fields by now putting them on separate files than non-instantaneous fields. The separate instantaneous history files represent the exact time step when they were written and do not include a time_bounds variable. Conversely, non-instantaneous history files represent the period of their time_bounds variable. As a result, time data on non-instantaneous history files are now read correctly during post processing (e.g. by xarray). Special handling may still be needed for instantaneous history files, whose timestamps represent the date and time at the END of the history timestep. So, e.g., an instantaneous variable saved at the end of year 2023 will get the timestamp 2024-01-01 00:00:00. Example: user_nl_clm namelist with various ways to average history fields ------------------------------------------------------------------------------------- @@ -162,12 +164,12 @@ Example: user_nl_clm namelist with various ways to average history fields 'EFLX_LH_TOT', 'WT' hist_fincl5 = 'TSOI', 'TG', 'TV', 'FIRE:I', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' - hist_avgflag_pertape = 'A', 'I', 'X', 'M', 'A' + hist_avgflag_pertape = 'A', 'I', 'X', 'M', 'A' hist_nhtfrq = 0, -6, -24, -24, -24 -In the example we put the same list of fields on each of the tapes: soil-temperature, ground temperature, vegetation temperature, emitted longwave radiation, reflected solar radiation, sensible heat, total latent-heat, and total water storage. We also modify the soil temperature for the primary and secondary auxiliary tapes by outputting them for a maximum instead of the prescribed per-tape of average and instantaneous respectively. For the tertiary auxiliary tape we output ground temperature instantaneous instead of as a maximum, and for the fourth auxiliary tape we output vegetation temperature instantaneous instead of as a minimum. Finally, for the fifth auxiliary tapes we output ``FIRE`` instantaneously instead of as an average. +In the example we put the same list of fields on each of the tapes: soil-temperature, ground temperature, vegetation temperature, emitted longwave radiation, reflected solar radiation, sensible heat, total latent-heat, and total water storage. We also modify the soil temperature for the primary and secondary auxiliary tapes by outputting them for a maximum instead of the prescribed per-tape of average and instantaneous respectively. For the tertiary auxiliary tape we output ground temperature as instantaneous instead of as maximum, and for the fourth auxiliary tape we output vegetation temperature as instantaneous instead of as minimum. Finally, for the fifth auxiliary tapes we output ``FIRE`` as instantaneous instead of as average. -.. note:: We also use ``hist_empty_htapes`` as in the previous example, so we can list ONLY the fields that we want on the primary history tapes. +.. note:: We also use ``hist_empty_htapes`` as in the previous example, so we can list ONLY the fields that we want on the primary history tape. Outputting history files as a vector in order to analyze the plant function types within gridcells -------------------------------------------------------------------------------------------------- From b883127c536a5f81b8959f4dcd78bc1c6204012c Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 16:18:05 -0700 Subject: [PATCH 100/112] Updated WhatsNewInCTSM54 --- WhatsNewInCTSM5.4.md | 190 ++++++++++++++++++++++++++++--------------- 1 file changed, 126 insertions(+), 64 deletions(-) mode change 100644 => 100755 WhatsNewInCTSM5.4.md diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md old mode 100644 new mode 100755 index a36b8266c0..35806d6f13 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -1,90 +1,152 @@ # What's new in CTSM 5.4 (tag `ctsm5.4.0xx`) -## Purpose and description of changes since CTSM 5.3 (tag `ctsm5.3.021`) - -REMOVE THESE NOTES WHEN DONE AND READY TO RELEASE -- As of 2025/7/29 slevis has gone through the ChangeLog from cctsm5.3.022 to ctsm5.3.065 -- Ask for reviewers to browse/read this doc for accuracy, omissions, and redundancies -- For omissions, request contributions from the relevant developers - -### New features - -* Can now choose the CRUJRA2024 atmospheric driver data with clm6 and clm5 [PR \#2956](https://github.com/ESCOMP/ctsm/pull/2956)) -* Can now run PLUMBER towers, similar to the NEON tower capability ([issue \#1487](https://github.com/ESCOMP/CTSM/issues/1487)) -* New namelist variables `snow_thermal_cond_glc_method` and `snow_thermal_cond_lake_method` ([PR \#3072](https://https://github.com/ESCOMP/CTSM/pull/3072)) -* Potentially time-evolving `leafcn_target` replaces time-constant `leafcn`: the former is calculated as a function of the latter and can be time-evolving depending on new paramfile parameter `leafcn_co2_slope` ([PR \#1654](https://github.com/ESCOMP/ctsm/pull/1654) -* Easier, more flexible use of anomaly forcings for ISSP cases ([PR \#2686](https://github.com/ESCOMP/CTSM/pull/2686) [PR \#3212](https://github.com/ESCOMP/CTSM/pull/3212)) -* New equilibrium script in /tools/contrib for spectral element grids ([PR \#2991](https://github.com/ESCOMP/ctsm/pull/2991)) - -### Answer changes - -Changes to defaults for `clm6_0` physics: - -* Bytnerowicz is now the default nfix_method for clm6 ([PR \#2972](https://github.com/ESCOMP/ctsm/pull/2972)) -* New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000) resolutions? -* Updates to MEGAN for BVOCs ([PR \#3065](https://github.com/ESCOMP/CTSM/pull/3065) [PR \#3309](https://github.com/ESCOMP/CTSM/pull/3309)) +# Purpose and description of changes since CTSM 5.3 (tag `ctsm5.3.021`) + +## New features + +* New surface datasets from CMIP7 data including PFT and urban distributions, land use transitions, population density, and atmospheric C isotopes. These data are only available through the historical record (1850-2023), and + * are not available for future periods (presently known as SSP), + * nitrogen deposition is calculated by CAM-CHEM, and not yet available from CESM3, + * for future periods and N deposition we continue to use CMIP6 data from CESM2. +* Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR \\\#2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. +* Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue \\\#1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. +* New CLM\_CMIP\_ERA flag in env\_run.xml. Valid options are cmip7 and cmip6. Defaults to cmip7 except in compsets containing SSP for which it defaults to cmip6 because there are no future-period datasets yet available for CMIP7. +* Automatic, more flexible use of anomaly forcings for CMIP6 ISSP cases, which also use the cmip6 CLM\_CMIP\_ERA flag: [Documentation](https://escomp.github.io/CTSM/users_guide/running-special-cases/Running-with-anomaly-forcing.html) + +* Unsupported script that checks for spinup equilibrium in `tools/contrib/` for spectral element grids ([PR \\\#2991](https://github.com/ESCOMP/ctsm/pull/2991)). +* New paramfile tools that allow users to query and modify CLM parameter files ([documentation](https://escomp.github.io/CTSM/users_guide/using-clm-tools/paramfile-tools.html)) +* Optional time-evolving \`leafcn\_target\`. More under “Additional detail” below. +* New vertical movement scheme for soil nitrate, which is off by default (PR \\[\#2992](https://github.com/ESCOMP/CTSM/pull/2992)). +* Documentation improvements and new URL: https://escomp.github.io/CTSM/index.html. +* FATES: + * Grazing ([sci.1.81.0\_api.37.1.0](https://github.com/NGEET/fates/releases/tag/sci.1.81.0_api.37.1.0)). + * Johnson and Berry 2021 electron transport model ([sci.1.85.0\_api.40.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.85.0_api.40.0.0)). + * Managed Fire ([sci.1.87.0\_api.41.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.87.0_api.41.0.0)). + +## Answer changes + +Changes to defaults for \`clm6\` physics: + +* New CMIP7 surface and landuse timeseries datasets (see in Additional Details below). +* New namelist variables \`snow\_thermal\_cond\_glc\_method\` and \`snow\_thermal\_cond\_lake\_method\` ([PR \\\#3072](https://github.com/ESCOMP/CTSM/pull/3072)). Snow thermal conductivity uses Jordan1991 over glaciers to reduce Greenland melt rates by default and Sturm over land and lake land units. +* Bytnerowicz is now the default nfix\_method for clm6 (\[PR \\\#2972\]([https://github.com/ESCOMP/ctsm/pull/2972](https://github.com/ESCOMP/ctsm/pull/2972))) which revises the temperature function for nitrogen fixation, replacing the Houlton *et al.* function. +* Updates to MEGAN for BVOCs (\[PR \\\#3065\](https://github.com/ESCOMP/CTSM/pull/3065) [PR \\\#3309](https://github.com/ESCOMP/CTSM/pull/3309)). Removes dependence on soil moisture from clm6 physics. +* New model parameter values that were calibrated to improve carbon cycle representation with CRUJRA. +* New model parameter values that were calibrated to improve the fire model. Now using li2024 fire code. +* New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000\) resolutions. +* Change default for glcmec\_downscale\_longwave to FALSE for clm6 physics as turning off the LW downscaling improves the melt and runoff biases. +* See “Changes to FATES and the FATES parameter file” below. +* Namelist defaults change so that + * use\_c13/use\_c14 are on only for HistClm60Bgc compsets with CRUJRA2024 or CAM7 forcing; examples of when use\_c13/use\_c14 are now off include SSP and single-point compsets, as well as cases using older forcings, such as CAM6, GSWP3v1, Qian, and CRUv7 + * when use\_c13 or use\_c14 is on, turn on the corresponding time series file + * irrigation is on for transient cases (1850-2000, 1850-2100, but not for clm4\_5). Changes for all physics versions: -* Parameters updated: Added MIMICS parameter `mimics_fi`. ([PR \#2365](https://github.com/ESCOMP/CTSM/pull/2365)) -* FATES parameter file updated: ([PR \#2965](https://github.com/ESCOMP/CTSM/pull/2965) [PR \#2904](https://github.com/ESCOMP/CTSM/pull/2904) [PR \#1344](https://https://github.com/NGEET/fates/pull/1344) [PR \#3087](https://github.com/ESCOMP/CTSM/pull/3087)) -* New surface datasets and landuse timeseries files (see section below). - -### Heads up +* Parameters updated: Added MIMICS parameter \`mimics\_fi\` (fraction of litter inputs that bypass litter pools, directly contributing to SOM) and updated other MIMICS parameters (\[PR \\\#2365\]([https://github.com/ESCOMP/CTSM/pull/2365](https://github.com/ESCOMP/CTSM/pull/2365))) to remove NPP control on turnover, fix density dependent control on turnover, add litterfall fluxes that bypass litter pools and contribute directly to soil organic matter. +* FATES parameter file updated: ([PR \\\#2965](https://github.com/ESCOMP/CTSM/pull/2965), [PR \\\#2904](https://github.com/ESCOMP/CTSM/pull/2904), [PR \\\#1344](https://github.com/NGEET/fates/pull/1344), [PR \\\#3087](https://github.com/ESCOMP/CTSM/pull/3087)). See “FATES parameter file” section below for details. +* New surface datasets and landuse timeseries files (see “surface datasets” section below). -* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details below and in the PRs [\#2445](https://github.com/ESCOMP/ctsm/pull/2445) [\#117](https://github.com/ESCOMP/MOSART/pull/117) [\#61](https://github.com/ESCOMP/RTM/pull/61) and the correspondng issues. -* As of ctsm5.3.040, the new ctsm_pylib conda environment is incompatible with our tools from before ctsm5.3.040 and vice versa. If you have a ctsm_pylib conda environment installed from before ctsm5.3.040, keep that under a different name. We suggest the following command for doing this in a local copy of ctsm5.3.040 or later: +## Heads up -./py_env_create -r ctsm_pylib_old - -This first renames your existing ctsm_pylib to ctsm_pylib_old and then installs the Python 3.13.2 version as ctsm_pylib. If you are unsure whether you already have ctsm_pylib installed, use the same command regardless, as it will skip the rename step if necessary. +* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details in the “history files” section below and in the PRs \[\\\#2445\](https://github.com/ESCOMP/ctsm/pull/2445) \[\\\#117\](https://github.com/ESCOMP/MOSART/pull/117) \[\\\#61\](https://github.com/ESCOMP/RTM/pull/61) and the corresponding issues. +* Adding time to 1d weighting fields in transient simulations PR \\[\#3328](https://github.com/ESCOMP/CTSM/pull/3328) +* Regarding CMIP7 vs. CMIP6 inputs: + * We supply only CMIP7 C13/C14 isotope datasets, so these get used regardless of CLM\_CMIP\_ERA setting. + * We supply only CMIP7 population density with clm6 physics in non-SSP cases, because the fire model is calibrated to that; conversely, we supply only CMIP6 population density for pre-clm6 physics and for SSP cases. + * We supply only CESM2 nitrogen deposition (ndep), so this gets used regardless of CLM\_CMIP\_ERA setting. + * For DATM we supply only CMIP6 aerosols. + * For DATM we supply only CMIP6 CO2. +* Issue with DOUT\_S\_SAVE\_INTERIM\_REST [https://github.com/ESCOMP/CTSM/issues/3351](https://github.com/ESCOMP/CTSM/issues/3351) was fixed. +* As of ctsm5.3.040, the new ctsm\_pylib conda environment is incompatible with our tools from before ctsm5.3.040 and vice versa. More under “Additional detail” below. -Information about additional py_env_create options — including how to install a fresh copy of the old conda environment — is available as follows: - -./py_env_create --help +# Additional detail -## +## Changes related to history files -## Additional detail +(Note 1: The same information in this section applies to MOSART and RTM. +Note 2: The gist of the information in this section also appears in the CTSM User’s Guide here [https://escomp.github.io/CTSM/users\_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.html\#various-ways-to-change-history-output-averaging-flags](https://escomp.github.io/CTSM/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.html#various-ways-to-change-history-output-averaging-flags)) -### Changes related to history files +Following ctsm5.3.018 "Change history time to be the middle of the time bounds" and keeping CLM history consistent with CAM history, the CTSM5.4 change intends to prevent confusion associated with the time corresponding to instantaneous history fields by putting them on separate files than non-instantaneous fields. -(Note that the same information in this section applies to MOSART and RTM.) +The now separate instantaneous history files represent the exact time step when they were written and do not include a time\_bounds variable. Conversely, non-instantaneous history files represent the period of their time\_bounds variable. As a result, time data on non-instantaneous history files are now read correctly during post processing (e.g. by xarray). Special handling may still be needed for instantaneous history files, whose timestamps represent the date and time at the END of the history timestep. So, e.g., an instantaneous variable saved at the end of year 2023 will get the timestamp 2024-01-01 00:00:00. -Following ctsm5.3.018 "Change history time to be the middle of the time bounds" and to keep CLM history consistent with CAM history, the CTSM5.4 change intends to prevent confusion associated with the time corresponding to instantaneous history fields by putting them on separate files than non-instantaneous fields. The result is +Users will now see: -1) two history files per clm, mosart, and rtm history tape: - tape h0 becomes h0a and h0i - tape h1 becomes h1a and h1i - ... +1\) Two history files per clm, mosart, and rtm history tape: + tape h0 becomes h0a and h0i + tape h1 becomes h1a and h1i + ... tape hX becomes hXa and hXi -2) two history restart files per history restart tape: - rh0 becomes rh0a and rh0i - rh1 becomes rh1a and rh1i - ... +2\) Two history-restart files per history restart tape: + rh0 becomes rh0a and rh0i + rh1 becomes rh1a and rh1i + ... rhX becomes rhXa and rhXi -The CLM handles empty history (and corresponding history restart) files by not generating them, while rtm and mosart give an error. Instead of refactoring rtm and mosart to behave like the clm (considered out of scope), we have introduced one active instantaneous field in mosart and one in rtm to bypass the "empty file" error. - -### New surface datasets and landuse timeseries files ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx)) +The CLM handles empty history (and corresponding history-restart) files by not generating them, while rtm and mosart give an error. Instead of refactoring rtm and mosart to behave like the clm (considered out of scope), we have introduced one active instantaneous field in mosart and one in rtm to bypass the "empty file" error. + +## New surface datasets and landuse timeseries files (\[PR \\\#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) + +* Transient landuse timeseries files going back to 1700 made for f09 and 360x720 grids. +* New resolutions now supported: ne3np4.pg3, mpasa30, ne0np4.NATL.ne30x8 (\[PR \\\#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) +* Updates to input datasets (also referred to as raw datasets): + * PFT/LAI/soil-color raw datasets; now from the CMIP7 timeseries that ends in 2023 (Issues [\\\#2851](https://github.com/ESCOMP/CTSM/issues/2851)). + * Two fire datasets: crop fire peak month and population density (Issue + * \[\\\#2701\]([https://github.com/ESCOMP/CTSM/issues/2701](https://github.com/ESCOMP/CTSM/issues/2701)) \[\\\#3302\](https://github.com/ESCOMP/CTSM/issues/3302)). + * Transient (historical) urban datasets are now based on CMIP7 urban data, partitioned into TBD, HD, and MD classes in proportion to GaoOneill present day classification. + +## Changes to FATES and the FATES parameter file + +* See [HLM-FATES compatibility table](https://fates-users-guide.readthedocs.io/en/latest/user/release-tags-compat-table.html) in the FATES user’s guide for all FATES tags associated with CTSM tag updates +* FATES answer changing updates + * The default hydro solver is updated to 2D Picard from 1D Taylor ([ctsm5.3.027](https://github.com/ESCOMP/CTSM/releases/tag/ctsm5.3.027)) + * Simplified leaf sun-shade fraction for two-stream radiation ([sci.1.83.0\_api.39.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.83.0_api.39.0.0)) + * Default maximum canopy layer updated from 2 to 3 ([sci.1.87.1\_api.41.0.0](https://github.com/NGEET/fates/releases/tag/sci.1.87.1_api.41.0.0)) + * Various bug fixes (see compatibility table) +* FATES Parameter File Updates + * ctsm5.3.025 (API 37\) + * Adds pft-dependent btran model switches + * Adds parameters for land use grazing + * Updates the FATES z0mr turbulence parameters for consistency with CLM + * ctsm5.3.027 (API 38\) + * Migrates a number of global parameter file variables to the namelist + * Adds \`fates\_leaf\_fnps\` parameter for the electron transport model + * \`fates\_leaf\_theta\_cj\_c3\` and \`fates\_leaf\_theta\_cj\_c4\` depricated + * ctsm5.3.045 (API 40\) + * Changes to the default competitive exclusion parameter from probabilistic to rank-ordered sorting of cohorts by default + * Sets the logging default to clear cut + * Refactors the pft-specific phenology habit selection into a single parameter + * ctsm5.3.070 (API 41\) + * Add parameters for the managed fire feature addition + * Corrects the fates landuse crop pft to c3 cool grass + +## New ctsm\_pylib conda environment + +If you have a ctsm\_pylib conda environment installed from before ctsm5.3.040, you may want to keep that under a different name. We suggest the following command for doing this in a local copy of ctsm5.3.040 or later: + +```shell +./py_env_create -r ctsm_pylib_old +``` -* Transient landuse timeseries files going back to 1700 made for f09. -* Surface datasets now provided for the `xxxx` grid. ([PR \#xxxx](https://github.com/ESCOMP/CTSM/pull/xxxx), [issue \#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx)) -* Updates to input datasets: - * PFT/LAI/soil-color raw datasets; now from the xxxx timeseries that ends in 2023. (Issues [\#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx) - * Two fire datasets: crop fire peak month and population density. (Issue [\#xxxx](https://github.com/ESCOMP/CTSM/issues/xxxx)) +This first renames your existing ctsm\_pylib to ctsm\_pylib\_old and then installs the Python 3.13.2 version as ctsm\_pylib. If you are unsure whether you already have ctsm\_pylib installed, use the same command regardless, as it will skip the renaming step if necessary. -### Changes to FATES parameter file +Information about additional py\_env\_create options — including how to install a fresh copy of the old conda environment — is available as follows: - * +```shell +./py_env_create --help +``` -### Changes to rpointer files +## Potentially time-evolving \`leafcn\_target\` replaces time-constant \`leafcn\` +The former is calculated as a function of the latter and can be time-evolving depending on new paramfile parameter \`leafcn\_co2\_slope\` (\[PR \\\#1654\](https://github.com/ESCOMP/ctsm/pull/1654). The time-evolving effect defaults to off with \`leafcn\_co2\_slope\` \= 0 on the parameter file. -## Simulations supporting this release +# Simulations supporting this release by providing initial conditions -- f19 `Clm60Bgc` 16pft: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) -- f09 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) -- ne30 with `Clm60BgcCrop`: [https://github.com/NCAR/LMWG\_dev/issues/xxx](https://github.com/NCAR/LMWG_dev/issues/xxx) +* f19 \`Clm60BgcCruJra\` 16pft: https://github.com/NCAR/LMWG\_dev/issues/125 +* f09 with \`Clm60BgcCropCruJra\`: https://github.com/NCAR/LMWG\_dev/issues/124 +* ne30 with \`Clm60BgcCropCruJra\`: [https://github.com/NCAR/LMWG\_dev/issues/123](https://github.com/NCAR/LMWG_dev/issues/123) (123\_HIST\_popDens) +* ne30 SP https://github.com/NCAR/LMWG\_dev/issues/126 +* f09 SP https://github.com/NCAR/LMWG\_dev/issues/127 From a8b1afeccebaf90cb091b6032bede0e6c59cfa81 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 17:17:25 -0700 Subject: [PATCH 101/112] Minor improvements to documentation --- WhatsNewInCTSM5.4.md | 1 - .../setting-up-and-running-a-case/history_fields_fates.rst | 2 +- .../setting-up-and-running-a-case/history_fields_nofates.rst | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index 35806d6f13..d868d87321 100755 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -6,7 +6,6 @@ * New surface datasets from CMIP7 data including PFT and urban distributions, land use transitions, population density, and atmospheric C isotopes. These data are only available through the historical record (1850-2023), and * are not available for future periods (presently known as SSP), - * nitrogen deposition is calculated by CAM-CHEM, and not yet available from CESM3, * for future periods and N deposition we continue to use CMIP6 data from CESM2. * Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR \\\#2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. * Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue \\\#1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst index 6c037f707d..ea964fc433 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst @@ -3,7 +3,7 @@ CTSM History Fields (fates) ============================= CAUTION: Not all variables are relevant / present for all CTSM cases. -NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history fields labeled with h0a, h1a, ... +NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history files labeled with h0a, h1a, ... Key flags used in this CTSM case: use_cn = F diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst index a38b8a9c9b..5322ae79a2 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst @@ -3,7 +3,7 @@ CTSM History Fields (nofates) ============================= CAUTION: Not all variables are relevant / present for all CTSM cases. -NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history fields labeled with h0a, h1a, ... +NOTE: Instantaneous fields will appear in history files labeled with h0i, h1i, ... and non-instantaneous fields will appear in history files labeled with h0a, h1a, ... Key flags used in this CTSM case: use_cn = T From 060bd7c5a48c9ead38151ce069591e0833b27fea Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 17:51:48 -0700 Subject: [PATCH 102/112] Update README and default_data*.cfg files to refer to 5.4, not 5.3 --- README | 6 +++--- python/ctsm/test/testinputs/default_data.cfg | 4 ++-- python/ctsm/test/testinputs/default_data_gswp3.cfg | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README b/README index b752d07660..dc38231173 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ -$CTSMROOT/README 09/05/2024 +$CTSMROOT/README 11/24/2025 -Community Terrestrial Systems Model (CTSM) science version 5.3 series -- source code, tools, +Community Terrestrial Systems Model (CTSM) science version 5.4 series -- source code, tools, offline-build and test scripts. This gives you everything you need to run CTSM with CESM with the CMEPS driver and CDEPS data models to provide CRUJRA or GSWP3 forcing data (some older options also available) in place of a modeled atmosphere. @@ -76,7 +76,7 @@ README ------------------- This file README.md ---------------- File that displays on github under https::/github.com/ESCOMP/CTSM.git README.rst --------------- File that displays under the project in github README_GITFLEXIMOD.rst --- Information on how to work with git-fleximod for CTSM -WhatsNewInCTSM5.3.md ----- Overview document of the changes between ctsm5.2.0 and ctsm5.3.0 +WhatsNewInCTSM5.4.md ----- Overview document of the changes between ctsm5.3 and ctsm5.4 CODE_OF_CONDUCT.md ------- Code of Conduct for how to work with each other on the CTSM project Copyright ---------------- CESM Copyright file doc/UpdateChangeLog.pl --- Script to add documentation on a tag to the diff --git a/python/ctsm/test/testinputs/default_data.cfg b/python/ctsm/test/testinputs/default_data.cfg index 60c012561c..9d9a781947 100644 --- a/python/ctsm/test/testinputs/default_data.cfg +++ b/python/ctsm/test/testinputs/default_data.cfg @@ -15,14 +15,14 @@ precname = CLMCRUJRA2024.Precip tpqwname = CLMCRUJRA2024.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc diff --git a/python/ctsm/test/testinputs/default_data_gswp3.cfg b/python/ctsm/test/testinputs/default_data_gswp3.cfg index 09e1463eb2..e5eb46d681 100644 --- a/python/ctsm/test/testinputs/default_data_gswp3.cfg +++ b/python/ctsm/test/testinputs/default_data_gswp3.cfg @@ -15,14 +15,14 @@ precname = CLMGSWP3v1.Precip tpqwname = CLMGSWP3v1.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc From addc86e067f2127f3eb03b324f6cd0e7a6b2d98f Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 18:08:12 -0700 Subject: [PATCH 103/112] Corrections to the WhatsNew markdown file --- WhatsNewInCTSM5.4.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index d868d87321..248008ea74 100755 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -7,15 +7,15 @@ * New surface datasets from CMIP7 data including PFT and urban distributions, land use transitions, population density, and atmospheric C isotopes. These data are only available through the historical record (1850-2023), and * are not available for future periods (presently known as SSP), * for future periods and N deposition we continue to use CMIP6 data from CESM2. -* Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR \\\#2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. -* Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue \\\#1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. +* Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR \#2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. +* Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue \#1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. * New CLM\_CMIP\_ERA flag in env\_run.xml. Valid options are cmip7 and cmip6. Defaults to cmip7 except in compsets containing SSP for which it defaults to cmip6 because there are no future-period datasets yet available for CMIP7. * Automatic, more flexible use of anomaly forcings for CMIP6 ISSP cases, which also use the cmip6 CLM\_CMIP\_ERA flag: [Documentation](https://escomp.github.io/CTSM/users_guide/running-special-cases/Running-with-anomaly-forcing.html) -* Unsupported script that checks for spinup equilibrium in `tools/contrib/` for spectral element grids ([PR \\\#2991](https://github.com/ESCOMP/ctsm/pull/2991)). +* Unsupported script that checks for spinup equilibrium in `tools/contrib/` for spectral element grids ([PR \#2991](https://github.com/ESCOMP/ctsm/pull/2991)). * New paramfile tools that allow users to query and modify CLM parameter files ([documentation](https://escomp.github.io/CTSM/users_guide/using-clm-tools/paramfile-tools.html)) * Optional time-evolving \`leafcn\_target\`. More under “Additional detail” below. -* New vertical movement scheme for soil nitrate, which is off by default (PR \\[\#2992](https://github.com/ESCOMP/CTSM/pull/2992)). +* New vertical movement scheme for soil nitrate, which is off by default (PR [\#2992](https://github.com/ESCOMP/CTSM/pull/2992)). * Documentation improvements and new URL: https://escomp.github.io/CTSM/index.html. * FATES: * Grazing ([sci.1.81.0\_api.37.1.0](https://github.com/NGEET/fates/releases/tag/sci.1.81.0_api.37.1.0)). @@ -27,9 +27,9 @@ Changes to defaults for \`clm6\` physics: * New CMIP7 surface and landuse timeseries datasets (see in Additional Details below). -* New namelist variables \`snow\_thermal\_cond\_glc\_method\` and \`snow\_thermal\_cond\_lake\_method\` ([PR \\\#3072](https://github.com/ESCOMP/CTSM/pull/3072)). Snow thermal conductivity uses Jordan1991 over glaciers to reduce Greenland melt rates by default and Sturm over land and lake land units. -* Bytnerowicz is now the default nfix\_method for clm6 (\[PR \\\#2972\]([https://github.com/ESCOMP/ctsm/pull/2972](https://github.com/ESCOMP/ctsm/pull/2972))) which revises the temperature function for nitrogen fixation, replacing the Houlton *et al.* function. -* Updates to MEGAN for BVOCs (\[PR \\\#3065\](https://github.com/ESCOMP/CTSM/pull/3065) [PR \\\#3309](https://github.com/ESCOMP/CTSM/pull/3309)). Removes dependence on soil moisture from clm6 physics. +* New namelist variables \`snow\_thermal\_cond\_glc\_method\` and \`snow\_thermal\_cond\_lake\_method\` ([PR \#3072](https://github.com/ESCOMP/CTSM/pull/3072)). Snow thermal conductivity uses Jordan1991 over glaciers to reduce Greenland melt rates by default and Sturm over land and lake land units. +* Bytnerowicz is now the default nfix\_method for clm6 (https://github.com/ESCOMP/ctsm/pull/2972) which revises the temperature function for nitrogen fixation, replacing the Houlton *et al.* function. +* Updates to MEGAN for BVOCs (https://github.com/ESCOMP/CTSM/pull/3065 https://github.com/ESCOMP/CTSM/pull/3309). Removes dependence on soil moisture from clm6 physics. * New model parameter values that were calibrated to improve carbon cycle representation with CRUJRA. * New model parameter values that were calibrated to improve the fire model. Now using li2024 fire code. * New initial conditions files for f09 ("1-degree" 1850, 2000), f19 (“2-degree” 1850), and ne30 (1850, 1979, 2000\) resolutions. @@ -42,13 +42,13 @@ Changes to defaults for \`clm6\` physics: Changes for all physics versions: -* Parameters updated: Added MIMICS parameter \`mimics\_fi\` (fraction of litter inputs that bypass litter pools, directly contributing to SOM) and updated other MIMICS parameters (\[PR \\\#2365\]([https://github.com/ESCOMP/CTSM/pull/2365](https://github.com/ESCOMP/CTSM/pull/2365))) to remove NPP control on turnover, fix density dependent control on turnover, add litterfall fluxes that bypass litter pools and contribute directly to soil organic matter. -* FATES parameter file updated: ([PR \\\#2965](https://github.com/ESCOMP/CTSM/pull/2965), [PR \\\#2904](https://github.com/ESCOMP/CTSM/pull/2904), [PR \\\#1344](https://github.com/NGEET/fates/pull/1344), [PR \\\#3087](https://github.com/ESCOMP/CTSM/pull/3087)). See “FATES parameter file” section below for details. +* Parameters updated: Added MIMICS parameter \`mimics\_fi\` (fraction of litter inputs that bypass litter pools, directly contributing to SOM) and updated other MIMICS parameters (\[PR \#2365\]([https://github.com/ESCOMP/CTSM/pull/2365](https://github.com/ESCOMP/CTSM/pull/2365))) to remove NPP control on turnover, fix density dependent control on turnover, add litterfall fluxes that bypass litter pools and contribute directly to soil organic matter. +* FATES parameter file updated: ([PR \#2965](https://github.com/ESCOMP/CTSM/pull/2965), [PR \#2904](https://github.com/ESCOMP/CTSM/pull/2904), [PR \#1344](https://github.com/NGEET/fates/pull/1344), [PR \#3087](https://github.com/ESCOMP/CTSM/pull/3087)). See “FATES parameter file” section below for details. * New surface datasets and landuse timeseries files (see “surface datasets” section below). ## Heads up -* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details in the “history files” section below and in the PRs \[\\\#2445\](https://github.com/ESCOMP/ctsm/pull/2445) \[\\\#117\](https://github.com/ESCOMP/MOSART/pull/117) \[\\\#61\](https://github.com/ESCOMP/RTM/pull/61) and the corresponding issues. +* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details in the “history files” section below and in the PRs \[\#2445\](https://github.com/ESCOMP/ctsm/pull/2445) \[\#117\](https://github.com/ESCOMP/MOSART/pull/117) \[\#61\](https://github.com/ESCOMP/RTM/pull/61) and the corresponding issues. * Adding time to 1d weighting fields in transient simulations PR \\[\#3328](https://github.com/ESCOMP/CTSM/pull/3328) * Regarding CMIP7 vs. CMIP6 inputs: * We supply only CMIP7 C13/C14 isotope datasets, so these get used regardless of CLM\_CMIP\_ERA setting. @@ -64,7 +64,7 @@ Changes for all physics versions: ## Changes related to history files (Note 1: The same information in this section applies to MOSART and RTM. -Note 2: The gist of the information in this section also appears in the CTSM User’s Guide here [https://escomp.github.io/CTSM/users\_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.html\#various-ways-to-change-history-output-averaging-flags](https://escomp.github.io/CTSM/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.html#various-ways-to-change-history-output-averaging-flags)) +Note 2: The gist of the information in this section also appears in the [CTSM User’s Guide](https://escomp.github.io/CTSM/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.html#various-ways-to-change-history-output-averaging-flags)). Following ctsm5.3.018 "Change history time to be the middle of the time bounds" and keeping CLM history consistent with CAM history, the CTSM5.4 change intends to prevent confusion associated with the time corresponding to instantaneous history fields by putting them on separate files than non-instantaneous fields. @@ -86,14 +86,14 @@ Users will now see: The CLM handles empty history (and corresponding history-restart) files by not generating them, while rtm and mosart give an error. Instead of refactoring rtm and mosart to behave like the clm (considered out of scope), we have introduced one active instantaneous field in mosart and one in rtm to bypass the "empty file" error. -## New surface datasets and landuse timeseries files (\[PR \\\#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) +## New surface datasets and landuse timeseries files (\[PR \#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) * Transient landuse timeseries files going back to 1700 made for f09 and 360x720 grids. -* New resolutions now supported: ne3np4.pg3, mpasa30, ne0np4.NATL.ne30x8 (\[PR \\\#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) +* New resolutions now supported: ne3np4.pg3, mpasa30, ne0np4.NATL.ne30x8 (\[PR \#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) * Updates to input datasets (also referred to as raw datasets): - * PFT/LAI/soil-color raw datasets; now from the CMIP7 timeseries that ends in 2023 (Issues [\\\#2851](https://github.com/ESCOMP/CTSM/issues/2851)). + * PFT/LAI/soil-color raw datasets; now from the CMIP7 timeseries that ends in 2023 (Issues [\#2851](https://github.com/ESCOMP/CTSM/issues/2851)). * Two fire datasets: crop fire peak month and population density (Issue - * \[\\\#2701\]([https://github.com/ESCOMP/CTSM/issues/2701](https://github.com/ESCOMP/CTSM/issues/2701)) \[\\\#3302\](https://github.com/ESCOMP/CTSM/issues/3302)). + * \[\#2701\]([https://github.com/ESCOMP/CTSM/issues/2701](https://github.com/ESCOMP/CTSM/issues/2701)) \[\#3302\](https://github.com/ESCOMP/CTSM/issues/3302)). * Transient (historical) urban datasets are now based on CMIP7 urban data, partitioned into TBD, HD, and MD classes in proportion to GaoOneill present day classification. ## Changes to FATES and the FATES parameter file @@ -139,13 +139,13 @@ Information about additional py\_env\_create options — including how to instal ## Potentially time-evolving \`leafcn\_target\` replaces time-constant \`leafcn\` -The former is calculated as a function of the latter and can be time-evolving depending on new paramfile parameter \`leafcn\_co2\_slope\` (\[PR \\\#1654\](https://github.com/ESCOMP/ctsm/pull/1654). The time-evolving effect defaults to off with \`leafcn\_co2\_slope\` \= 0 on the parameter file. +The former is calculated as a function of the latter and can be time-evolving depending on new paramfile parameter \`leafcn\_co2\_slope\` https://github.com/ESCOMP/ctsm/pull/1654. The time-evolving effect defaults to off with \`leafcn\_co2\_slope\` \= 0 on the parameter file. # Simulations supporting this release by providing initial conditions -* f19 \`Clm60BgcCruJra\` 16pft: https://github.com/NCAR/LMWG\_dev/issues/125 -* f09 with \`Clm60BgcCropCruJra\`: https://github.com/NCAR/LMWG\_dev/issues/124 -* ne30 with \`Clm60BgcCropCruJra\`: [https://github.com/NCAR/LMWG\_dev/issues/123](https://github.com/NCAR/LMWG_dev/issues/123) (123\_HIST\_popDens) -* ne30 SP https://github.com/NCAR/LMWG\_dev/issues/126 -* f09 SP https://github.com/NCAR/LMWG\_dev/issues/127 +* f19 \`Clm60BgcCruJra\` 16pft: https://github.com/NCAR/LMWG_dev/issues/125 +* f09 with \`Clm60BgcCropCruJra\`: https://github.com/NCAR/LMWG_dev/issues/124 +* ne30 with \`Clm60BgcCropCruJra\`: https://github.com/NCAR/LMWG_dev/issues/123 (123\_HIST\_popDens) +* ne30 SP https://github.com/NCAR/LMWG_dev/issues/126 +* f09 SP https://github.com/NCAR/LMWG_dev/issues/127 From cd3558f73e115645004f7ec1ea6d2336a820086e Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 18:12:11 -0700 Subject: [PATCH 104/112] More corrections to the WhatsNew file --- WhatsNewInCTSM5.4.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index 248008ea74..6f1015757e 100755 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -7,15 +7,15 @@ * New surface datasets from CMIP7 data including PFT and urban distributions, land use transitions, population density, and atmospheric C isotopes. These data are only available through the historical record (1850-2023), and * are not available for future periods (presently known as SSP), * for future periods and N deposition we continue to use CMIP6 data from CESM2. -* Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR \#2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. -* Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue \#1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. +* Option to use CRUJRA2024 atmospheric driver data with clm6 and clm5 physics options ([PR #2956](https://github.com/ESCOMP/ctsm/pull/2956)), this is the default data-atmosphere (DATM) for clm6. This CRUJRA dataset covers 1901-2023, whereas previous GSWP3 only covers 1901-2014. +* Capability to run single-point PLUMBER tower sites, similar to the NEON tower capability ([issue #1487](https://github.com/ESCOMP/CTSM/issues/1487)). Initial conditions are not provided for PLUMBER sites. * New CLM\_CMIP\_ERA flag in env\_run.xml. Valid options are cmip7 and cmip6. Defaults to cmip7 except in compsets containing SSP for which it defaults to cmip6 because there are no future-period datasets yet available for CMIP7. * Automatic, more flexible use of anomaly forcings for CMIP6 ISSP cases, which also use the cmip6 CLM\_CMIP\_ERA flag: [Documentation](https://escomp.github.io/CTSM/users_guide/running-special-cases/Running-with-anomaly-forcing.html) -* Unsupported script that checks for spinup equilibrium in `tools/contrib/` for spectral element grids ([PR \#2991](https://github.com/ESCOMP/ctsm/pull/2991)). +* Unsupported script that checks for spinup equilibrium in `tools/contrib/` for spectral element grids ([PR #2991](https://github.com/ESCOMP/ctsm/pull/2991)). * New paramfile tools that allow users to query and modify CLM parameter files ([documentation](https://escomp.github.io/CTSM/users_guide/using-clm-tools/paramfile-tools.html)) * Optional time-evolving \`leafcn\_target\`. More under “Additional detail” below. -* New vertical movement scheme for soil nitrate, which is off by default (PR [\#2992](https://github.com/ESCOMP/CTSM/pull/2992)). +* New vertical movement scheme for soil nitrate, which is off by default (PR [#2992](https://github.com/ESCOMP/CTSM/pull/2992)). * Documentation improvements and new URL: https://escomp.github.io/CTSM/index.html. * FATES: * Grazing ([sci.1.81.0\_api.37.1.0](https://github.com/NGEET/fates/releases/tag/sci.1.81.0_api.37.1.0)). @@ -27,7 +27,7 @@ Changes to defaults for \`clm6\` physics: * New CMIP7 surface and landuse timeseries datasets (see in Additional Details below). -* New namelist variables \`snow\_thermal\_cond\_glc\_method\` and \`snow\_thermal\_cond\_lake\_method\` ([PR \#3072](https://github.com/ESCOMP/CTSM/pull/3072)). Snow thermal conductivity uses Jordan1991 over glaciers to reduce Greenland melt rates by default and Sturm over land and lake land units. +* New namelist variables \`snow\_thermal\_cond\_glc\_method\` and \`snow\_thermal\_cond\_lake\_method\` ([PR #3072](https://github.com/ESCOMP/CTSM/pull/3072)). Snow thermal conductivity uses Jordan1991 over glaciers to reduce Greenland melt rates by default and Sturm over land and lake land units. * Bytnerowicz is now the default nfix\_method for clm6 (https://github.com/ESCOMP/ctsm/pull/2972) which revises the temperature function for nitrogen fixation, replacing the Houlton *et al.* function. * Updates to MEGAN for BVOCs (https://github.com/ESCOMP/CTSM/pull/3065 https://github.com/ESCOMP/CTSM/pull/3309). Removes dependence on soil moisture from clm6 physics. * New model parameter values that were calibrated to improve carbon cycle representation with CRUJRA. @@ -42,7 +42,7 @@ Changes to defaults for \`clm6\` physics: Changes for all physics versions: -* Parameters updated: Added MIMICS parameter \`mimics\_fi\` (fraction of litter inputs that bypass litter pools, directly contributing to SOM) and updated other MIMICS parameters (\[PR \#2365\]([https://github.com/ESCOMP/CTSM/pull/2365](https://github.com/ESCOMP/CTSM/pull/2365))) to remove NPP control on turnover, fix density dependent control on turnover, add litterfall fluxes that bypass litter pools and contribute directly to soil organic matter. +* Parameters updated: Added MIMICS parameter \`mimics\_fi\` (fraction of litter inputs that bypass litter pools, directly contributing to SOM) and updated other MIMICS parameters (https://github.com/ESCOMP/CTSM/pull/2365) to remove NPP control on turnover, fix density dependent control on turnover, add litterfall fluxes that bypass litter pools and contribute directly to soil organic matter. * FATES parameter file updated: ([PR \#2965](https://github.com/ESCOMP/CTSM/pull/2965), [PR \#2904](https://github.com/ESCOMP/CTSM/pull/2904), [PR \#1344](https://github.com/NGEET/fates/pull/1344), [PR \#3087](https://github.com/ESCOMP/CTSM/pull/3087)). See “FATES parameter file” section below for details. * New surface datasets and landuse timeseries files (see “surface datasets” section below). From b5fa1cf8ac3597d36b27732fdf96b94a063685f1 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 24 Nov 2025 18:20:09 -0700 Subject: [PATCH 105/112] More corrections to the WhatsNew file --- WhatsNewInCTSM5.4.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/WhatsNewInCTSM5.4.md b/WhatsNewInCTSM5.4.md index 6f1015757e..fe721ae312 100755 --- a/WhatsNewInCTSM5.4.md +++ b/WhatsNewInCTSM5.4.md @@ -48,8 +48,8 @@ Changes for all physics versions: ## Heads up -* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details in the “history files” section below and in the PRs \[\#2445\](https://github.com/ESCOMP/ctsm/pull/2445) \[\#117\](https://github.com/ESCOMP/MOSART/pull/117) \[\#61\](https://github.com/ESCOMP/RTM/pull/61) and the corresponding issues. -* Adding time to 1d weighting fields in transient simulations PR \\[\#3328](https://github.com/ESCOMP/CTSM/pull/3328) +* History tapes now split into two files from hX to hXi and hXa, where X is the tape number (e.g. h0i/h0a) and where "i" stands for history file containing instantaneous fields, while "a" stands for history file containing non-instantaneous fields. Details in the “history files” section below and in the PRs https://github.com/ESCOMP/ctsm/pull/2445 https://github.com/ESCOMP/MOSART/pull/117 https://github.com/ESCOMP/RTM/pull/61 and the corresponding issues. +* Adding time to 1d weighting fields in transient simulations PR https://github.com/ESCOMP/CTSM/pull/3328 * Regarding CMIP7 vs. CMIP6 inputs: * We supply only CMIP7 C13/C14 isotope datasets, so these get used regardless of CLM\_CMIP\_ERA setting. * We supply only CMIP7 population density with clm6 physics in non-SSP cases, because the fire model is calibrated to that; conversely, we supply only CMIP6 population density for pre-clm6 physics and for SSP cases. @@ -86,14 +86,13 @@ Users will now see: The CLM handles empty history (and corresponding history-restart) files by not generating them, while rtm and mosart give an error. Instead of refactoring rtm and mosart to behave like the clm (considered out of scope), we have introduced one active instantaneous field in mosart and one in rtm to bypass the "empty file" error. -## New surface datasets and landuse timeseries files (\[PR \#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) +## New surface datasets and landuse timeseries files (https://github.com/ESCOMP/CTSM/pull/3482) * Transient landuse timeseries files going back to 1700 made for f09 and 360x720 grids. -* New resolutions now supported: ne3np4.pg3, mpasa30, ne0np4.NATL.ne30x8 (\[PR \#3482\](https://github.com/ESCOMP/CTSM/pull/3482)) +* New resolutions now supported: ne3np4.pg3, mpasa30, ne0np4.NATL.ne30x8 (https://github.com/ESCOMP/CTSM/pull/3482) * Updates to input datasets (also referred to as raw datasets): - * PFT/LAI/soil-color raw datasets; now from the CMIP7 timeseries that ends in 2023 (Issues [\#2851](https://github.com/ESCOMP/CTSM/issues/2851)). - * Two fire datasets: crop fire peak month and population density (Issue - * \[\#2701\]([https://github.com/ESCOMP/CTSM/issues/2701](https://github.com/ESCOMP/CTSM/issues/2701)) \[\#3302\](https://github.com/ESCOMP/CTSM/issues/3302)). + * PFT/LAI/soil-color raw datasets; now from the CMIP7 timeseries that ends in 2023 (Issue [\#2851](https://github.com/ESCOMP/CTSM/issues/2851)). + * Two fire datasets: crop fire peak month and population density (https://github.com/ESCOMP/CTSM/issues/2701 https://github.com/ESCOMP/CTSM/issues/3302). * Transient (historical) urban datasets are now based on CMIP7 urban data, partitioned into TBD, HD, and MD classes in proportion to GaoOneill present day classification. ## Changes to FATES and the FATES parameter file From 7d56d4b84c1f75cc8e4aa65660d2002cbab23c4f Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 26 Nov 2025 12:14:23 -0700 Subject: [PATCH 106/112] Small corrections to README and CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- README | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70bf041ba7..a0a98d507e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,4 +45,4 @@ Code conventions: https://github.com/ESCOMP/ctsm/wiki/CTSM-coding-guidelines #### Code of Conduct: -See the `CODE_OF_CONDUCT.md` file for expectations of how to work in the community. +See https://github.com/ESCOMP/CTSM?tab=coc-ov-file for expectations of how to work in the community. diff --git a/README b/README index dc38231173..deca3cd8d2 100644 --- a/README +++ b/README @@ -15,6 +15,10 @@ For lists of current bugs (issues) and current development see the CTSM GitHub p https://github.com/ESCOMP/CTSM +For Code of Conduct (how to work with each other on the CTSM project): + +https://github.com/ESCOMP/CTSM?tab=coc-ov-file + INFORMATION ON THE CMEPS DRIVER: https://escomp.github.io/CMEPS @@ -77,7 +81,6 @@ README.md ---------------- File that displays on github under https::/github.com README.rst --------------- File that displays under the project in github README_GITFLEXIMOD.rst --- Information on how to work with git-fleximod for CTSM WhatsNewInCTSM5.4.md ----- Overview document of the changes between ctsm5.3 and ctsm5.4 -CODE_OF_CONDUCT.md ------- Code of Conduct for how to work with each other on the CTSM project Copyright ---------------- CESM Copyright file doc/UpdateChangeLog.pl --- Script to add documentation on a tag to the ChangeLog/ChangeSum files @@ -168,7 +171,7 @@ src/unit_test_stubs Unit test stubs that replicate CTSM code simpler cd $CIMEROOT/scripts ./create_newcase # get help on how to run create_newcase - ./create_newcase --case testI --res f19_g17_gl4 --compset I2000Clm60BgcCrop + ./create_newcase --case testI --res f09_t232 --compset I2000Clm60BgcCrop # create new "I" case for default machine at 1.9x2.5_gx1v7 # "I2000Clm60BgcCrop" case is clm6_0 physics, CDEPS, and inactive ice/ocn/glc # and MOSART for river-routing From 90a165f27a1c7aa388f2647d3b98108e3fa6822d Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 26 Nov 2025 12:24:37 -0700 Subject: [PATCH 107/112] Revert updates to two default_data*cfg files to avoid testing in this PR Reverts part of 060bd7c5a48c9ead38151ce069591e0833b27fea --- python/ctsm/test/testinputs/default_data.cfg | 4 ++-- python/ctsm/test/testinputs/default_data_gswp3.cfg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ctsm/test/testinputs/default_data.cfg b/python/ctsm/test/testinputs/default_data.cfg index 9d9a781947..60c012561c 100644 --- a/python/ctsm/test/testinputs/default_data.cfg +++ b/python/ctsm/test/testinputs/default_data.cfg @@ -15,14 +15,14 @@ precname = CLMCRUJRA2024.Precip tpqwname = CLMCRUJRA2024.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc diff --git a/python/ctsm/test/testinputs/default_data_gswp3.cfg b/python/ctsm/test/testinputs/default_data_gswp3.cfg index e5eb46d681..09e1463eb2 100644 --- a/python/ctsm/test/testinputs/default_data_gswp3.cfg +++ b/python/ctsm/test/testinputs/default_data_gswp3.cfg @@ -15,14 +15,14 @@ precname = CLMGSWP3v1.Precip tpqwname = CLMGSWP3v1.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc From 74ed7a52f1b68b3ba59f072132a3b4899eeac8c8 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 26 Nov 2025 12:47:33 -0700 Subject: [PATCH 108/112] Update tools in /contrib to make h0a the default option (instead of h0) --- tools/contrib/SpinupStability_BGC_v11.ncl | 4 ++-- tools/contrib/SpinupStability_BGC_v12_SE.ncl | 2 +- tools/contrib/SpinupStability_SP_v10.ncl | 2 +- tools/contrib/run_clm_historical.v11.csh | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/contrib/SpinupStability_BGC_v11.ncl b/tools/contrib/SpinupStability_BGC_v11.ncl index d81b38ad9c..92a06e123e 100644 --- a/tools/contrib/SpinupStability_BGC_v11.ncl +++ b/tools/contrib/SpinupStability_BGC_v11.ncl @@ -36,7 +36,7 @@ begin region = "Global" ; Global, Arctic, or SPT (single point) subper = 20 ; Subsampling period in years paleo = False ; Use paleo map - hist_ext = "h0" ; Set to either h0 (ctsm5.3.061 or earlier) or h0a (ctsm5.3.062 or later) + hist_ext = "h0a" ; Valid options: h0a, h0 ; SPT (single point) EXAMPLE ; caseid = "clm50_release-clm5.0.15_SPT_GSWP3V1_1850spin" @@ -45,7 +45,7 @@ begin ; region = "SPT" ; Global, Arctic, or SPT (single point) ; subper = 20 ; Subsampling period in years ; paleo = False ; Use paleo map -; hist_ext = "h0" ; Set to either h0 (ctsm5.3.061 or earlier) or h0a (ctsm5.3.062 or later) +; hist_ext = "h0a" ; Valid options: h0a, h0 do_plot = True ; do_plot = False diff --git a/tools/contrib/SpinupStability_BGC_v12_SE.ncl b/tools/contrib/SpinupStability_BGC_v12_SE.ncl index aa044b9c17..25e909b696 100644 --- a/tools/contrib/SpinupStability_BGC_v12_SE.ncl +++ b/tools/contrib/SpinupStability_BGC_v12_SE.ncl @@ -41,7 +41,7 @@ begin region = "Global" ; Global subper = 20 ; Subsampling period in years paleo = False ; Use paleo map ; UNTESTED IN THIS VERSION - hist_ext = "h0" ; Set to either h0 (ctsm5.3.061 or earlier) or h0a (ctsm5.3.062 or later) + hist_ext = "h0a" ; Valid options: h0a, h0 ;--- ARH mods --- map_method = "conserve" ;bilinear,conserve or patch diff --git a/tools/contrib/SpinupStability_SP_v10.ncl b/tools/contrib/SpinupStability_SP_v10.ncl index 6b3d310234..97eccd8183 100644 --- a/tools/contrib/SpinupStability_SP_v10.ncl +++ b/tools/contrib/SpinupStability_SP_v10.ncl @@ -32,7 +32,7 @@ begin subper = 20 h2osoi_layer = 8 ; Desired soil layer (layer 8 is about 1m) tsoi_layer = 10 ; Desired soil layer (layer 10 is about 3m) - hist_ext = "h0" ; Set to either h0 (ctsm5.3.061 or earlier) or h0a (ctsm5.3.062 or later) + hist_ext = "h0a" ; Valid options: h0a, h0 do_plot = "True" ;=======================================================================; diff --git a/tools/contrib/run_clm_historical.v11.csh b/tools/contrib/run_clm_historical.v11.csh index 04e22aa31a..7398487a13 100755 --- a/tools/contrib/run_clm_historical.v11.csh +++ b/tools/contrib/run_clm_historical.v11.csh @@ -72,6 +72,8 @@ # ./run_clm_historical.v10.csh ! > & run_clm_historical.out & # - Modify to look for either h0 or h0a files - Keith Oleson July, 2025 # ./run_clm_historical.v11.csh ! > & run_clm_historical.out & +# - Update the comment about h0a/h0 files - slevis Nov, 2025 +# ./run_clm_historical.v11.csh ! > & run_clm_historical.out & ######################################################################################### ######################################################################################### @@ -85,8 +87,8 @@ # --- CASENAME is your case name set CASENAME = 'ctsm530_f19_PPE_TESTv11_hist' -# --- Set to either h0 (ctsm5.3.061 or earlier) or h0a (ctsm5.3.062 or later) -set HIST_EXT = 'h0' +# HIST_EXT valid options: h0a, h0 +set HIST_EXT = 'h0a' # --- Set the user namelist file. cp original_user_nl_clm user_nl_clm From 83f35cbf30eea63cec47407e02a74150a771053f Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 26 Nov 2025 18:31:04 -0700 Subject: [PATCH 109/112] Three user_nl_clm and two .cfg files with ctsm5.4 updates --- .../clm/smallville_dynlakes_monthly/user_nl_clm | 2 +- .../clm/smallville_dynurban_monthly/user_nl_clm | 2 +- .../usermods_dirs/clm/PLUMBER2/defaults/user_nl_clm | 2 +- python/ctsm/test/testinputs/default_data.cfg | 12 ++++++------ python/ctsm/test/testinputs/default_data_gswp3.cfg | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynlakes_monthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/smallville_dynlakes_monthly/user_nl_clm index 6223cc203f..46680fd36d 100644 --- a/cime_config/testdefs/testmods_dirs/clm/smallville_dynlakes_monthly/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynlakes_monthly/user_nl_clm @@ -6,7 +6,7 @@ do_transient_lakes = .true. ! Key points are that lake area starts as 0, increases after the first year, then decreases after the second year. ! PCT_CROP is also changed so that PCT_LAKE + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_LAKE in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.) ! Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid. -flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/ctsm5.3.0/synthetic/landuse.timeseries_1x1_smallvilleIA_synth_SSP2-4.5_1850-1855_78pfts_dynLakes_c240912.nc' +flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/ctsm5.4.0/synthetic/landuse.timeseries_1x1_smallvilleIA_synth_1850-1855_78pfts_dynLakes_c251023.nc' ! NOTE slevis (2024/2/23) Adding option for tests to pass. In the long term ! ensure that subset_data generates fsurdat and landuse files consistent with diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm index 958265cffc..bb18638158 100644 --- a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm @@ -7,7 +7,7 @@ do_transient_urban = .true. ! Medium density urban is set to zero to test the memory-saving behavior of PCT_URBAN_MAX. ! PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.) ! Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid. -flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/ctsm5.3.0/synthetic/landuse.timeseries_1x1_smallvilleIA_synth_SSP2-4.5_1850-1855_78pfts_dynUrban_c240912.nc' +flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/ctsm5.4.0/synthetic/landuse.timeseries_1x1_smallvilleIA_synth_1850-1855_78pfts_dynUrban_c251023.nc' ! NOTE slevis (2024/2/23) Adding option for tests to pass. In the long term ! ensure that subset_data generates fsurdat and landuse files consistent with diff --git a/cime_config/usermods_dirs/clm/PLUMBER2/defaults/user_nl_clm b/cime_config/usermods_dirs/clm/PLUMBER2/defaults/user_nl_clm index 5216afb381..ff20dae0b4 100644 --- a/cime_config/usermods_dirs/clm/PLUMBER2/defaults/user_nl_clm +++ b/cime_config/usermods_dirs/clm/PLUMBER2/defaults/user_nl_clm @@ -19,6 +19,6 @@ !---------------------------------------------------------------------------------- flanduse_timeseries = ' ' ! This isn't needed for a non transient case, but will be once we start using transient compsets -fsurdat = "$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/PLUMBER2/ctsm5.3.0/surfdata_1x1_PLUMBER2_${PLUMBER2SITE}_hist_2000_16pfts_c240912.nc" +fsurdat = "$DIN_LOC_ROOT/lnd/clm2/surfdata_esmf/PLUMBER2/ctsm5.4.0/surfdata_1x1_PLUMBER2_${PLUMBER2SITE}_hist_2000_16pfts_c251023.nc" ! custom namelist changes for each site / case diff --git a/python/ctsm/test/testinputs/default_data.cfg b/python/ctsm/test/testinputs/default_data.cfg index 60c012561c..a220a0ee90 100644 --- a/python/ctsm/test/testinputs/default_data.cfg +++ b/python/ctsm/test/testinputs/default_data.cfg @@ -15,16 +15,16 @@ precname = CLMCRUJRA2024.Precip tpqwname = CLMCRUJRA2024.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 -surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc -surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c251022.nc +surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c251022.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 -landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc -landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +landuse_16pft = landuse.timeseries_0.9x1.25_hist_1850-2023_16pfts_c251022.nc +landuse_78pft = landuse.timeseries_0.9x1.25_hist_1850-2023_78pfts_c251022.nc [domain] file = share/domains/domain.lnd.fv0.9x1.25_gx1v7.151020.nc diff --git a/python/ctsm/test/testinputs/default_data_gswp3.cfg b/python/ctsm/test/testinputs/default_data_gswp3.cfg index 09e1463eb2..a7da53cea3 100644 --- a/python/ctsm/test/testinputs/default_data_gswp3.cfg +++ b/python/ctsm/test/testinputs/default_data_gswp3.cfg @@ -15,16 +15,16 @@ precname = CLMGSWP3v1.Precip tpqwname = CLMGSWP3v1.TPQW [surfdat] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 -surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c240908.nc -surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c240908.nc +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +surfdat_16pft = surfdata_0.9x1.25_hist_2000_16pfts_c251022.nc +surfdat_78pft = surfdata_0.9x1.25_hist_2000_78pfts_c251022.nc mesh_dir = share/meshes/ mesh_surf = fv0.9x1.25_141008_ESMFmesh.nc [landuse] -dir = lnd/clm2/surfdata_esmf/ctsm5.3.0 -landuse_16pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc -landuse_78pft = landuse.timeseries_0.9x1.25_SSP2-4.5_1850-2100_78pfts_c240908.nc +dir = lnd/clm2/surfdata_esmf/ctsm5.4.0 +landuse_16pft = landuse.timeseries_0.9x1.25_hist_1850-2023_16pfts_c251022.nc +landuse_78pft = landuse.timeseries_0.9x1.25_hist_1850-2023_78pfts_c251022.nc [domain] file = share/domains/domain.lnd.fv0.9x1.25_gx1v7.151020.nc From 8b49fe389a009dc3e5037e255edc1cbdbab05dae Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 1 Dec 2025 16:31:38 -0700 Subject: [PATCH 110/112] Update ExpectedTestFails.xml according to Erik's comments --- cime_config/testdefs/ExpectedTestFails.xml | 24 +--------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index d21fc39c42..0cee0d9ba1 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -29,13 +29,6 @@ - - - FAIL - #3529 - We will generate new fsurdat files with the new raw lai file to resolve this issue. - - FAIL @@ -73,6 +66,7 @@ FAIL #3495 + Problem getting gdds_20250809_025305.tweaked_latlons.nc on izumi. @@ -338,22 +332,6 @@ - - - FAIL - #3252 - This should be resolved for the 5.4 release. - - - - - - FAIL - #3252 - This should be resolved for the 5.4 release. - - - From 2dbc28d073797fac181c522f6dff206f75fa784b Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Mon, 1 Dec 2025 18:56:53 -0700 Subject: [PATCH 111/112] Remove one more previously-expected failure from ExpectedTestFails.xml --- cime_config/testdefs/ExpectedTestFails.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 0cee0d9ba1..f7f4e8ba24 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -62,13 +62,6 @@ - - - FAIL - #3495 - Problem getting gdds_20250809_025305.tweaked_latlons.nc on izumi. - - FAIL From 89a4caee7072aa75a0516335cef0366f11e5e345 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Tue, 2 Dec 2025 15:20:02 -0700 Subject: [PATCH 112/112] Add H2OSNO to default hist_fincl1 for clm_accelerated_spinup ON or SASU --- bld/namelist_files/namelist_defaults_ctsm.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 627f1d54d0..bf3f9effc7 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -70,21 +70,19 @@ attributes from the config_cache.xml file (with keys converted to upper-case). .true. .true. 'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','NPP','TWS','TSAI','HTOP','HBOT' +>'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','NPP','TWS','TSAI','HTOP','HBOT','H2OSNO' 'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','CPOOL','NPP','TWS' +>'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','CPOOL','NPP','TWS','H2OSNO' 'TOTSOMC','TOTSOMN','TLAI','GPP','NPP','TWS' +>'TOTSOMC','TOTSOMN','TLAI','GPP','NPP','TWS','H2OSNO' 'TLAI','TWS' +>'TLAI','TWS','H2OSNO' 'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','NPP','TWS','TSAI','HTOP','HBOT' +>'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','NPP','TWS','TSAI','HTOP','HBOT','H2OSNO' 'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','CPOOL','NPP','TWS' +>'TOTECOSYSC','TOTECOSYSN','TOTSOMC','TOTSOMN','TOTVEGC','TOTVEGN','TLAI','GPP','CPOOL','NPP','TWS','H2OSNO' 'TOTSOMC','TOTSOMN','TLAI','GPP','NPP','TWS' -'TLAI','TWS' +>'TOTSOMC','TOTSOMN','TLAI','GPP','NPP','TWS','H2OSNO' -8760 -8760 20