source: trunk/LMDZ.COMMON/libf/evolution/soil.F90 @ 4072

Last change on this file since 4072 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: 12.0 KB
Line 
1MODULE soil
2!-----------------------------------------------------------------------
3! NAME
4!     soil
5!
6! DESCRIPTION
7!     Soil state and properties for PEM.
8!
9! AUTHORS & DATE
10!     L. Lange, 04/2023
11!     JB Clement, 2023-2025
12!
13! NOTES
14!
15!-----------------------------------------------------------------------
16
17! DEPENDENCIES
18! ------------
19use numerics, only: dp, di, k4
20
21! DECLARATION
22! -----------
23implicit none
24
25! PARAMETERS
26! ----------
27logical(k4),                             protected :: do_soil            ! To run with subsurface physics
28logical(k4),                             protected :: reg_thprop_dependp ! Regolith thermal properties depend on surface pressure
29real(dp),                                protected :: flux_geo           ! Geothermal flux [W/m^2]
30real(dp),                                protected :: depth_breccia      ! Depth of breccia [m]
31real(dp),                                protected :: depth_bedrock      ! Depth of bedrock [m]
32real(dp), allocatable, dimension(:,:,:), protected :: TI_PCM             ! Soil thermal inertia in the "startfi.nc" at the beginning [SI]
33
34! VARIABLES
35! ---------
36real(dp), allocatable, dimension(:)     :: layer              ! Soil layer depths [m]
37real(dp), allocatable, dimension(:)     :: mlayer             ! Soil mid-layer depths [m]
38real(dp), allocatable, dimension(:,:,:) :: TI                 ! Soil thermal inertia [SI]
39real(dp), allocatable, dimension(:,:)   :: inertiedat         ! Soil thermal inertia saved as reference for current climate [SI]
40real(dp), allocatable, dimension(:,:)   :: inertiedat_PCM     ! Soil thermal inertia saved as reference for current climate in the PCM [SI]
41real(dp), allocatable, dimension(:,:)   :: mthermdiff         ! Mid-layer thermal diffusivity [SI]
42real(dp), allocatable, dimension(:,:)   :: thermdiff          ! Inter-layer thermal diffusivity [SI]
43real(dp), allocatable, dimension(:)     :: coefq              ! q_{k+1/2} coefficients [SI]
44real(dp), allocatable, dimension(:,:)   :: coefd              ! d_k coefficients [SI]
45real(dp), allocatable, dimension(:,:)   :: alph               ! alpha_k coefficients [SI]
46real(dp), allocatable, dimension(:,:)   :: beta               ! beta_k coefficients [SI]
47real(dp)                                :: mu                 ! mu coefficient [SI]
48integer(di)                             :: index_breccia      ! Last index of the depth grid before having breccia
49integer(di)                             :: index_bedrock      ! Last index of the depth grid before having bedrock
50real(dp)                                :: volcapa            ! Soil volumetric heat capacity
51
52contains
53!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
54
55!=======================================================================
56SUBROUTINE ini_soil()
57!-----------------------------------------------------------------------
58! NAME
59!     ini_soil
60!
61! DESCRIPTION
62!     Allocate soil arrays based on grid dimensions.
63!
64! AUTHORS & DATE
65!     L. Lange, 2023
66!     JB Clement, 2023-2025
67!
68! NOTES
69!
70!-----------------------------------------------------------------------
71
72! DEPENDENCIES
73! ------------
74use geometry, only: ngrid, nslope, nsoil, nsoil_PCM
75
76! DECLARATION
77! -----------
78implicit none
79
80! CODE
81! ----
82allocate(layer(nsoil))
83allocate(mlayer(0:nsoil - 1))
84allocate(TI(ngrid,nsoil,nslope))
85allocate(mthermdiff(ngrid,0:nsoil - 1))
86allocate(thermdiff(ngrid,nsoil - 1))
87allocate(coefq(0:nsoil - 1))
88allocate(coefd(ngrid,nsoil - 1))
89allocate(alph(ngrid,nsoil - 1))
90allocate(beta(ngrid,nsoil - 1))
91allocate(inertiedat(ngrid,nsoil))
92if (.not. allocated(inertiedat_PCM)) allocate(inertiedat_PCM(ngrid,nsoil_PCM))
93if (.not. allocated(TI_PCM)) allocate(TI_PCM(ngrid,nsoil_PCM,nslope))
94
95END SUBROUTINE ini_soil
96!=======================================================================
97
98!=======================================================================
99SUBROUTINE end_soil
100!-----------------------------------------------------------------------
101! NAME
102!     end_soil
103!
104! DESCRIPTION
105!     Deallocate all soil arrays.
106!
107! AUTHORS & DATE
108!     L. Lange, 2023
109!     JB Clement, 2023-2025
110!
111! NOTES
112!
113!-----------------------------------------------------------------------
114
115! DECLARATION
116! -----------
117implicit none
118
119! CODE
120! ----
121if (allocated(layer)) deallocate(layer)
122if (allocated(mlayer)) deallocate(mlayer)
123if (allocated(TI)) deallocate(TI)
124if (allocated(mthermdiff)) deallocate(mthermdiff)
125if (allocated(thermdiff)) deallocate(thermdiff)
126if (allocated(coefq)) deallocate(coefq)
127if (allocated(coefd)) deallocate(coefd)
128if (allocated(alph)) deallocate(alph)
129if (allocated(beta)) deallocate(beta)
130if (allocated(inertiedat)) deallocate(inertiedat)
131if (allocated(inertiedat_PCM)) deallocate(inertiedat_PCM)
132if (allocated(TI_PCM)) deallocate(TI_PCM)
133
134END SUBROUTINE end_soil
135!=======================================================================
136
137!=======================================================================
138SUBROUTINE set_soil_config(do_soil_in,reg_thprop_dependp_in,flux_geo_in,depth_breccia_in,depth_bedrock_in)
139!-----------------------------------------------------------------------
140! NAME
141!     set_soil_config
142!
143! DESCRIPTION
144!     Setter for 'soil' configuration parameters.
145!
146! AUTHORS & DATE
147!     JB Clement, 02/2026
148!
149! NOTES
150!
151!-----------------------------------------------------------------------
152
153! DEPENDENCIES
154! ------------
155use utility, only: real2str, bool2str
156use display, only: print_msg
157
158! DECLARATION
159! -----------
160implicit none
161
162! ARGUMENTS
163! ---------
164logical(k4), intent(in) :: do_soil_in, reg_thprop_dependp_in
165real(dp),    intent(in) :: flux_geo_in, depth_breccia_in, depth_bedrock_in
166
167! CODE
168! ----
169do_soil = do_soil_in
170reg_thprop_dependp = reg_thprop_dependp_in
171flux_geo = flux_geo_in
172depth_breccia = depth_breccia_in
173depth_bedrock = depth_bedrock_in
174call print_msg('do_soil            = '//bool2str(do_soil))
175call print_msg('reg_thprop_dependp = '//bool2str(reg_thprop_dependp))
176call print_msg('flux_geo           = '//real2str(flux_geo))
177call print_msg('depth_breccia      = '//real2str(depth_breccia))
178call print_msg('depth_bedrock      = '//real2str(depth_bedrock))
179
180END SUBROUTINE set_soil_config
181!=======================================================================
182
183!=======================================================================
184SUBROUTINE set_TI_PCM(TI_PCM_in)
185!-----------------------------------------------------------------------
186! NAME
187!     set_TI_PCM
188!
189! DESCRIPTION
190!     Setter for 'TI_PCM'.
191!
192! AUTHORS & DATE
193!     JB Clement, 01/2026
194!
195! NOTES
196!
197!-----------------------------------------------------------------------
198
199! DECLARATION
200! -----------
201implicit none
202
203! ARGUMENTS
204! ---------
205real(dp), dimension(:,:,:), intent(in) :: TI_PCM_in
206
207! CODE
208! ----
209TI_PCM(:,:,:) = TI_PCM_in(:,:,:)
210
211END SUBROUTINE set_TI_PCM
212!=======================================================================
213
214!=======================================================================
215SUBROUTINE set_inertiedat_PCM(inertiedat_PCM_in)
216!-----------------------------------------------------------------------
217! NAME
218!     set_inertiedat_PCM
219!
220! DESCRIPTION
221!     Setter for 'inertiedat_PCM'.
222!
223! AUTHORS & DATE
224!     JB Clement, 01/2026
225!
226! NOTES
227!
228!-----------------------------------------------------------------------
229
230! DECLARATION
231! -----------
232implicit none
233
234! ARGUMENTS
235! ---------
236real(dp), dimension(:,:), intent(in) :: inertiedat_PCM_in
237
238! CODE
239! ----
240inertiedat_PCM(:,:) = inertiedat_PCM_in(:,:)
241
242END SUBROUTINE set_inertiedat_PCM
243!=======================================================================
244
245!=======================================================================
246SUBROUTINE set_soil(TI)
247!-----------------------------------------------------------------------
248! NAME
249!     set_soil
250!
251! DESCRIPTION
252!     Initialize soil depths and properties. Builds layer depths using
253!     exponential then power-law distribution; interpolates thermal inertia
254!     from PCM to PEM grid.
255!
256! AUTHORS & DATE
257!     E. Millour, 07/2006
258!     L. Lange, 2023
259!     JB Clement, 2023-2025
260!
261! NOTES
262!     Modifications:
263!          Aug.2010 EM: use NetCDF90 to load variables (enables using
264!                       r4 or r8 restarts independently of having compiled
265!                       the GCM in r4 or r8)
266!          June 2013 TN: Possibility to read files with a time axis
267!     The various actions and variable read/initialized are:
268!     1. Read/build layer (and midlayer) depth
269!     2. Interpolate thermal inertia and temperature on the new grid, if necessary
270!-----------------------------------------------------------------------
271
272! DEPENDENCIES
273! ------------
274use geometry, only: ngrid, nslope, nsoil, nsoil_PCM
275
276! DECLARATION
277! -----------
278implicit none
279
280! ARGUMENTS
281! ---------
282real(dp), dimension(:,:,:), intent(inout) :: TI ! Thermal inertia in the PEM [SI]
283
284! LOCAL VARIABLES
285! ---------------
286integer(di) :: ig, iloop, islope ! loop counters
287real(dp)    :: alpha, lay1       ! coefficients for building layers
288real(dp)    :: index_powerlaw    ! coefficient to match the power low grid with the exponential one
289
290! CODE
291! ----
292! 1. Depth coordinate
293! -------------------
294! mlayer distribution: For the grid in common with the PCM: lay1*(1+k^(2.9)*(1-exp(-k/20))
295! Then we use a power low : lay1*alpha**(k-1/2)
296lay1 = 2.e-4
297alpha = 2
298do iloop = 0,nsoil_PCM - 1
299    mlayer(iloop) = lay1*(1 + iloop**2.9*(1._dp - exp(-real(iloop,dp)/20._dp)))
300end do
301
302do iloop = 0,nsoil - 1
303    if (lay1*(alpha**(iloop - 0.5_dp)) > mlayer(nsoil_PCM - 1)) then
304        index_powerlaw = iloop
305        exit
306    end if
307end do
308
309do iloop = nsoil_PCM,nsoil - 1
310    mlayer(iloop) = lay1*(alpha**(index_powerlaw + (iloop - nsoil_PCM) - 0.5_dp))
311end do
312
313! Build layer()
314do iloop = 1,nsoil - 1
315    layer(iloop) = (mlayer(iloop) + mlayer(iloop - 1))/2._dp
316end do
317layer(nsoil) = 2._dp*mlayer(nsoil - 1) - mlayer(nsoil - 2)
318
319
320! 2. Thermal inertia (note: it is declared in comsoil_h)
321! ------------------
322do ig = 1,ngrid
323    do islope = 1,nslope
324        do iloop = 1,nsoil_PCM
325            TI(ig,iloop,islope) = TI_PCM(ig,iloop,islope)
326        end do
327        if (nsoil > nsoil_PCM) then
328            do iloop = nsoil_PCM + 1,nsoil
329                TI(ig,iloop,islope) = TI_PCM(ig,nsoil_PCM,islope)
330            end do
331        end if
332    end do
333end do
334
335! 3. Index for subsurface layering
336! ------------------
337index_breccia = 1
338do iloop = 1,nsoil - 1
339    if (depth_breccia >= layer(iloop)) then
340        index_breccia = iloop
341    else
342        exit
343    end if
344end do
345
346index_bedrock = 1
347do iloop = 1,nsoil - 1
348    if (depth_bedrock >= layer(iloop)) then
349        index_bedrock = iloop
350    else
351        exit
352    end if
353end do
354
355END SUBROUTINE set_soil
356!=======================================================================
357
358!=======================================================================
359SUBROUTINE build4PCM_soil(tsoil_avg,tsoil_dev,inertiesoil4PCM,tsoil4PCM,flux_geo4PCM)
360!-----------------------------------------------------------------------
361! NAME
362!     build4PCM_soil
363!
364! DESCRIPTION
365!     Reconstructs the soil temperature profile, thermal inertia and
366!     the geothermal flux for the PCM.
367!
368! AUTHORS & DATE
369!     JB Clement, 12/2025
370!     C. Metz, 01/2026
371!
372! NOTES
373!
374!-----------------------------------------------------------------------
375
376! DEPENDENCIES
377! ------------
378use geometry, only: nsoil_PCM
379use display,  only: print_msg
380
381! DECLARATION
382! -----------
383implicit none
384
385! ARGUMENTS
386! ---------
387real(dp), dimension(:,:,:), intent(in)  :: tsoil_avg, tsoil_dev
388real(dp), dimension(:,:,:), intent(out) :: tsoil4PCM, inertiesoil4PCM
389real(dp), dimension(:,:),   intent(out) :: flux_geo4PCM
390
391! CODE
392! ----
393call print_msg('> Building soil thermal inertia for the PCM')
394inertiesoil4PCM(:,:,:) = TI(:,1:nsoil_PCM,:)
395
396call print_msg('> Building soil temperature profile for the PCM')
397tsoil4PCM(:,:,:) = tsoil_avg(:,1:nsoil_PCM,:) + tsoil_dev(:,:,:)
398
399call print_msg('> Building geothermal flux for the PCM')
400flux_geo4PCM(:,:) = (tsoil_avg(:,nsoil_PCM + 1,:) - tsoil4PCM(:,nsoil_PCM,:))/(mlayer(nsoil_PCM + 1) - mlayer(nsoil_PCM))
401
402END SUBROUTINE build4PCM_soil
403!=======================================================================
404
405END MODULE soil
Note: See TracBrowser for help on using the repository browser.