source: trunk/LMDZ.COMMON/libf/evolution/tracers.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: 9.3 KB
Line 
1MODULE tracers
2!-----------------------------------------------------------------------
3! NAME
4!     tracers
5!
6! DESCRIPTION
7!     Tracer species management for tracking.
8!
9! AUTHORS & DATE
10!     JB Clement, 12/2025
11!
12! NOTES
13!
14!-----------------------------------------------------------------------
15
16! DEPENDENCIES
17! ------------
18use numerics, only: dp, di, k4
19
20! DECLARATION
21! -----------
22implicit none
23
24! PARAMETERS
25! ----------
26character(11),                                parameter :: traceurdef_name = 'traceur.def'
27integer(di),                                  protected :: nq        ! Number of tracers
28character(30), dimension(:),     allocatable, protected :: qnames    ! Names of tracers
29real(dp),      dimension(:,:,:), allocatable, protected :: q_PCM     ! Tracers in the "start.nc" at the beginning
30integer(di),                                  protected :: iPCM_qh2o ! Index for H2O vapor tracer from PCM
31real(dp),      dimension(:),     allocatable, protected :: mmol      ! Molar masses of tracers [g/mol]
32
33contains
34!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
35
36!=======================================================================
37SUBROUTINE ini_tracers()
38!-----------------------------------------------------------------------
39! NAME
40!     ini_tracers
41!
42! DESCRIPTION
43!     Allocate tracer molar mass array.
44!
45! AUTHORS & DATE
46!     JB Clement, 12/2025
47!
48! NOTES
49!
50!-----------------------------------------------------------------------
51
52! DEPENDENCIES
53! ------------
54use stoppage, only: stop_clean
55use geometry, only: ngrid, nlayer
56use display,  only: print_msg
57use utility,  only: int2str
58
59! DECLARATION
60! -----------
61implicit none
62
63! LOCAL VARIABLES
64! ---------------
65logical(k4) :: here
66integer(di) :: i, ierr, funit
67
68! CODE
69! ----
70inquire(file = traceurdef_name,exist = here)
71if (.not. here) call stop_clean(__FILE__,__LINE__,'cannot find required file "'//traceurdef_name//'"!',1)
72call print_msg('> Reading "'//traceurdef_name//'"')
73open(newunit = funit,file = traceurdef_name,status = 'old',form = 'formatted',action = 'read',iostat = ierr)
74if (ierr /= 0) call stop_clean(__FILE__,__LINE__,'error opening file "'//traceurdef_name//'"!',ierr)
75read(funit,*) nq
76call print_msg('nq = '//int2str(nq))
77
78! Allocation
79if (.not. allocated(mmol)) allocate(mmol(nq))
80if (.not. allocated(qnames)) allocate(qnames(nq))
81if (.not. allocated(q_PCM)) allocate(q_PCM(ngrid,nlayer,nq))
82
83! Initialize the names of tracers
84do i = 1,nq
85    read(funit,*,iostat = ierr) qnames(i)
86    if (ierr /= 0) call stop_clean(__FILE__,__LINE__,'error reading the names of tracers in "'//traceurdef_name//'"!',1)
87end do
88close(funit)
89
90! Initialize the indices of tracers
91iPCM_qh2o = -1
92do i = 1,nq
93    if (qnames(i) == "h2o_vap") then
94        iPCM_qh2o = i
95        mmol(i) = 18._dp
96    end if
97end do
98
99! Checking if everything has been found
100if (iPCM_qh2o < 0) call stop_clean(__FILE__,__LINE__,'H2O vapour index not found!',1)
101
102END SUBROUTINE ini_tracers
103!=======================================================================
104
105!=======================================================================
106SUBROUTINE end_tracers()
107!-----------------------------------------------------------------------
108! NAME
109!     end_tracers
110!
111! DESCRIPTION
112!     Deallocate tracer molar mass array.
113!
114! AUTHORS & DATE
115!     JB Clement, 12/2025
116!
117! NOTES
118!
119!-----------------------------------------------------------------------
120
121! DECLARATION
122! -----------
123implicit none
124
125! CODE
126! ----
127if (allocated(mmol)) deallocate(mmol)
128if (allocated(qnames)) deallocate(qnames)
129if (allocated(q_PCM)) deallocate(q_PCM)
130
131END SUBROUTINE end_tracers
132!=======================================================================
133
134!=======================================================================
135SUBROUTINE set_q_PCM(q_PCM_in,iq)
136!-----------------------------------------------------------------------
137! NAME
138!     set_q_PCM
139!
140! DESCRIPTION
141!     Setter for 'q_PCM'.
142!
143! AUTHORS & DATE
144!     JB Clement, 12/2025
145!
146! NOTES
147!
148!-----------------------------------------------------------------------
149
150! DEPENDENCIES
151! ------------
152use geometry, only: dyngrd2vect, nlon, nlat, ngrid
153
154! DECLARATION
155! -----------
156implicit none
157
158! ARGUMENTS
159! ---------
160real(dp), dimension(:,:,:), intent(in) :: q_PCM_in
161integer(di),                intent(in) :: iq
162
163! LOCAL VARIABLES
164! ---------------
165integer(di) :: l
166
167! CODE
168! ----
169do l = 1,size(q_PCM_in,3)
170    call dyngrd2vect(nlon + 1,nlat,ngrid,q_PCM_in(:,:,l),q_PCM(:,l,iq))
171end do
172
173END SUBROUTINE set_q_PCM
174!=======================================================================
175
176!=======================================================================
177SUBROUTINE adapt_tracers2pressure(ps_avg_glob_old,ps_avg_glob,ps_ts,q_h2o_ts,q_co2_ts)
178!-----------------------------------------------------------------------
179! NAME
180!     adapt_tracers2pressure
181!
182! DESCRIPTION
183!     Adapt the timeseries of tracers VMR to the new pressure.
184!
185! AUTHORS & DATE
186!     JB Clement, 01/2026
187!
188! NOTES
189!
190!-----------------------------------------------------------------------
191
192! DEPENDENCIES
193! ------------
194use geometry,   only: ngrid, nday
195use atmosphere, only: ap, bp
196use display,    only: print_msg
197
198! DECLARATION
199! -----------
200implicit none
201
202! ARGUMENTS
203! ---------
204real(dp),                        intent(in)    :: ps_avg_glob_old, ps_avg_glob
205real(dp), dimension(ngrid,nday), intent(inout) :: ps_ts, q_h2o_ts, q_co2_ts
206
207! LOCAL VARIABLES
208! ---------------
209real(dp), dimension(ngrid,nday) :: plev1, plev2, plev1_old, plev2_old
210
211! CODE
212! ----
213call print_msg("> Adapting the timeseries of tracers VMR to the new pressure")
214
215! Build the first levels of pressure
216plev1_old(:,:) = ap(1) + bp(1)*ps_ts(:,:)
217plev2_old(:,:) = ap(2) + bp(2)*ps_ts(:,:)
218
219! Evolve timeseries of surface pressure
220ps_ts(:,:) = ps_ts(:,:)*ps_avg_glob/ps_avg_glob_old
221
222! Build the first levels of pressure
223plev1(:,:) = ap(1) + bp(1)*ps_ts(:,:)
224plev2(:,:) = ap(2) + bp(2)*ps_ts(:,:)
225
226! Adapt the timeseries of VMR tracers
227! H2O
228q_h2o_ts(:,:) = q_h2o_ts(:,:)*(plev1_old(:,:) - plev2_old(:,:))/(plev1(:,:) - plev2(:,:))
229where (q_h2o_ts < 0._dp) q_h2o_ts = 0._dp
230where (q_h2o_ts > 1._dp) q_h2o_ts = 1._dp
231! CO2
232q_co2_ts(:,:) = q_co2_ts(:,:)*(plev1_old(:,:) - plev2_old(:,:))/(plev1(:,:) - plev2(:,:))
233where (q_co2_ts < 0._dp) q_co2_ts = 0._dp
234where (q_co2_ts > 1._dp) q_co2_ts = 1._dp
235
236END SUBROUTINE adapt_tracers2pressure
237!=======================================================================
238
239!=======================================================================
240SUBROUTINE build4PCM_tracers(ps4PCM,q4PCM)
241!-----------------------------------------------------------------------
242! NAME
243!     build4PCM_tracers
244!
245! DESCRIPTION
246!     Reconstructs the tracer VMRs for the PCM.
247!
248! AUTHORS & DATE
249!     JB Clement, 12/2025
250!     C. Metz, 01/2026
251!
252! NOTES
253!
254!-----------------------------------------------------------------------
255
256! DEPENDENCIES
257! ------------
258use atmosphere, only: ap, bp, ps_PCM
259use geometry,   only: ngrid, nlayer
260use display,    only: print_msg
261
262! DECLARATION
263! -----------
264implicit none
265
266! ARGUMENTS
267! ---------
268real(dp), dimension(:),     intent(in)  :: ps4PCM
269real(dp), dimension(:,:,:), intent(out) :: q4PCM
270
271! LOCAL VARIABLES
272! ---------------
273integer(di)                           :: i, l, iq
274real(dp)                              :: extra_mass
275real(dp), dimension(:,:), allocatable :: dz_plev_PCM, dz_plev_new
276
277! CODE
278! ----
279call print_msg('> Building tracers for the PCM')
280allocate(dz_plev_PCM(ngrid,nlayer),dz_plev_new(ngrid,nlayer))
281
282! Compute thickness of pressure levels (= atmospheric layers)
283do l = 1,nlayer
284    dz_plev_PCM(:,l) = ap(l) + bp(l)*ps_PCM(:) - ap(l + 1) - bp(l + 1)*ps_PCM(:)
285    dz_plev_new(:,l) = ap(l) + bp(l)*ps_PCM(:) - ap(l + 1) - bp(l + 1)*ps4PCM(:)
286end do
287
288! Reconstruct tracers
289do iq = 1,nq
290    do l = 1,nlayer
291        do i = 1,ngrid
292            ! Standard scaling
293            q4PCM(i,l,iq) = q_PCM(i,l,iq)*dz_plev_PCM(i,l)/dz_plev_new(i,l)
294
295            ! CO2 logic: account for the dry air mass change
296            if (trim(qnames(iq)) == "co2") q4PCM(i,l,iq) = q4PCM(i,l,iq) + (dz_plev_new(i,l) - dz_plev_PCM(i,l)) / dz_plev_new(i,l)
297        end do
298    end do
299end do
300
301! Mass conservation and clipping
302do iq = 1,nq
303    do i = 1,ngrid
304        do l = 1,nlayer
305            ! Clipping negative values
306            if (q4PCM(i,l,iq) < 0._dp) q4PCM(i,l,iq) = 0._dp
307
308            ! VMR limit check (ignore number density tracers)
309            if (q4PCM(i,l,iq) > 1._dp .and.              &
310                (qnames(iq) /= "dust_number") .and.      &
311                (qnames(iq) /= "ccn_number") .and.       &
312                (qnames(iq) /= "stormdust_number") .and. &
313                (qnames(iq) /= "topdust_number")) then
314
315                if (l < nlayer) then
316                    extra_mass = (q4PCM(i,l,iq) - 1.)*dz_plev_new(i,l) ! extra_mass units: [VMR*pressure]
317                    q4PCM(i,l,iq) = 1._dp
318
319                    ! Transfer extra mass to the layer above
320                    ! Note: We divide by dz_plev_new(i,l + 1) to convert back to VMR
321                    q4PCM(i,l + 1,iq) = q4PCM(i,l + 1,iq) + extra_mass/dz_plev_new(i,l + 1)
322                else
323                    q4PCM(i,l,iq) = 1._dp
324                end if
325            end if
326        end do
327    end do
328end do
329
330deallocate(dz_plev_PCM,dz_plev_new)
331
332END SUBROUTINE build4PCM_tracers
333!=======================================================================
334
335END MODULE tracers
Note: See TracBrowser for help on using the repository browser.