source: trunk/LMDZ.COMMON/libf/evolution/soil_therm_inertia.F90

Last change on this file was 4065, checked in by jbclement, 2 weeks ago

PEM:
Major refactor following the previous ones (r3989 and r3991) completing the large structural reorganization and cleanup of the PEM codebase. This revision introduces newly designed modules, standardizes interfaces with explicit ini/end APIs and adds native NetCDF I/O together with explicit PCM/PEM adapters. In detail:

  • Some PEM models were corrected or improved:
    • Frost/perennial ice semantics are clarified via renaming;
    • Soil temperature remapping clarified, notably by removing the rescaling of temperature deviation;
    • Geothermal flux for the PCM is computed based on the PEM state;
  • New explicit PEM/PCM adapters ("set_*"/"build4PCM_*") to decouple PEM internal representation from PCM file layouts and reconstruct consistent fields returned to the PCM;
  • New explicit build/teardown routines that centralize allocation and initialization ordering, reducing accidental use of uninitialized data and making the model lifecycle explicit;
  • Add native read/write helpers for NetCDF that centralize all low-level NetCDF interactions with major improvements (and more simplicity) compared to legacy PEM/PCM I/O (see the modules "io_netcdf" and "output"). They support reading, creation and writing of "diagevol.nc" (renamed from "diagpem.nc") and start/restart files;
  • Provide well-focused modules ("numerics"/"maths"/"utility"/"display") to host commonly-used primitives:
    • "numerics" defines numerical types and constants for reproducibility, portability across compilers and future transitions (e.g. quadruple precision experiments);
    • "display" provides a single controlled interface for runtime messages, status output and diagnostics, avoiding direct 'print'/'write' to enable silent mode, log redirection, and MPI-safe output in the future.
    • "utility" (new module) hosts generic helpers used throughout the code (e.g. "int2str" or "real2str");
  • Add modules "clim_state_init"/"clim_state_rec" which provide robust read/write logic for "start/startfi/startpem", including 1D fallbacks, mesh conversions and dimension checks. NetCDF file creation is centralized and explicit. Restart files are now self-consistent and future-proof, requiring changes only to affected variables;
  • Add module "atmosphere" which computes pressure fields, reconstructs potential temperature and air mass. It also holds the whole logic to define sigma or hybrid coordinates for altitudes;
  • Add module "geometry" to centrilize dimensions logic and grid conversions routines (including 2 new ones "dyngrd2vect"/"vect2dyngrd");
  • Add module "slopes" to isolate slopes handling;
  • Add module "surface" to isolate surface management. Notably, albedo and emissivity are now fully reconstructed following the PCM settings;
  • Add module "allocation" to check the dimension initialization and centrilize allocation/deallocation;
  • Finalize module decomposition and renaming to consolidate domain-based modules, purpose-based routines and physics/process-based variables;
  • The main program now drives a clearer sequence of conceptual steps (initialization / reading / evolution / update / build / writing) and fails explicitly instead of silently defaulting;
  • Ice table logic is made restart-safe;
  • 'Open'/'read' intrinsic logic is made safe and automatic;
  • Improve discoverability and standardize the data handling (private vs protected vs public);
  • Apply consistent documentation/header style already introduced;
  • Update deftank scripts to reflect new names and launch wrappers;

This revision is a structural milestone aiming to be behavior-preserving where possible. It has been tested via compilation and short integration runs. However, due to extensive renames, moves, and API changes, full validation is still ongoing.
Note: the revision includes one (possibly two) easter egg hidden in the code for future archaeologists of the PEM. No physics were harmed.
JBC

File size: 13.2 KB
Line 
1MODULE soil_therm_inertia
2!-----------------------------------------------------------------------
3! NAME
4!     soil_therm_inertia
5!
6! DESCRIPTION
7!     Compute and update soil thermal properties based on ice content,
8!     pressure, and cementation state.
9!
10! AUTHORS & DATE
11!     L. Lange, 2023
12!     JB Clement, 2025
13!
14! NOTES
15!
16!-----------------------------------------------------------------------
17
18! DEPENDENCIES
19! ------------
20use numerics, only: dp, di, k4, minieps
21
22! DECLARATION
23! -----------
24implicit none
25
26! PARAMETERS
27! ----------
28real(dp), parameter :: reg_inertie_thresold = 370._dp ! Above this thermal inertia, the regolith has too much cementation to be dependant on the pressure [J/m^2/K/s^1/2]
29real(dp), parameter :: reg_inertie_minvalue = 50._dp  ! Minimum value of the Thermal Inertia at low pressure (Piqueux & Christensen 2009) [J/m^2/K/s^1/2]
30real(dp), parameter :: reg_inertie_maxvalue = 370._dp ! Maximum value of the Thermal Inertia at low pressure (Piqueux & Christensen 2009) [J/m^2/K/s^1/2]
31real(dp), parameter :: C = 0.0015_dp                  ! Constant to derive TI as a function of P, from Presley and Christensen 1997 [unitless]
32real(dp), parameter :: K = 8.1*1e4_dp                 ! Constant to derive TI as a function of P, from Presley and Christensen 1997 [Torr, or 133.3Pa]
33real(dp), parameter :: Pa2Torr = 1./133.3_dp          ! Conversion from Pa to tor [Pa/Torr]
34
35contains
36!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
37
38!=======================================================================
39SUBROUTINE get_ice_TI(ispureice,pore_filling,surf_thermalinertia,ice_thermalinertia)
40!-----------------------------------------------------------------------
41! NAME
42!     get_ice_TI
43!
44! DESCRIPTION
45!     Compute ice thermal properties based on pore filling and purity.
46!
47! AUTHORS & DATE
48!     L. Lange, 2023
49!     JB Clement, 2025
50!
51! NOTES
52!     Uses Siegler et al. (2012) formula for pore-filling ice;
53!     uses Mellon et al. (2004) value for pure water ice.
54!-----------------------------------------------------------------------
55
56! DEPENDENCIES
57! ------------
58use planet, only: porosity
59
60! DECLARATION
61! -----------
62implicit none
63
64! ARGUMENTS
65! ---------
66logical(k4), intent(in)  :: ispureice           ! Boolean to check if ice is massive or just pore filling
67real(dp),    intent(in)  :: pore_filling        ! ice pore filling in each layer (1)
68real(dp),    intent(in)  :: surf_thermalinertia ! surface thermal inertia (J/m^2/K/s^1/2)
69real(dp),    intent(out) :: ice_thermalinertia  ! Thermal inertia of ice when present in the pore (J/m^2/K/s^1/2)
70
71! LOCAL VARIABLES
72! ---------------
73real(dp) :: inertie_purewaterice = 2100._dp ! 2050 is better according to my computations with the formula from Siegler et al., 2012, but let's follow Mellon et al. (2004)
74
75! CODE
76! ----
77if (ispureice) then
78    ice_thermalinertia = inertie_purewaterice
79else
80    ice_thermalinertia = sqrt(surf_thermalinertia**2 + porosity*pore_filling*inertie_purewaterice**2) ! Siegler et al., 2012
81end if
82
83END SUBROUTINE get_ice_TI
84!=======================================================================
85
86!=======================================================================
87SUBROUTINE update_soil_TI(tendencies_waterice,waterice,p_avg_new,icetable_depth,icetable_thickness,ice_porefilling,icetable_equilibrium,icetable_dynamic,TI)
88!-----------------------------------------------------------------------
89! NAME
90!     update_soil_TI
91!
92! DESCRIPTION
93!     Update soil thermal inertia based on ice table, regolith properties,
94!     and pressure-dependent cementation.
95!
96! AUTHORS & DATE
97!     L. Lange, 2023
98!     JB Clement, 2025
99!
100! NOTES
101!
102!-----------------------------------------------------------------------
103
104! DEPENDENCIES
105! ------------
106use geometry, only: ngrid, nslope, nsoil
107use soil,     only: volcapa, layer, inertiedat, depth_breccia, depth_bedrock, index_breccia, index_bedrock, reg_thprop_dependp
108use planet,   only: TI_breccia, TI_bedrock, TI_regolith_avg, P610
109use display,  only: print_msg
110
111! DECLARATION
112! -----------
113implicit none
114
115! ARGUMENTS
116! ---------
117real(dp),                   intent(in)    :: p_avg_new            ! Global average surface pressure [Pa]
118real(dp), dimension(:,:),   intent(in)    :: tendencies_waterice  ! Tendencies on the water ice [kg/m^2/year]
119real(dp), dimension(:,:),   intent(in)    :: waterice             ! Surface Water ice [kg/m^2]
120real(dp), dimension(:,:),   intent(in)    :: icetable_depth       ! Ice table depth [m]
121real(dp), dimension(:,:),   intent(in)    :: icetable_thickness   ! Ice table thickness [m]
122real(dp), dimension(:,:,:), intent(in)    :: ice_porefilling      ! Ice porefilling [m^3/m^3]
123logical(k4),                intent(in)    :: icetable_equilibrium, icetable_dynamic ! Computing method for ice table
124real(dp), dimension(:,:,:), intent(inout) :: TI                   ! Soil Thermal Inertia [J/m^2/K/s^1/2]
125
126! LOCAL VARIABLES
127! ---------------
128integer(di)                       :: ig, islope, isoil, iref, iend ! Loop variables
129real(dp), dimension(ngrid,nslope) :: regolith_inertia              ! Thermal inertia of the regolith (first layer in the GCM) [J/m^2/K/s^1/2]
130real(dp)                          :: delta                         ! Difference of depth to compute the  thermal inertia in a mixed layer [m]
131real(dp)                          :: ice_bottom_depth              ! Bottom depth of the subsurface ice [m]
132real(dp)                          :: d_part                        ! Regolith particle size [micrometer]
133real(dp)                          :: ice_inertia                   ! Inertia of water ice [SI]
134
135! CODE
136! ----
137call print_msg("> Updating soil properties")
138
139! 1. Modification of the regolith thermal inertia.
140do islope = 1,nslope
141    regolith_inertia(:,islope) = inertiedat(:,1)
142    do ig = 1,ngrid
143        if (tendencies_waterice(ig,islope) < -1.e-5_dp .and. abs(waterice(ig,islope)) < minieps) regolith_inertia(ig,islope) = TI_regolith_avg
144        if (reg_thprop_dependp) then
145            if (TI(ig,1,islope) < reg_inertie_thresold) then
146                d_part = (regolith_inertia(ig,islope)**2/(volcapa*C*(P610*Pa2Torr)**0.6))**(-1./(0.11_dp*log10(P610*Pa2Torr/K))) ! compute particle size, in micrometer
147                regolith_inertia(ig,islope) = sqrt(volcapa*C*(p_avg_new*Pa2Torr)**0.6*d_part**(-0.11_dp*log10(p_avg_new*Pa2Torr/K)))
148                if (regolith_inertia(ig,islope) > reg_inertie_maxvalue) regolith_inertia(ig,islope) = reg_inertie_maxvalue
149                if (regolith_inertia(ig,islope) < reg_inertie_minvalue) regolith_inertia(ig,islope) = reg_inertie_minvalue
150            end if
151        end if
152    end do
153end do
154
155! 2. Build new Thermal Inertia
156do islope = 1,nslope
157    do ig = 1,ngrid
158        do isoil = 1,index_breccia
159            TI(ig,isoil,islope) = regolith_inertia(ig,islope)
160        end do
161        if (regolith_inertia(ig,islope) < TI_breccia) then
162!!! transition
163            delta = depth_breccia
164            TI(ig,index_breccia + 1,islope) = sqrt((layer(index_breccia + 1) - layer(index_breccia))/                  &
165                                                  (((delta - layer(index_breccia))/(TI(ig,index_breccia,islope)**2)) + &
166                                                  ((layer(index_breccia + 1) - delta)/(TI_breccia**2))))
167            do isoil = index_breccia + 2,index_bedrock
168                TI(ig,isoil,islope) = TI_breccia
169            end do
170        else ! we keep the high ti values
171            do isoil = index_breccia + 1,index_bedrock
172                TI(ig,isoil,islope) = TI(ig,index_breccia,islope)
173            end do
174        end if ! TI PEM and breccia comparison
175!!! transition
176        delta = depth_bedrock
177        TI(ig,index_bedrock + 1,islope) = sqrt((layer(index_bedrock + 1) - layer(index_bedrock))/                  &
178                                              (((delta - layer(index_bedrock))/(TI(ig,index_bedrock,islope)**2)) + &
179                                              ((layer(index_bedrock + 1) - delta)/(TI_bedrock**2))))
180        do isoil = index_bedrock + 2,nsoil
181            TI(ig,isoil,islope) = TI_bedrock
182        end do
183    end do ! ig
184end do ! islope
185
186! 3. Build new TI in case of ice table
187do ig = 1,ngrid
188    do islope = 1,nslope
189        if (icetable_depth(ig,islope) > -1.e-6_dp) then
190        ! 3.0 Case where it is perennial ice
191            if (icetable_depth(ig,islope) < minieps) then
192                call get_ice_TI(.true.,1._dp,0._dp,ice_inertia)
193                do isoil = 1,nsoil
194                    TI(ig,isoil,islope) = ice_inertia
195                end do
196            else
197                if (icetable_equilibrium) then
198                    call get_ice_TI(.false.,1._dp,regolith_inertia(ig,islope),ice_inertia)
199                    ! 3.1.1 find the index of the mixed layer
200                    iref = 0 ! initialize iref
201                    do isoil = 1,nsoil ! loop on layers to find the beginning of the ice table
202                        if (icetable_depth(ig,islope) >= layer(isoil)) then
203                            iref = isoil ! pure regolith layer up to here
204                        else
205                            exit ! correct iref was obtained in previous cycle
206                        end if
207                    end do
208                    ! 3.1.2 find the index of the end of the ice table
209                    iend = 0 ! initialize iend
210                    ice_bottom_depth = icetable_depth(ig,islope) + icetable_thickness(ig,islope)
211                    do isoil = 1,nsoil ! loop on layers to find the end of the ice table
212                        if (ice_bottom_depth >= layer(isoil)) then
213                            iend = isoil ! pure regolith layer up to here
214                        else
215                            exit ! correct iref was obtained in previous cycle
216                        end if
217                    end do
218                    ! 3.2 Build the new ti
219                    if (iref < nsoil) then
220                        if (iref == iend) then
221                            ! Ice table begins and end in the same mixture with three components
222                            if (iref /= 0) then ! mixed layer
223                                TI(ig,iref + 1,islope) = sqrt((layer(iref + 1) - layer(iref))/                                      &
224                                                             (((icetable_depth(ig,islope) - layer(iref))/(TI(ig,iref,islope)**2)) + &
225                                                             ((ice_bottom_depth - icetable_depth(ig,islope))/(ice_inertia**2)) +    &
226                                                             ((layer(iref + 1) - ice_bottom_depth)/(TI(ig,iref + 1,islope)**2))))
227                            else ! first layer is already a mixed layer
228                                ! (ie: take layer(iref=0)=0)
229                                TI(ig,1,islope) = sqrt((layer(1))/                                                        &
230                                                      (((icetable_depth(ig,islope))/(TI(ig,1,islope)**2)) +               &
231                                                      ((ice_bottom_depth - icetable_depth(ig,islope))/(ice_inertia**2)) + &
232                                                      ((layer(2) - ice_bottom_depth)/(TI(ig,2,islope)**2))))
233                            end if ! of if (iref /= 0)
234                        else
235                            if (iref /= 0) then ! mixed layer
236                                TI(ig,iref + 1,islope) = sqrt((layer(iref + 1) - layer(iref))/                                      &
237                                                             (((icetable_depth(ig,islope) - layer(iref))/(TI(ig,iref,islope)**2)) + &
238                                                             ((layer(iref + 1) - icetable_depth(ig,islope))/(ice_inertia**2))))
239                            else ! first layer is already a mixed layer
240                                ! (ie: take layer(iref=0)=0)
241                                TI(ig,1,islope) = sqrt((layer(1))/                                          &
242                                                      (((icetable_depth(ig,islope))/(TI(ig,1,islope)**2)) + &
243                                                      ((layer(1) - icetable_depth(ig,islope))/(ice_inertia**2))))
244                            end if ! of if (iref /= 0)
245                        end if ! iref == iend
246
247                        TI(ig,iref + 2:iend,islope) = ice_inertia
248                        if (iend < nsoil) then
249                            TI(ig,iend + 1,islope) = sqrt((layer(iend + 1) - layer(iend))/                             &
250                                                         (((ice_bottom_depth - layer(iend))/(TI(ig,iend,islope)**2)) + &
251                                                         ((layer(iend + 1) - ice_bottom_depth)/(ice_inertia**2))))
252                        end if
253                    end if ! of if (iref < nsoil)
254                else if (icetable_dynamic) then
255                    do  isoil = 1,nsoil
256                        call get_ice_TI(.false.,ice_porefilling(ig,isoil,islope),regolith_inertia(ig,islope),TI(ig,isoil,islope))
257                    end do
258                end if ! of if icetable_equilibrium
259            end if ! permanent glaciers
260        end if ! icetable_depth(ig,islope) > 0.
261    end do !islope
262end do !ig
263
264END SUBROUTINE update_soil_TI
265!=======================================================================
266
267END MODULE soil_therm_inertia
Note: See TracBrowser for help on using the repository browser.