Ignore:
Timestamp:
Feb 12, 2026, 9:09:12 AM (2 weeks ago)
Author:
jbclement
Message:

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:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/LMDZ.COMMON/libf/evolution/tendencies.F90

    r3991 r4065  
    1616!-----------------------------------------------------------------------
    1717
     18! DEPENDENCIES
     19! ------------
     20use numerics, only: dp, di, minieps, tol
     21
    1822! DECLARATION
    1923! -----------
     
    2428
    2529!=======================================================================
    26 SUBROUTINE compute_tend(ngrid,nslope,min_ice,d_ice)
     30SUBROUTINE compute_tend(min_ice,d_ice)
    2731!-----------------------------------------------------------------------
    2832! NAME
     
    4751! ARGUMENTS
    4852! ---------
    49 integer,                         intent(in)  :: ngrid
    50 integer,                         intent(in)  :: nslope
    51 real, dimension(ngrid,nslope,2), intent(in)  :: min_ice ! Minima of ice at each point for the PCM years
    52 real, dimension(ngrid,nslope),   intent(out) :: d_ice   ! Evolution of perennial ice
     53real(dp), dimension(:,:,:), intent(in)  :: min_ice
     54real(dp), dimension(:,:),   intent(out) :: d_ice
    5355
    5456! CODE
    5557! ----
    5658! We compute the difference
    57 d_ice = min_ice(:,:,2) - min_ice(:,:,1)
     59d_ice(:,:) = min_ice(:,:,2) - min_ice(:,:,1)
    5860
    5961! If the difference is too small, then there is no evolution
    60 where (abs(d_ice) < 1.e-10) d_ice = 0.
     62where (abs(d_ice) < minieps) d_ice = 0._dp
    6163
    6264! If the minimum over the last year is 0, then we have no perennial ice
    63 where (abs(min_ice(:,:,2)) < 1.e-10) d_ice = 0.
     65where (abs(min_ice(:,:,2)) < minieps) d_ice = 0._dp
    6466
    6567END SUBROUTINE compute_tend
     
    6769
    6870!=======================================================================
    69 SUBROUTINE recomp_tend_co2(ngrid,nslope,timelen,d_co2ice_phys,d_co2ice_ini,co2ice,emissivity, &
    70                            vmr_co2_PCM,vmr_co2_PEM,ps_PCM,ps_avg_global_ini,ps_avg_global)
    71 !-----------------------------------------------------------------------
    72 ! NAME
    73 !     recomp_tend_co2
     71SUBROUTINE evolve_tend_co2(d_co2ice_ini,co2ice,emissivity,q_co2_ts_ini,q_co2_ts,ps_PCM,ps_avg_glob_ini,ps_avg_global,d_co2ice)
     72!-----------------------------------------------------------------------
     73! NAME
     74!     evolve_tend_co2
    7475!
    7576! DESCRIPTION
     
    8687! DEPENDENCIES
    8788! ------------
    88 use constants_marspem_mod, only : alpha_clap_co2, beta_clap_co2, sigmaB, Lco2, sols_per_my, sec_per_sol
     89use geometry, only: ngrid, nslope, nday
     90use planet,   only: alpha_clap_co2, beta_clap_co2, sigmaB, Lco2, m_co2, A, B
     91use orbit,    only: yr_len_sol, sol_len_s
     92use display,  only: print_msg
     93use utility,  only: real2str
    8994
    9095! DECLARATION
     
    9499! ARGUMENTS
    95100! ---------
    96 integer,                        intent(in)    :: timelen, ngrid, nslope ! Time length, # of grid points and slopes
    97 real, dimension(ngrid,timelen), intent(in)    :: vmr_co2_PCM            ! CO2 VMR in PCM first layer
    98 real, dimension(ngrid,timelen), intent(in)    :: vmr_co2_PEM            ! CO2 VMR in PEM first layer
    99 real, dimension(ngrid,timelen), intent(in)    :: ps_PCM                 ! Surface pressure in PCM
    100 real,                           intent(in)    :: ps_avg_global_ini      ! Global average pressure (initial)
    101 real,                           intent(in)    :: ps_avg_global          ! Global average pressure (current)
    102 real, dimension(ngrid,nslope),  intent(in)    :: d_co2ice_ini           ! Initial CO2 ice evolution
    103 real, dimension(ngrid,nslope),  intent(in)    :: co2ice                 ! CO2 ice surface [kg/m^2]
    104 real, dimension(ngrid,nslope),  intent(in)    :: emissivity             ! Emissivity
    105 real, dimension(ngrid,nslope),  intent(inout) :: d_co2ice_phys          ! Updated CO2 ice evolution
     101real(dp), dimension(:,:), intent(in)    :: q_co2_ts, q_co2_ts_ini, ps_PCM, d_co2ice_ini, co2ice, emissivity
     102real(dp),                 intent(in)    :: ps_avg_global, ps_avg_glob_ini
     103real(dp), dimension(:,:), intent(inout) :: d_co2ice
    106104
    107105! LOCAL VARIABLES
    108106! ---------------
    109 integer :: i, t, islope
    110 real    :: coef, avg
     107integer(di)                     :: i, iday, islope
     108real(dp)                        :: coef, avg
     109real(dp), dimension(ngrid,nday) :: vmr_co2_ts, vmr_co2_ts_ini
    111110
    112111! CODE
    113112! ----
    114 write(*,*) "> Updating the CO2 ice tendency for the new pressure"
    115 
    116 ! Evolution of the water ice for each physical point
     113call print_msg("> Evolving the CO2 ice tendency according to the new pressure")
     114call print_msg('Old CO2 ice tendencies [kg/m2/yr] (min|max): '//real2str(minval(d_co2ice))//' | '//real2str(maxval(d_co2ice)))
     115
     116! Compute volume mixing ratio
     117vmr_co2_ts(:,:) = q_co2_ts(:,:)/(A*q_co2_ts(:,:) + B)/m_co2
     118vmr_co2_ts_ini(:,:) = q_co2_ts_ini(:,:)/(A*q_co2_ts_ini(:,:) + B)/m_co2
     119
     120! Recompute the tendency
    117121do i = 1,ngrid
    118122    do islope = 1,nslope
    119         coef = sols_per_my*sec_per_sol*emissivity(i,islope)*sigmaB/Lco2
    120         avg = 0.
    121         if (co2ice(i,islope) > 1.e-4 .and. abs(d_co2ice_phys(i,islope)) > 1.e-5) then
    122             do t = 1,timelen
    123                 avg = avg + (beta_clap_co2/(alpha_clap_co2-log(vmr_co2_PCM(i,t)*ps_PCM(i,t)/100.)))**4 &
    124                       - (beta_clap_co2/(alpha_clap_co2-log(vmr_co2_PEM(i,t)*ps_PCM(i,t)*(ps_avg_global/ps_avg_global_ini)/100.)))**4
    125             enddo
    126             if (avg < 1.e-4) avg = 0.
    127             d_co2ice_phys(i,islope) = d_co2ice_ini(i,islope) - coef*avg/timelen
    128         endif
    129     enddo
    130 enddo
    131 
    132 END SUBROUTINE recomp_tend_co2
    133 !=======================================================================
    134 
    135 !=======================================================================
    136 SUBROUTINE recomp_tend_h2o(h2oice_depth_old,h2oice_depth_new,tsurf,tsoil_PEM_timeseries_old,tsoil_PEM_timeseries_new,d_h2oice)
    137 !-----------------------------------------------------------------------
    138 ! NAME
    139 !     recomp_tend_h2o
     123        coef = yr_len_sol*sol_len_s*emissivity(i,islope)*sigmaB/Lco2
     124        avg = 0._dp
     125        if (co2ice(i,islope) > 1.e-4_dp .and. abs(d_co2ice(i,islope)) > 1.e-5_dp) then
     126            do iday = 1,nday
     127                avg = avg + (beta_clap_co2/(alpha_clap_co2 - log(q_co2_ts_ini(i,iday)*ps_PCM(i,iday)/100._dp)))**4 &
     128                      - (beta_clap_co2/(alpha_clap_co2 - log(vmr_co2_ts(i,iday)*ps_PCM(i,iday)*(ps_avg_global/ps_avg_glob_ini)/100._dp)))**4
     129            end do
     130            if (avg < 1.e-4_dp) avg = 0._dp
     131            d_co2ice(i,islope) = d_co2ice_ini(i,islope) - coef*avg/nday
     132        end if
     133    end do
     134end do
     135call print_msg('New CO2 ice tendencies [kg/m2/yr] (min|max): '//real2str( minval(d_co2ice))//' | '//real2str(maxval(d_co2ice)))
     136
     137END SUBROUTINE evolve_tend_co2
     138!=======================================================================
     139
     140!=======================================================================
     141SUBROUTINE evolve_tend_h2o(h2oice_depth_old,h2oice_depth_new,tsurf,tsoil_ts_old,tsoil_ts_new,d_h2oice)
     142!-----------------------------------------------------------------------
     143! NAME
     144!     evolve_tend_h2o
    140145!
    141146! DESCRIPTION
     
    160165! ARGUMENTS
    161166! ---------
    162 real,                 intent(in)    :: h2oice_depth_old ! Old H2O ice depth
    163 real,                 intent(in)    :: h2oice_depth_new ! New H2O ice depth
    164 real,                 intent(in)    :: tsurf            ! Surface temperature
    165 real, dimension(:,:), intent(in)    :: tsoil_PEM_timeseries_old ! Old soil temperature time series
    166 real, dimension(:,:), intent(in)    :: tsoil_PEM_timeseries_new ! New soil temperature time series
    167 real,                 intent(inout) :: d_h2oice         ! Evolution of perennial ice
     167real(dp),                 intent(in)    :: h2oice_depth_old ! Old H2O ice depth
     168real(dp),                 intent(in)    :: h2oice_depth_new ! New H2O ice depth
     169real(dp),                 intent(in)    :: tsurf            ! Surface temperature
     170real(dp), dimension(:,:), intent(in)    :: tsoil_ts_old    ! Old soil temperature time series
     171real(dp), dimension(:,:), intent(in)    :: tsoil_ts_new    ! New soil temperature time series
     172real(dp),                 intent(inout) :: d_h2oice         ! Evolution of perennial ice
    168173
    169174! LOCAL VARIABLES
    170175! ---------------
    171 real            :: Rz_old, Rz_new, R_dec, hum_dec, psv_max_old, psv_max_new
    172 integer         :: t
    173 real, parameter :: coef_diff = 4.e-4 ! Diffusion coefficient
    174 real, parameter :: zcdv = 0.0325     ! Drag coefficient
     176real(dp)            :: Rz_old, Rz_new, R_dec, hum_dec, psv_max_old, psv_max_new
     177integer(di)         :: iday
     178real(dp), parameter :: coef_diff = 4.e-4 ! Diffusion coefficient
     179real(dp), parameter :: zcdv = 0.0325     ! Drag coefficient
    175180
    176181! CODE
     
    182187
    183188! The maxmimum of the daily averages over one year for the saturation vapor pressure at the ice table location
    184 psv_max_old = 0.
    185 psv_max_new = 0.
    186 do t = 1,size(tsoil_PEM_timeseries_old,2)
    187     psv_max_old = max(psv_max_old,psv(itp_tsoil(tsoil_PEM_timeseries_old(:,t),tsurf,h2oice_depth_old)))
    188     psv_max_new = max(psv_max_new,psv(itp_tsoil(tsoil_PEM_timeseries_new(:,t),tsurf,h2oice_depth_new)))
    189 enddo
     189psv_max_old = 0._dp
     190psv_max_new = 0._dp
     191do iday = 1,size(tsoil_ts_old,2)
     192    psv_max_old = max(psv_max_old,psv(itp_tsoil(tsoil_ts_old(:,iday),tsurf,h2oice_depth_old)))
     193    psv_max_new = max(psv_max_new,psv(itp_tsoil(tsoil_ts_new(:,iday),tsurf,h2oice_depth_new)))
     194end do
    190195
    191196! Lower humidity due to growing lag layer (higher depth)
    192 if (abs(psv_max_old) < 1.e2*epsilon(1.)) then
    193     hum_dec = 1.
     197if (abs(psv_max_old) < tol) then
     198    hum_dec = 1._dp
    194199else
    195200    hum_dec = psv_max_new/psv_max_old ! Decrease because of lower water vapor pressure at the new depth
    196 endif
     201end if
    197202
    198203! Flux correction (decrease)
    199204d_h2oice = d_h2oice*R_dec*hum_dec
    200205
    201 END SUBROUTINE recomp_tend_h2o
     206END SUBROUTINE evolve_tend_h2o
    202207!=======================================================================
    203208
Note: See TracChangeset for help on using the changeset viewer.