source: trunk/LMDZ.COMMON/libf/evolution/config.F90 @ 4066

Last change on this file since 4066 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.4 KB
Line 
1MODULE config
2!-----------------------------------------------------------------------
3! NAME
4!     config
5!
6! DESCRIPTION
7!     Read and apply parameters for a PEM run from run_pem.def.
8!
9! AUTHORS & DATE
10!     R. Vandemeulebrouck
11!     JB Clement, 2023-2025
12!
13! NOTES
14!
15!-----------------------------------------------------------------------
16
17! DEPENDENCIES
18! ------------
19use numerics, only: dp, di, k4, minieps
20
21! DECLARATION
22! -----------
23implicit none
24
25character(7),  parameter :: rundef_name = 'run.def'
26character(11), parameter :: runPCMdef_name = 'run_PCM.def'
27character(12), parameter :: callphys_name = 'callphys.def'
28
29contains
30!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
31
32!=======================================================================
33SUBROUTINE read_rundef()
34!-----------------------------------------------------------------------
35! NAME
36!     read_rundef
37!
38! DESCRIPTION
39!     Read PEM runtime configuration from getin keys, then set
40!     module-scoped parameters accordingly.
41!
42! AUTHORS & DATE
43!     R. Vandemeulebrouck
44!     JB Clement, 2023-2025
45!
46! NOTES
47!
48!-----------------------------------------------------------------------
49
50! DEPENDENCIES
51! ------------
52#ifdef CPP_IOIPSL
53use IOIPSL,           only: getin
54#else
55! If not using IOIPSL, we still need to use (a local version of) getin
56use ioipsl_getincom,  only: getin
57#endif
58use evolution,        only: set_evolution_config
59use stopping_crit,    only: set_stopping_crit_config
60use soil,             only: set_soil_config
61use sorption,         only: set_sorption_config
62use glaciers,         only: set_glaciers_config
63use surf_ice,         only: set_surf_ice_config
64use ice_table,        only: set_ice_table_config
65use layered_deposits, only: set_layered_deposits_config
66use output,           only: set_output_config
67use orbit,            only: set_orbit_config
68use atmosphere,       only: set_atmosphere_config
69use stoppage,         only: stop_clean
70use display,          only: print_msg
71
72! DECLARATION
73! -----------
74implicit none
75
76! LOCAL VARIABLES
77! ---------------
78logical(k4) :: here, evol_orbit_l, evol_obl_l, evol_ecc_l, evol_lsp_l, do_soil_l, reg_thprop_dependp_l, do_sorption_l, hybrid_alt_coord_l
79logical(k4) :: icetable_equilibrium_l, icetable_dynamic_l, h2oice_flow_l, co2ice_flow_l, do_layering_l, impose_dust_ratio_l
80integer(di) :: output_rate_l
81integer(di) :: pem_ini_earth_date ! Initial year (in Earth years) of the simulation of the PEM defined in run.def
82real(dp)    :: dt_l, nmax_yr_run_l, h2oice_crit_l, co2ice_crit_l, ps_crit_l, max_change_obl_l, max_change_ecc_l, max_change_lsp_l
83real(dp)    :: flux_geo_l, depth_breccia_l, depth_bedrock_l, threshold_h2oice_cap_l, h2oice_huge_ini_l, d_dust_l, dust2ice_ratio_l
84
85! CODE
86! ----
87inquire(file = rundef_name,exist = here)
88if (.not. here) call stop_clean(__FILE__,__LINE__,'cannot find required file "'//rundef_name//'" (PEM)!',1)
89call print_msg('> Reading "'//rundef_name//'"')
90
91! Output
92! ~~~~~~
93output_rate_l = 1_di ! Default value: every year
94call getin('output_rate',output_rate_l)
95
96! Orbital parameters
97! ~~~~~~~~~~~~~~~~~~
98evol_orbit_l = .false.
99call getin('evol_orbit',evol_orbit_l)
100
101pem_ini_earth_date = 0_di
102call getin('pem_ini_earth_date',pem_ini_earth_date)
103
104evol_obl_l = .true.
105call getin('evol_obl',evol_obl_l)
106
107evol_ecc_l = .true.
108call getin('evol_ecc',evol_ecc_l)
109
110evol_lsp_l = .true.
111call getin('evol_lsp',evol_lsp_l)
112
113dt_l = 1._dp
114call getin('dt',dt_l)
115
116! Stopping criteria
117! ~~~~~~~~~~~~~~~~~
118nmax_yr_run_l = 100000000._dp
119call getin('nmax_yr_run',nmax_yr_run_l)
120
121h2oice_crit_l = 0.2_dp
122call getin('h2oice_crit',h2oice_crit_l)
123
124co2ice_crit_l = 0.2_dp
125call getin('co2ice_crit',co2ice_crit_l)
126
127ps_crit_l = 0.15_dp
128call getin('ps_crit',ps_crit_l)
129
130max_change_obl_l = 1._dp
131call getin('max_change_obl',max_change_obl_l)
132
133max_change_ecc_l = 5.e-3_dp
134call getin('max_change_ecc',max_change_ecc_l)
135
136max_change_lsp_l = 20._dp
137call getin('max_change_lsp',max_change_lsp_l)
138
139! Subsurface
140! ~~~~~~~~~~
141do_soil_l = .true.
142call getin('do_soil',do_soil_l)
143
144do_sorption_l = .false.
145call getin('do_sorption',do_sorption_l)
146
147reg_thprop_dependp_l = .false.
148call getin('reg_thprop_dependp',reg_thprop_dependp_l)
149
150flux_geo_l = 0._dp
151call getin('flux_geo',flux_geo_l)
152
153depth_breccia_l = 10._dp
154call getin('depth_breccia',depth_breccia_l)
155
156depth_bedrock_l = 1000._dp
157call getin('depth_bedrock',depth_bedrock_l)
158
159icetable_equilibrium_l = .true.
160call getin('icetable_equilibrium',icetable_equilibrium_l)
161
162icetable_dynamic_l = .false.
163call getin('icetable_dynamic',icetable_dynamic_l)
164
165! Ice management
166! ~~~~~~~~~~~~~~
167h2oice_huge_ini_l = 9200._dp ! [kg/m^2]; Default = 10 m
168call getin('h2oice_huge_ini',h2oice_huge_ini_l)
169
170threshold_h2oice_cap_l = 460._dp ! kg.m-2 (= 0.5 m)
171call getin('threshold_h2oice_cap',threshold_h2oice_cap_l)
172
173h2oice_flow_l = .true.
174call getin('h2oice_flow',h2oice_flow_l)
175
176co2ice_flow_l = .true.
177call getin('co2ice_flow',co2ice_flow_l)
178
179! Layering
180! ~~~~~~~~
181do_layering_l = .false.
182call getin('do_layering',do_layering_l)
183
184d_dust_l = 5.78e-2_dp ! kg.m-2.y-1 (= 1.e-9 kg.m-2.s-1)
185call getin('d_dust',d_dust_l)
186
187impose_dust_ratio_l = .false.
188call getin('impose_dust_ratio',impose_dust_ratio_l)
189
190dust2ice_ratio_l = 0.01_dp
191call getin('dust2ice_ratio',dust2ice_ratio_l)
192
193! Setters
194call set_output_config(output_rate_l)
195call set_orbit_config(evol_orbit_l,evol_obl_l,evol_ecc_l,evol_lsp_l,max_change_obl_l,max_change_ecc_l,max_change_lsp_l)
196call set_evolution_config(pem_ini_earth_date,dt_l,nmax_yr_run_l)
197call set_stopping_crit_config(h2oice_crit_l,co2ice_crit_l,ps_crit_l)
198call set_soil_config(do_soil_l,reg_thprop_dependp_l,flux_geo_l,depth_breccia_l,depth_bedrock_l)
199call set_sorption_config(do_sorption_l)
200call set_ice_table_config(icetable_equilibrium_l,icetable_dynamic_l)
201call set_surf_ice_config(threshold_h2oice_cap_l,h2oice_huge_ini_l)
202call set_glaciers_config(h2oice_flow_l,co2ice_flow_l)
203call set_layered_deposits_config(do_layering_l,impose_dust_ratio_l,d_dust_l,dust2ice_ratio_l)
204
205! Read "run_PCM.def" parameters
206hybrid_alt_coord_l = .true. ! Default setting
207call get_hybrid(hybrid_alt_coord_l)
208call set_atmosphere_config(hybrid_alt_coord_l)
209
210! Check incompatibilities
211call check_config_incompatibility()
212
213END SUBROUTINE read_rundef
214!=======================================================================
215
216!=======================================================================
217SUBROUTINE check_config_incompatibility()
218!-----------------------------------------------------------------------
219! NAME
220!     check_config_incompatibility
221!
222! DESCRIPTION
223!     Check incompatibilities in the PEM runtime configuration.
224!
225! AUTHORS & DATE
226!     JB Clement, 02/2026
227!
228! NOTES
229!
230!-----------------------------------------------------------------------
231
232! DEPENDENCIES
233! ------------
234use stoppage,  only: stop_clean
235use soil,      only: do_soil, reg_thprop_dependp, flux_geo
236use sorption,  only: do_sorption
237use orbit,     only: evol_orbit
238use evolution, only: pem_ini_date
239use ice_table, only: icetable_equilibrium, icetable_dynamic
240use display,   only: print_msg
241
242! DECLARATION
243! -----------
244implicit none
245
246! CODE
247! ----
248! Warnings (possible incompatibilities)
249if (evol_orbit .and. abs(pem_ini_date) < minieps) call print_msg('Warning: evol_orbit = .true. but the initial date of the PEM is set to 0!')
250
251! Errors (true incompatibilities)
252if (.not. do_soil) then
253    if (icetable_equilibrium .or. icetable_dynamic) call stop_clean(__FILE__,__LINE__,'ice table must be used when do_soil = true!',1)
254    if (abs(flux_geo) > minieps) call stop_clean(__FILE__,__LINE__,'soil is not activated but flux_geo /= 0!',1)
255    if (reg_thprop_dependp) call stop_clean(__FILE__,__LINE__,'regolith properties vary according to Ps only when soil = true!',1)
256    if (do_sorption) call stop_clean(__FILE__,__LINE__,'do_soil must be true when do_sorption = true!',1)
257end if
258
259END SUBROUTINE check_config_incompatibility
260!=======================================================================
261
262!=======================================================================
263SUBROUTINE read_callphys()
264!-----------------------------------------------------------------------
265! NAME
266!     read_callphys
267!
268! DESCRIPTION
269!     Read physics runtime configuration from getin keys, then set
270!     module-scoped parameters accordingly.
271!
272! AUTHORS & DATE
273!     JB Clement, 01/2026
274!
275! NOTES
276!     To work, it needs that "run_PEM.def" hols a line with
277!     "INCLUDEDEF=callphys.def".
278!-----------------------------------------------------------------------
279
280! DEPENDENCIES
281! ------------
282#ifdef CPP_IOIPSL
283use IOIPSL,          only: getin
284#else
285! If not using IOIPSL, we still need to use (a local version of) getin
286use ioipsl_getincom, only: getin
287#endif
288use atmosphere,      only: CO2cond_ps_PCM
289use stoppage,        only: stop_clean
290use display,         only: print_msg
291
292! DECLARATION
293! -----------
294implicit none
295
296! LOCAL VARIABLES
297! ---------------
298logical(k4) :: here
299
300! CODE
301! ----
302inquire(file = callphys_name,exist = here)
303if (.not. here) call stop_clean(__FILE__,__LINE__,'cannot find required file "'//callphys_name//'"!',1)
304call print_msg('> Reading "'//callphys_name//'"')
305
306CO2cond_ps_PCM = 1._dp ! Default value
307call getin("CO2cond_ps",CO2cond_ps_PCM)
308if (CO2cond_ps_PCM < 0._dp .or. CO2cond_ps_PCM > 1._dp) call stop_clean(__FILE__,__LINE__,'Value for ''CO2cond_ps'' is not between 0 and 1. Please, specify a correct value in "'//callphys_name//'"!',1)
309
310END SUBROUTINE read_callphys
311!=======================================================================
312
313!=======================================================================
314SUBROUTINE get_hybrid(hybrid)
315!-----------------------------------------------------------------------
316! NAME
317!     get_hybrid
318!
319! DESCRIPTION
320!     Get the key definition in "run_PCM.def".
321!
322! AUTHORS & DATE
323!     JB Clement, 12/2025
324!
325! NOTES
326!
327!-----------------------------------------------------------------------
328
329! DEPENDENCIES
330! ------------
331use stoppage, only: stop_clean
332use display,  only: print_msg
333use utility,  only: bool2str
334
335! DECLARATION
336! -----------
337implicit none
338
339! ARGUMENTS
340! ---------
341logical(k4), intent(inout) :: hybrid
342
343! LOCAL VARIABLES
344! ---------------
345integer(di)    :: ierr, funit, eq_pos
346logical(k4)    :: here, found, hybrid_in
347character(256) :: line
348character(128) :: key, res
349
350! CODE
351! ----
352inquire(file = runPCMdef_name,exist = here)
353if (.not. here) call stop_clean(__FILE__,__LINE__,'cannot find required file "'//runPCMdef_name//'"!',1)
354call print_msg('> Reading "'//runPCMdef_name//'"')
355open(newunit = funit,file = runPCMdef_name,status = 'old',form = 'formatted',action = 'read',iostat = ierr)
356if (ierr /= 0) call stop_clean(__FILE__,__LINE__,'error opening file "'//runPCMdef_name//'"!',ierr)
357
358found = .false.
359hybrid_in = hybrid
360do
361    ! Read the line
362    read(funit,'(a)',iostat = ierr) line
363    if (ierr /= 0) exit
364
365    ! Skip empty lines and comments
366    if (trim(line) == '') cycle
367    if (line(1:1) == '#' .or. line(1:1) == '!') cycle
368
369    ! Get the position of equal sign
370    eq_pos = index(line,'=')
371    if (eq_pos == 0) cycle
372
373    ! Get the key and its value/result
374    key = adjustl(trim(line(:eq_pos - 1)))
375    res = adjustl(trim(line(eq_pos + 1:)))
376
377    ! Check the key
378    if (trim(key) == 'hybrid') then
379        read(res,*,iostat = ierr) hybrid
380        if (ierr == 0) found = .true.
381        exit
382    end if
383end do
384
385close(funit)
386
387if (.not. found) call print_msg('Warning: key ''hybrid'' not found in the file "'//runPCMdef_name//'"!')
388if (hybrid .eqv. hybrid_in) call print_msg('USING DEFAULTS : hybrid = '//bool2str(hybrid_in))
389
390END SUBROUTINE get_hybrid
391!=======================================================================
392
393!=======================================================================
394SUBROUTINE read_controldata()
395!-----------------------------------------------------------------------
396! NAME
397!     read_controldata
398!
399! DESCRIPTION
400!     Read 'controle' data in "startfi.nc".
401!
402! AUTHORS & DATE
403!     JB Clement, 01/2026
404!
405! NOTES
406!
407!-----------------------------------------------------------------------
408
409! DEPENDENCIES
410! ------------
411use io_netcdf, only: open_nc, close_nc, startfi_name, get_dim_nc, get_var_nc
412use physics,   only: init_physics
413use orbit,     only: init_orbit
414use soil,      only: volcapa
415use stoppage,  only: stop_clean
416use display,   only: print_msg
417
418! DECLARATION
419! -----------
420implicit none
421
422! LOCAL VARIABLES
423! ---------------
424integer(di)                         :: nindex ! Size of dimension 'index'
425real(dp), dimension(:), allocatable :: controle
426
427! CODE
428! ----
429call print_msg('> Reading control data ("'//startfi_name//'")')
430! Open the "startfi.nc" file
431call open_nc(startfi_name,'read')
432
433! Get the dimension size of 'index'
434call get_dim_nc('index',nindex)
435
436! Get 'controle'
437allocate(controle(nindex))
438call get_var_nc('controle',controle)
439
440! Close the file
441call close_nc(startfi_name)
442
443! Initialize physical data
444!     Arguments order: rad, g, mugaz, rcp
445call print_msg('    > Initializing physical constants')
446call init_physics(controle(5),controle(7),controle(8),controle(9))
447
448! Initialize soil data
449call print_msg('    > Initializing soil parameters')
450volcapa = controle(35)
451if (abs(volcapa) < minieps) call stop_clean(__FILE__,__LINE__,'volcapa is 0 in "'//startfi_name//'"!',1)
452
453! Initialize orbital data
454!     Arguments order: Obliquity, Perihelion, Aphelion, Date of perihelion, Year length, Sol length
455call print_msg('    > Initializing orbital characteristics of the planet')
456call init_orbit(controle(18),controle(15),controle(16),controle(17),controle(14),controle(10))
457
458deallocate(controle)
459
460END SUBROUTINE read_controldata
461!=======================================================================
462
463END MODULE config
Note: See TracBrowser for help on using the repository browser.