Changeset 4192
- Timestamp:
- Apr 20, 2026, 3:58:44 PM (3 weeks ago)
- Location:
- trunk/LMDZ.COMMON
- Files:
-
- 14 edited
-
libf/evolution/backup.F90 (modified) (1 diff)
-
libf/evolution/changelog.txt (modified) (1 diff)
-
libf/evolution/climate_init.F90 (modified) (7 diffs)
-
libf/evolution/climate_rec.F90 (modified) (4 diffs)
-
libf/evolution/deftank/visu_layering.py (modified) (5 diffs)
-
libf/evolution/deftank/visu_layering_evo.py (modified) (8 diffs)
-
libf/evolution/display.F90 (modified) (3 diffs)
-
libf/evolution/layered_deposits.F90 (modified) (44 diffs)
-
libf/evolution/maths.F90 (modified) (3 diffs)
-
libf/evolution/pem.F90 (modified) (2 diffs)
-
libf/evolution/soil.F90 (modified) (2 diffs)
-
libf/evolution/subsurface_ice.F90 (modified) (18 diffs)
-
libf/misc/job.F90 (modified) (3 diffs)
-
makelmdz_fcm (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LMDZ.COMMON/libf/evolution/backup.F90
r4189 r4192 96 96 use tracers, only: build4PCM_tracers, nq 97 97 use ice_table, only: build4PCM_ssice 98 use climate_rec, only: write_restart, write_restartfi, write_restartevo98 use climate_rec, only: write_restart, write_restartfi, write_restartevo 99 99 use layered_deposits, only: layering 100 100 -
trunk/LMDZ.COMMON/libf/evolution/changelog.txt
r4189 r4192 977 977 - Rename "clim_state_init"/"clim_state_rec" into "climate_init"/"climate_rec". 978 978 - Add routines to handle conservative remapping in 1d with slope limiters, positivity and boundedness enforcements. 979 980 == 20/04/2026 == JBC 981 Codebase for pore ice representation and evolution in the layered deposits: 982 - replace single pore ice volume fraction with coefficients of polymial expansion to describe the stratum pore ice profile; 983 - add the formation date in the stratum information; 984 - make default initialization for 'stratum'; 985 - centralize the definition of stratum types; 986 - merge of similar strata; 987 - add interpolation and polynomial fitting tools; 988 - remove subsurface strata. -
trunk/LMDZ.COMMON/libf/evolution/climate_init.F90
r4189 r4192 321 321 use stoppage, only: stop_clean 322 322 use geometry, only: ngrid, nslope, nsoil, nsoil_PCM 323 use evolution, only: dt 323 use evolution, only: dt, pem_ini_date, n_yr_sim 324 324 use physics, only: mugaz, r, alpha_clap_h2o, beta_clap_h2o 325 325 use frost, only: h2ofrost_PCM, h2o_frost4PCM, co2frost_PCM, co2_frost4PCM … … 331 331 use sorption, only: do_sorption, evolve_regolith_adsorption 332 332 use tracers, only: mmol, iPCM_qh2o 333 use layered_deposits, only: layering, do_layering, array2map, ini_layering, add_stratum 333 use layered_deposits, only: layering, do_layering, array2map, ini_layering, add_stratum, ncoef_poreice 334 334 use surf_ice, only: rho_co2ice, rho_h2oice 335 335 use slopes, only: subslope_dist, def_slope_mean … … 361 361 ! LOCAL VARIABLES 362 362 ! --------------- 363 logical(k4) :: here 364 integer(di) :: i, islope, k, nb_str_max, nsoil_startevo 365 real(dp) :: delta ! Depth of the interface regolith/breccia, breccia/bedrock [m] 366 real(dp), dimension(ngrid,nsoil,nslope) :: TI_startevo ! Soil thermal inertia saved in the startevo [SI] 367 real(dp), dimension(ngrid,nsoil,nslope) :: tsoil_startevo ! Soil temperature saved in the startevo [K] 368 real(dp), dimension(:,:,:,:), allocatable :: layerings_array ! Array for layerings 363 logical(k4) :: here 364 integer(di) :: i, islope, k, nb_str_max, nsoil_startevo 365 real(dp) :: delta ! Depth of the interface regolith/breccia, breccia/bedrock [m] 366 real(dp), dimension(ngrid,nsoil,nslope) :: TI_startevo ! Soil thermal inertia saved in the startevo [SI] 367 real(dp), dimension(ngrid,nsoil,nslope) :: tsoil_startevo ! Soil temperature saved in the startevo [K] 368 real(dp), dimension(:,:,:,:), allocatable :: layerings_array ! Array for layerings 369 real(dp), dimension(ncoef_poreice), parameter :: coef_poreice_0 = 0._dp 369 370 370 371 ! CODE … … 489 490 ! Layering 490 491 if (do_layering) then 491 call print_msg('layerings_map is initialized with sub-surface strata.',LVL_WRN)492 call print_msg('layerings_map is initialized with a surface reference stratum.',LVL_WRN) 492 493 call print_msg("Ice is added with 'h2oice_huge_ini' where 'watercaptag' is true and otherwise with 'perennial_co2ice' found in the PCM.",LVL_WRN) 493 494 do i = 1,ngrid … … 495 496 do islope = 1,nslope 496 497 call ini_layering(layerings_map(i,islope)) 497 call add_stratum(layerings_map(i,islope),1.05_dp*h2oice_huge_ini/rho_h2oice,0._dp,h2oice_huge_ini/rho_h2oice,0.05_dp*h2oice_huge_ini/rho_h2oice,0._dp,0._dp) 498 call add_stratum(layerings_map(i,islope),1.05_dp*h2oice_huge_ini/rho_h2oice,0._dp,h2oice_huge_ini/rho_h2oice, & 499 0.05_dp*h2oice_huge_ini/rho_h2oice,0._dp,coef_poreice_0,pem_ini_date + n_yr_sim) 498 500 end do 499 501 else 500 502 do islope = 1,nslope 501 503 call ini_layering(layerings_map(i,islope)) 502 if (co2_perice_PCM(i,islope) > 0.) call add_stratum(layerings_map(i,islope),1.05_dp*co2_perice_PCM(i,islope)/rho_co2ice,co2_perice_PCM(i,islope)/rho_co2ice,0._dp,0.05_dp*co2_perice_PCM(i,islope)/rho_co2ice,0._dp,0._dp) 504 if (co2_perice_PCM(i,islope) > 0.) then 505 call add_stratum(layerings_map(i,islope),1.05_dp*co2_perice_PCM(i,islope)/rho_co2ice,co2_perice_PCM(i,islope)/rho_co2ice,0._dp, & 506 0.05_dp*co2_perice_PCM(i,islope)/rho_co2ice,0._dp,coef_poreice_0,pem_ini_date + n_yr_sim) 507 end if 503 508 end do 504 509 end if … … 567 572 if (do_layering) then 568 573 call get_dim_nc('nb_str_max',nb_str_max) 569 allocate(layerings_array(ngrid,nslope,nb_str_max,6 ))574 allocate(layerings_array(ngrid,nslope,nb_str_max,6 + ncoef_poreice)) 570 575 layerings_array = 0. 571 576 call get_var_nc('stratif_top_elevation',layerings_array(:,:,:,1)) … … 574 579 call get_var_nc('stratif_h_dust',layerings_array(:,:,:,4)) 575 580 call get_var_nc('stratif_h_pore',layerings_array(:,:,:,5)) 576 call get_var_nc('stratif_poreice_volfrac',layerings_array(:,:,:,6)) 581 call get_var_nc('stratif_poreice_coef1',layerings_array(:,:,:,6)) 582 call get_var_nc('stratif_poreice_coef2',layerings_array(:,:,:,7)) 583 call get_var_nc('stratif_poreice_coef3',layerings_array(:,:,:,8)) 584 call get_var_nc('stratif_poreice_coef4',layerings_array(:,:,:,9)) 585 call get_var_nc('stratif_date',layerings_array(:,:,:,10)) 577 586 call array2map(layerings_array,layerings_map) 578 587 deallocate(layerings_array) -
trunk/LMDZ.COMMON/libf/evolution/climate_rec.F90
r4189 r4192 395 395 call check_nc(nf90_put_att(ncid,varid,'title','Layering pore height'),'putting title attribute for stratif_h_pore') 396 396 397 call check_nc(nf90_def_var(ncid,'stratif_poreice_volfrac',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_poreice_volfrac') 398 call check_nc(nf90_put_att(ncid,varid,'title','Layering ice pore volume fraction'),'putting title attribute for stratif_poreice_volfrac') 397 call check_nc(nf90_def_var(ncid,'stratif_poreice_coef1',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_poreice_coef1') 398 call check_nc(nf90_put_att(ncid,varid,'title','Layering pore-ice coefficient #1'),'putting title attribute for stratif_poreice_coef1') 399 400 call check_nc(nf90_def_var(ncid,'stratif_poreice_coef2',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_poreice_coef2') 401 call check_nc(nf90_put_att(ncid,varid,'title','Layering pore-ice coefficient #2'),'putting title attribute for stratif_poreice_coef2') 402 403 call check_nc(nf90_def_var(ncid,'stratif_poreice_coef3',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_poreice_coef3') 404 call check_nc(nf90_put_att(ncid,varid,'title','Layering pore-ice coefficient #3'),'putting title attribute for stratif_poreice_coef3') 405 406 call check_nc(nf90_def_var(ncid,'stratif_poreice_coef4',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_poreice_coef4') 407 call check_nc(nf90_put_att(ncid,varid,'title','Layering pore-ice coefficient #4'),'putting title attribute for stratif_poreice_coef4') 408 409 call check_nc(nf90_def_var(ncid,'stratif_date',nf90_double,(/dim_ngrid,dim_nb_str,dim_nslope,dim_time/),varid),'defining variable stratif_date') 410 call check_nc(nf90_put_att(ncid,varid,'title','Layering stratum formation date'),'putting title attribute for stratif_date') 399 411 end if 400 412 … … 436 448 use geometry, only: ngrid, nslope 437 449 use evolution, only: pem_ini_date, n_yr_sim 438 use layered_deposits, only: layering, do_layering, map2array, get_nb_str_max 450 use layered_deposits, only: layering, do_layering, map2array, get_nb_str_max, ncoef_poreice 439 451 use soil, only: do_soil, inertiedat 440 452 use sorption, only: do_sorption … … 493 505 494 506 if (do_layering) then 495 allocate(layerings_array(ngrid,nslope,nb_str_max,6 ))507 allocate(layerings_array(ngrid,nslope,nb_str_max,6 + ncoef_poreice)) 496 508 call map2array(layerings_map,layerings_array) 497 509 call put_var_nc('stratif_top_elevation',layerings_array(:,:,:,1),itime) … … 500 512 call put_var_nc('stratif_h_dust',layerings_array(:,:,:,4),itime) 501 513 call put_var_nc('stratif_h_pore',layerings_array(:,:,:,5),itime) 502 call put_var_nc('stratif_poreice_volfrac',layerings_array(:,:,:,6),itime) 514 call put_var_nc('stratif_poreice_coef1',layerings_array(:,:,:,6),itime) 515 call put_var_nc('stratif_poreice_coef2',layerings_array(:,:,:,7),itime) 516 call put_var_nc('stratif_poreice_coef3',layerings_array(:,:,:,8),itime) 517 call put_var_nc('stratif_poreice_coef4',layerings_array(:,:,:,9),itime) 518 call put_var_nc('stratif_date',layerings_array(:,:,:,10),itime) 503 519 deallocate(layerings_array) 504 520 end if -
trunk/LMDZ.COMMON/libf/evolution/deftank/visu_layering.py
r4110 r4192 6 6 import os 7 7 import sys 8 from typing import Optional 8 9 import numpy as np 9 10 import netCDF4 as nc … … 44 45 45 46 47 def _get_axis(dim_names: list[str], aliases: tuple[str, ...]) -> Optional[int]: 48 """Return axis index matching one alias in a list of NetCDF dimension names.""" 49 for i, name in enumerate(dim_names): 50 lname = name.lower() 51 for alias in aliases: 52 if lname == alias or alias in lname: 53 return i 54 return None 55 56 57 def _read_stratif_var_tsg(nc_dataset: nc.Dataset, var_name: str, slope_index: int) -> Optional[np.ndarray]: 58 """ 59 Read one stratification variable and reshape it to (time, strata, grid). 60 If a slope dimension exists, keep only slope_index. 61 """ 62 if var_name not in nc_dataset.variables: 63 return None 64 65 var = nc_dataset.variables[var_name] 66 arr = np.asarray(var[:], dtype=np.float64) 67 dims = list(var.dimensions) 68 69 ax_slope = _get_axis(dims, ('nslope', 'slope')) 70 if ax_slope is not None: 71 arr = np.take(arr, slope_index, axis=ax_slope) 72 dims.pop(ax_slope) 73 74 if arr.ndim == 2: 75 return arr[np.newaxis, :, :] 76 if arr.ndim != 3: 77 sys.exit(f"Error: Variable '{var_name}' has unsupported shape {arr.shape}.") 78 79 ax_time = _get_axis(dims, ('time',)) 80 ax_str = _get_axis(dims, ('nb_str_max', 'nb_str')) 81 ax_grid = _get_axis(dims, ('physical_points', 'ngrid', 'grid')) 82 83 if ax_time is None or ax_str is None or ax_grid is None: 84 # Fallback to historical layout already used in this script. 85 return arr 86 87 return np.transpose(arr, (ax_time, ax_str, ax_grid)) 88 89 90 def _poreice_profile_from_coeff(coeff: np.ndarray, z_norm: np.ndarray) -> np.ndarray: 91 """Evaluate clamped cubic pore-ice profile on normalized depth z in [0,1].""" 92 profile = coeff[0] + coeff[1] * z_norm + coeff[2] * z_norm**2 + coeff[3] * z_norm**3 93 return np.clip(profile, 0.0, 1.0) 94 95 96 def _mean_poreice_fraction(coeff: np.ndarray, n_profile: int = 33) -> float: 97 """Compute mean pore-ice volumetric fraction by integrating the cubic profile.""" 98 z_norm = np.linspace(0.0, 1.0, n_profile) 99 profile = _poreice_profile_from_coeff(coeff, z_norm) 100 return float(np.clip(np.trapz(profile, z_norm), 0.0, 1.0)) 101 102 46 103 def load_slope_variables(nc_dataset: nc.Dataset, slope_index: int) -> dict: 47 104 """ … … 51 108 idx_str = str(slope_index + 1).zfill(2) 52 109 vars_base = { 53 'top_elev': f"stratif_slope{idx_str}_top_elevation", 54 'h_co2': f"stratif_slope{idx_str}_h_co2ice", 55 'h_h2o': f"stratif_slope{idx_str}_h_h2oice", 56 'h_dust': f"stratif_slope{idx_str}_h_dust", 57 'h_pore': f"stratif_slope{idx_str}_h_pore", 58 'poreice_volfrac': f"stratif_slope{idx_str}_poreice_volfrac", 110 'top_elev': (f"stratif_slope{idx_str}_top_elevation", "stratif_top_elevation"), 111 'h_co2': (f"stratif_slope{idx_str}_h_co2ice", "stratif_h_co2ice"), 112 'h_h2o': (f"stratif_slope{idx_str}_h_h2oice", "stratif_h_h2oice"), 113 'h_dust': (f"stratif_slope{idx_str}_h_dust", "stratif_h_dust"), 114 'h_pore': (f"stratif_slope{idx_str}_h_pore", "stratif_h_pore"), 59 115 } 60 116 61 117 data = {} 62 for key, var_name in vars_base.items(): 63 if var_name not in nc_dataset.variables: 64 sys.exit(f"Error: Variable '{var_name}' not found in the NetCDF file.") 65 data[key] = nc_dataset.variables[var_name][:] 66 return data 118 for key, var_names in vars_base.items(): 119 arr = None 120 for var_name in var_names: 121 arr = _read_stratif_var_tsg(nc_dataset, var_name, slope_index) 122 if arr is not None: 123 break 124 if arr is None: 125 sys.exit(f"Error: Variables {var_names} not found in the NetCDF file.") 126 data[key] = arr 127 128 coeff_arrays = [] 129 for icoef in range(1, 5): 130 arr = _read_stratif_var_tsg(nc_dataset, f"stratif_slope{idx_str}_poreice_coef{icoef}", slope_index) 131 if arr is None: 132 arr = _read_stratif_var_tsg(nc_dataset, f"stratif_poreice_coef{icoef}", slope_index) 133 if arr is None: 134 coeff_arrays = [] 135 break 136 coeff_arrays.append(arr) 137 138 if coeff_arrays: 139 data['poreice_coeff'] = np.stack(coeff_arrays, axis=-1) 140 return data 141 142 sys.exit("Error: Missing pore-ice coefficient variables (expected poreice_coef1..4).") 67 143 68 144 … … 83 159 h_dust = data['h_dust'] 84 160 h_pore = data['h_pore'] 85 poreice_ volfrac = data['poreice_volfrac']161 poreice_coeff = data['poreice_coeff'] 86 162 87 163 layers = top.shape[1] … … 135 211 h2o = h_h2o[0, layer_idx, grid_index] / thickness 136 212 dust = h_dust[0, layer_idx, grid_index] / thickness 137 air = h_pore[0, layer_idx, grid_index] * (1.0 - poreice_volfrac[0, layer_idx, grid_index]) / thickness 138 poreice = h_pore[0, layer_idx, grid_index] * poreice_volfrac[0, layer_idx, grid_index] / thickness 213 poreice_frac = _mean_poreice_fraction(poreice_coeff[0, layer_idx, grid_index, :]) 214 air = h_pore[0, layer_idx, grid_index] * (1.0 - poreice_frac) / thickness 215 poreice = h_pore[0, layer_idx, grid_index] * poreice_frac / thickness 139 216 contents[:, idx + 1] = [co2, h2o, dust, air, poreice] 140 217 -
trunk/LMDZ.COMMON/libf/evolution/deftank/visu_layering_evo.py
r4110 r4192 97 97 98 98 99 def _get_axis(dim_names, aliases): 100 """Return axis index matching aliases in a dimension-name list.""" 101 for i, name in enumerate(dim_names): 102 lname = name.lower() 103 for alias in aliases: 104 if lname == alias or alias in lname: 105 return i 106 return None 107 108 109 def _var_to_tsgi(nc_var): 110 """ 111 Convert a NetCDF stratification variable to shape (time, slope, strata, grid). 112 Handles both slope-specific variables and generic variables with an nslope dimension. 113 """ 114 arr = np.asarray(nc_var[:], dtype=np.float32) 115 dims = [d.lower() for d in nc_var.dimensions] 116 117 ax_time = _get_axis(dims, ('time',)) 118 ax_slope = _get_axis(dims, ('nslope', 'slope')) 119 ax_str = _get_axis(dims, ('nb_str_max', 'nb_str')) 120 ax_grid = _get_axis(dims, ('physical_points', 'ngrid', 'grid')) 121 122 if arr.ndim == 4 and None not in (ax_time, ax_slope, ax_str, ax_grid): 123 return np.transpose(arr, (ax_time, ax_slope, ax_str, ax_grid)) 124 125 if arr.ndim == 3 and None not in (ax_time, ax_str, ax_grid): 126 arr_tsg = np.transpose(arr, (ax_time, ax_str, ax_grid)) 127 return arr_tsg[:, np.newaxis, :, :] 128 129 if arr.ndim == 3 and None not in (ax_slope, ax_str, ax_grid): 130 arr_ssg = np.transpose(arr, (ax_slope, ax_str, ax_grid)) 131 return arr_ssg[np.newaxis, :, :, :] 132 133 if arr.ndim == 3: 134 # Historical fallback used by this script: (time, strata, grid) 135 return arr[:, np.newaxis, :, :] 136 137 if arr.ndim == 2: 138 return arr[np.newaxis, np.newaxis, :, :] 139 140 raise ValueError(f"Unsupported variable shape {arr.shape} for '{nc_var.name}'") 141 142 143 def _slope_index_from_var(vname): 144 """Return 0-based slope index from a slope-specific variable name, else None.""" 145 if 'slope' not in vname: 146 return None 147 try: 148 return int(vname.split('slope')[1].split('_')[0]) - 1 149 except (ValueError, IndexError): 150 return None 151 152 99 153 def collect_stratification_variables(files, base_name): 100 154 """ … … 114 168 115 169 property_markers = { 116 'heights': ' stratif_slope', # "..._top_elevation"170 'heights': 'top_elevation', 117 171 'co2_ice': 'h_co2ice', 118 172 'h2o_ice': 'h_h2oice', 119 173 'dust': 'h_dust', 120 174 'pore': 'h_pore', 121 'pore_ice': 'poreice_volfrac' 175 'poreice_coef1': 'poreice_coef1', 176 'poreice_coef2': 'poreice_coef2', 177 'poreice_coef3': 'poreice_coef3', 178 'poreice_coef4': 'poreice_coef4' 122 179 } 123 180 var_info = {prop: set() for prop in property_markers} … … 128 185 max_nb_str = max(max_nb_str, ds.dimensions['nb_str_max'].size) 129 186 130 nslope = ds.dimensions['nslope'].size131 for k in range(1, nslope + 1):132 var_name = f"stratif_slope{k:02d}_top_elevation"133 if var_name in ds.variables:134 arr = ds.variables[var_name][:]135 min_base_elev = min(min_base_elev, np.min(arr))136 max_top_elev = max(max_top_elev, np.max(arr))137 var_info['heights'].add(var_name)138 139 187 for full_var in ds.variables: 188 if 'stratif' not in full_var: 189 continue 190 191 if property_markers['heights'] in full_var: 192 arr = np.asarray(ds.variables[full_var][:]) 193 if arr.size > 0: 194 min_base_elev = min(min_base_elev, float(np.nanmin(arr))) 195 max_top_elev = max(max_top_elev, float(np.nanmax(arr))) 196 var_info['heights'].add(full_var) 197 140 198 for prop, marker in property_markers.items(): 141 if (marker in full_var) and prop != 'heights':199 if prop != 'heights' and marker in full_var: 142 200 var_info[prop].add(full_var) 143 201 … … 174 232 for t_idx, ds in enumerate(datasets): 175 233 for var_name in var_info['heights']: 176 slope_idx = int(var_name.split("slope")[1].split("_")[0]) - 1 177 if 0 <= slope_idx < nslope: 178 raw = ds.variables[var_name][0, :, :] # (n_strata, ngrid) 179 heights_data[t_idx][slope_idx] = raw.T # (ngrid, n_strata) 234 if var_name not in ds.variables: 235 continue 236 raw_tsgi = _var_to_tsgi(ds.variables[var_name]) 237 raw_sgi = raw_tsgi[0] # (nslope_var, nstrata, ngrid) 238 239 slope_idx = _slope_index_from_var(var_name) 240 if slope_idx is not None and raw_sgi.shape[0] == 1: 241 if 0 <= slope_idx < nslope: 242 heights_data[t_idx][slope_idx] = raw_sgi[0].T 243 continue 244 245 nsl = min(nslope, raw_sgi.shape[0]) 246 for isl in range(nsl): 247 heights_data[t_idx][isl] = raw_sgi[isl].T 180 248 181 249 raw_prop_arrays = {} … … 185 253 raw_prop_arrays[prop] = np.zeros((ngrid, ntime, nslope, max_nb_str), dtype=np.float32) 186 254 187 def slope_index_from_var(vname):188 return int(vname.split("slope")[1].split("_")[0]) - 1189 190 255 for prop in raw_prop_arrays: 191 slope_map = {}192 for vname in var_info[prop]:193 isl = slope_index_from_var(vname)194 if 0 <= isl < nslope:195 slope_map[isl] = vname196 197 256 arr = raw_prop_arrays[prop] 198 257 for t_idx, ds in enumerate(datasets): 199 for isl, var_name in slope_map.items(): 200 raw = ds.variables[var_name][0, :, :] # (n_strata, ngrid) 201 n_strata_current = raw.shape[0] 202 arr[:, t_idx, isl, :n_strata_current] = raw.T 258 for var_name in var_info[prop]: 259 if var_name not in ds.variables: 260 continue 261 262 raw_tsgi = _var_to_tsgi(ds.variables[var_name]) 263 raw_sgi = raw_tsgi[0] # (nslope_var, nstrata, ngrid) 264 slope_idx = _slope_index_from_var(var_name) 265 266 if slope_idx is not None and raw_sgi.shape[0] == 1: 267 if 0 <= slope_idx < nslope: 268 n_strata_current = min(raw_sgi.shape[1], max_nb_str) 269 arr[:, t_idx, slope_idx, :n_strata_current] = raw_sgi[0, :n_strata_current, :].T 270 continue 271 272 nsl = min(nslope, raw_sgi.shape[0]) 273 for isl in range(nsl): 274 n_strata_current = min(raw_sgi.shape[1], max_nb_str) 275 arr[:, t_idx, isl, :n_strata_current] = raw_sgi[isl, :n_strata_current, :].T 203 276 204 277 return heights_data, raw_prop_arrays, ntime … … 209 282 Given raw_prop_arrays for 'co2_ice', 'h2o_ice', 'dust', 'pore' (in meters), 210 283 normalize each set of strata so that the sum of those four = 1 per cell. 211 Returns: 212 - frac_arrays: dict mapping same keys -> 4D arrays of fractions (0..1). 284 Returns: 285 - frac_arrays: dict mapping same keys -> 4D arrays of fractions (0..1). 286 - includes an additional 'pore_ice' fraction derived from pore-ice coefficients. 213 287 """ 214 288 co2 = raw_prop_arrays['co2_ice'] … … 224 298 frac_dust = np.zeros_like(dust, dtype=np.float32) 225 299 frac_pore = np.zeros_like(pore, dtype=np.float32) 300 frac_pore_ice = np.zeros_like(pore, dtype=np.float32) 226 301 227 302 frac_co2[mask] = co2[mask] / total[mask] … … 230 305 frac_pore[mask] = pore[mask] / total[mask] 231 306 307 if all(key in raw_prop_arrays for key in ('poreice_coef1', 'poreice_coef2', 'poreice_coef3', 'poreice_coef4')): 308 z_norm = np.linspace(0.0, 1.0, 33, dtype=np.float32) 309 c1 = raw_prop_arrays['poreice_coef1'] 310 c2 = raw_prop_arrays['poreice_coef2'] 311 c3 = raw_prop_arrays['poreice_coef3'] 312 c4 = raw_prop_arrays['poreice_coef4'] 313 profile = (c1[..., None] + c2[..., None] * z_norm + c3[..., None] * z_norm**2 + c4[..., None] * z_norm**3) 314 profile = np.clip(profile, 0.0, 1.0) 315 poreice_mean = np.clip(np.trapz(profile, z_norm, axis=-1), 0.0, 1.0).astype(np.float32) 316 else: 317 poreice_mean = np.zeros_like(pore, dtype=np.float32) 318 319 frac_pore_ice[mask] = pore[mask] * poreice_mean[mask] / total[mask] 320 232 321 return { 233 322 'co2_ice': frac_co2, 234 323 'h2o_ice': frac_h2o, 235 324 'dust': frac_dust, 236 'pore': frac_pore 325 'pore': frac_pore, 326 'pore_ice': frac_pore_ice 237 327 } 238 328 -
trunk/LMDZ.COMMON/libf/evolution/display.F90
r4180 r4192 35 35 character(32), protected, private :: username = ' ' ! User name 36 36 character(32), protected, private :: hostname = ' ' ! Machine/station name 37 character(128), protected, private :: cmd_pgrm = ' ' ! Command used to run the program 38 character(8), protected, private :: date = ' ' ! Current date (YYYYMMDD) 39 character(10), protected, private :: time = ' ' ! Current time (hhmmss.sss) 40 character(5), protected, private :: zone = ' ' ! UTC offset (+/-hhmm) 37 41 integer(di), protected, private :: verbosity_lvl = LVL_NFO 38 42 logical(k4), protected, private :: out2term = .true. ! Flag to output to terminal … … 125 129 ! LOCAL VARIABLES 126 130 ! --------------- 127 character(8) :: date128 character(10) :: time129 character(5) :: zone130 131 character(100) :: msg 131 integer(di), dimension(8) :: values 132 integer(di), dimension(8) :: values ! Date/time components 132 133 133 134 ! CODE … … 147 148 call getlog(username) 148 149 call hostnm(hostname) 150 call get_command(cmd_pgrm) 149 151 call print_msg('',LVL_NFO) 150 152 call print_msg('********* PEM information *********',LVL_NFO) 151 call print_msg('+ User : '//trim(username),LVL_NFO) 152 call print_msg('+ Machine : '//trim(hostname),LVL_NFO) 153 call print_msg('+ Directory: '//trim(curr_dir),LVL_NFO) 154 write(msg,'(a,i2,a,i2,a,i4)') '+ Date : ',values(3),'/',values(2),'/',values(1) 155 call print_msg(msg,LVL_NFO) 156 write(msg,'(a,i2,a,i2,a,i2,a)') '+ Time : ',values(5),':',values(6),':',values(7) 157 call print_msg(msg,LVL_NFO) 153 call print_msg('+ User : '//trim(adjustl(username)),LVL_NFO) 154 call print_msg('+ Machine : '//trim(adjustl(hostname)),LVL_NFO) 155 call print_msg('+ Directory: '//trim(adjustl(curr_dir)),LVL_NFO) 156 call print_msg('+ Command : '//trim(adjustl(cmd_pgrm)),LVL_NFO) 157 write(msg,'(a,a4,a,a2,a,a2)') '+ Date : ',date(1:4),'-',date(5:6),'-',date(7:8) 158 call print_msg(trim(msg),LVL_NFO) 159 write(msg,'(a,a2,a,a2,a,a2,a,a3)') '+ Time : ',time(1:2),':',time(3:4),':',time(5:6),'.',time(8:10) 160 call print_msg(trim(msg),LVL_NFO) 161 if (zone(1:1) == '+' .or. zone(1:1) == '-') then 162 write(msg,'(a,a1,a2,a,a2)') '+ Timezone : UTC',zone(1:1),zone(2:3),':',zone(4:5) 163 else 164 write(msg,'(a,i0,a)') '+ Timezone : unknown (offset=',values(4),' min)' 165 end if 166 call print_msg(trim(msg),LVL_NFO) 158 167 call print_msg('',LVL_NFO) 159 168 call print_msg('********* Initialization *********',LVL_NFO) -
trunk/LMDZ.COMMON/libf/evolution/layered_deposits.F90
r4180 r4192 5 5 ! 6 6 ! DESCRIPTION 7 ! Manage layered deposits in the PEM witha linked list data structure.7 ! Manages layered deposits thanks to a linked list data structure. 8 8 ! Provides data types and subroutines to manipulate layers of ice and dust. 9 9 ! 10 10 ! AUTHORS & DATE 11 ! JB Clement, 202411 ! JB Clement, 03/2024 12 12 ! 13 13 ! NOTES … … 18 18 ! ------------ 19 19 use numerics, only: dp, di, k4, tol, eps 20 use geometry, only: ngrid, nslope21 20 use surf_ice, only: rho_co2ice, rho_h2oice 22 21 … … 31 30 32 31 ! Physical parameters 33 logical(k4), protected, private :: impose_dust_ratio ! Impose dust-to-ice ratio instead of dust tendency (see 'dust2ice_ratio') 34 real(dp), protected, private :: dust2ice_ratio ! Dust-to-ice ratio when ice condenses (10% by default) 35 real(dp), protected, private :: d_dust ! Tendency of dust [kg/m2/y] 36 real(dp), parameter, private :: dry_lag_porosity = 0.2_dp ! Porosity of dust lag 37 real(dp), parameter, private :: wet_lag_porosity = 0.15_dp ! Porosity of water ice lag 38 real(dp), parameter, private :: co2ice_porosity = 0.05_dp ! Porosity of CO2 ice 39 real(dp), parameter, private :: h2oice_porosity = 0.1_dp ! Porosity of H2O ice 40 real(dp), parameter, private :: rho_dust = 2500._dp ! Density of dust [kg.m-3] 41 real(dp), parameter, private :: h_patchy_dust = 5.e-4_dp ! Height under which a dust lag is considered patchy [m] 42 real(dp), parameter, private :: h_patchy_ice = 5.e-4_dp ! Height under which a H2O ice lag is considered patchy [m] 32 logical(k4), protected, private :: impose_dust_ratio ! Impose dust-to-ice ratio instead of dust tendency (see 'dust2ice_ratio') 33 real(dp), protected, private :: dust2ice_ratio ! Dust-to-ice ratio when ice condenses (10% by default) 34 real(dp), protected, private :: d_dust ! Tendency of dust [kg/m2/y] 35 real(dp), parameter, private :: dry_lag_porosity = 0.2_dp ! Porosity of dust lag 36 real(dp), parameter, private :: wet_lag_porosity = 0.15_dp ! Porosity of water ice lag 37 real(dp), parameter, private :: co2ice_porosity = 0.05_dp ! Porosity of CO2 ice 38 real(dp), parameter, private :: h2oice_porosity = 0.1_dp ! Porosity of H2O ice 39 real(dp), parameter, private :: rho_dust = 2500._dp ! Density of dust [kg.m-3] 40 real(dp), parameter, private :: h_patchy_dust = 5.e-4_dp ! Height under which a dust lag is considered patchy [m] 41 real(dp), parameter, private :: h_patchy_ice = 5.e-4_dp ! Height under which a H2O ice lag is considered patchy [m] 42 43 ! Stratum type codes 44 integer(di), parameter, private :: STR_TYPE_EMPTY = 0_di ! Empty stratum 45 integer(di), parameter, private :: STR_TYPE_CO2ICE = 1_di ! CO2 ice stratum 46 integer(di), parameter, private :: STR_TYPE_H2OICE = 2_di ! H2O ice stratum 47 integer(di), parameter, private :: STR_TYPE_DUST = 3_di ! Dust stratum 48 integer(di), parameter, private :: STR_TYPE_MIXED = 4_di ! Mixed stratum (ice and dust) 49 50 ! Parameters for pore ice profile reconstruction 51 integer(di), parameter :: ncoef_poreice = 4_di ! Number of cubic coefficients for pore ice profile 52 real(dp), parameter, private :: dz_poreice = 0.01_dp ! Target depth step for pore-ice profile reconstruction [m] 53 integer(di), parameter, private :: nmin_poreice = 4_di ! Minimal number of points for pore-ice profile reconstruction 54 integer(di), parameter, private :: nmax_poreice = 64_di ! Maximal number of points for pore-ice profile reconstruction 43 55 44 56 ! Parameters for CO2 flux correction (inspired by Levrard et al. 2007) … … 50 62 ! Stratum = node of the linked list 51 63 type :: stratum 52 real(dp) :: top_elevation ! Layer top_elevation (top height from the surface) [m] 53 real(dp) :: h_co2ice ! Height of CO2 ice [m] 54 real(dp) :: h_h2oice ! Height of H2O ice [m] 55 real(dp) :: h_dust ! Height of dust [m] 56 real(dp) :: h_pore ! Height of pores [m] 57 real(dp) :: poreice_volfrac ! Pore-filled ice volumetric fraction 58 type(stratum), pointer :: up => null() ! Upper stratum (next node) 59 type(stratum), pointer :: down => null() ! Lower stratum (previous node) 64 real(dp) :: top_elevation = 1._dp ! Layer top_elevation (top height from the surface) [m] 65 real(dp) :: h_co2ice = 0._dp ! Height of CO2 ice [m] 66 real(dp) :: h_h2oice = 0._dp ! Height of H2O ice [m] 67 real(dp) :: h_dust = 1._dp ! Height of dust [m] 68 real(dp) :: h_pore = 0._dp ! Height of pores [m] 69 real(dp), dimension(ncoef_poreice) :: coef_poreice = 0._dp ! Coefficients for pore ice profile 70 real(dp) :: form_date = 0._dp ! Stratum formation date [planetary years] 71 type(stratum), pointer :: up => null() ! Upper stratum (next node) 72 type(stratum), pointer :: down => null() ! Lower stratum (previous node) 60 73 end type stratum 61 74 62 75 ! Layering = linked list 63 type layering76 type :: layering 64 77 integer(di) :: nb_str = 0 ! Number of strata 65 type(stratum), pointer :: bottom => null() ! First stratum (head of the list) 66 type(stratum), pointer :: top => null() ! Last stratum (tail of the list) 67 !real(dp) :: h_toplag ! Height of dust layer at the top [m] 68 !type(stratum), pointer :: subice => null() ! Shallower stratum containing ice 69 !type(stratum), pointer :: surf => null() ! Surface stratum 78 type(stratum), pointer :: bottom => null() ! First stratum (head of the list) at the very bottom 79 type(stratum), pointer :: top => null() ! Last stratum (tail of the list) at the very top 80 !type(stratum), pointer :: subice => null() ! Shallowest stratum containing compact ice 70 81 end type layering 71 82 72 83 ! Array of pointers to "stratum" 73 type ptrarray84 type :: ptrarray 74 85 type(stratum), pointer :: p 75 86 end type ptrarray … … 125 136 126 137 !======================================================================= 138 FUNCTION get_str_type(str) RESULT(type) 139 !----------------------------------------------------------------------- 140 ! NAME 141 ! get_str_type 142 ! 143 ! DESCRIPTION 144 ! Determine stratum type from composition and pore-ice state. 145 ! 146 ! AUTHORS & DATE 147 ! JB Clement, 04/2026 148 ! 149 ! NOTES 150 ! 151 !----------------------------------------------------------------------- 152 153 ! DECLARATION 154 ! ----------- 155 implicit none 156 157 ! ARGUMENTS 158 ! --------- 159 type(stratum), intent(in) :: str 160 161 ! LOCAL VARIABLES 162 ! --------------- 163 integer(di) :: type 164 165 ! CODE 166 ! ---- 167 if (str%h_co2ice <= tol .and. str%h_h2oice <= tol .and. str%h_dust <= tol .and. is_poreice_empty(str)) then 168 type = STR_TYPE_EMPTY 169 else if (str%h_co2ice > str%h_h2oice .and. str%h_co2ice > str%h_dust) then 170 type = STR_TYPE_CO2ICE 171 else if (str%h_h2oice > str%h_co2ice .and. str%h_h2oice > str%h_dust) then 172 type = STR_TYPE_H2OICE 173 else if (str%h_dust > str%h_co2ice .and. str%h_dust > str%h_h2oice) then 174 type = STR_TYPE_DUST 175 else 176 type = STR_TYPE_MIXED 177 end if 178 179 END FUNCTION get_str_type 180 !======================================================================= 181 182 !======================================================================= 183 FUNCTION strtype2txt(type) RESULT(txt_type) 184 !----------------------------------------------------------------------- 185 ! NAME 186 ! strtype2txt 187 ! 188 ! DESCRIPTION 189 ! Convert stratum type integer code into a readable string. 190 ! 191 ! AUTHORS & DATE 192 ! JB Clement, 04/2026 193 ! 194 ! NOTES 195 ! 196 !----------------------------------------------------------------------- 197 198 ! DECLARATION 199 ! ----------- 200 implicit none 201 202 ! ARGUMENTS 203 ! --------- 204 integer(di), intent(in) :: type 205 206 ! LOCAL VARIABLES 207 ! --------------- 208 character(16) :: txt_type 209 210 ! CODE 211 ! ---- 212 select case (type) 213 case (STR_TYPE_EMPTY) 214 txt_type = 'EMPTY' 215 case (STR_TYPE_CO2ICE) 216 txt_type = 'CO2_ICE' 217 case (STR_TYPE_H2OICE) 218 txt_type = 'H2O_ICE' 219 case (STR_TYPE_DUST) 220 txt_type = 'DUST' 221 case (STR_TYPE_MIXED) 222 txt_type = 'MIXED' 223 case default 224 txt_type = 'UNKNOWN' 225 end select 226 227 END FUNCTION strtype2txt 228 !======================================================================= 229 230 !======================================================================= 127 231 SUBROUTINE print_stratum(this) 128 232 !----------------------------------------------------------------------- … … 153 257 type(stratum), intent(in) :: this 154 258 155 ! CODE 156 ! ---- 157 call print_msg('Top elevation [m]: '//real2str(this%top_elevation),LVL_DBG) 158 call print_msg('Height of CO2 ice [m]: '//real2str(this%h_co2ice),LVL_DBG) 159 call print_msg('Height of H2O ice [m]: '//real2str(this%h_h2oice),LVL_DBG) 160 call print_msg('Height of dust [m]: '//real2str(this%h_dust),LVL_DBG) 161 call print_msg('Height of pores [m]: '//real2str(this%h_pore),LVL_DBG) 162 call print_msg('Pore-filled ice [m3/m3]: '//real2str(this%poreice_volfrac),LVL_DBG) 259 ! LOCAL VARIABLES 260 ! --------------- 261 integer(di) :: i 262 character(128) :: msg 263 264 ! CODE 265 ! ---- 266 call print_msg('Nature [-]: '//trim(strtype2txt(get_str_type(this))),LVL_DBG) 267 call print_msg('Formation date [plnt year]: '//real2str(this%form_date),LVL_DBG) 268 call print_msg('Top elevation [m]: '//real2str(this%top_elevation),LVL_DBG) 269 call print_msg('Height of CO2 ice [m]: '//real2str(this%h_co2ice),LVL_DBG) 270 call print_msg('Height of H2O ice [m]: '//real2str(this%h_h2oice),LVL_DBG) 271 call print_msg('Height of dust [m]: '//real2str(this%h_dust),LVL_DBG) 272 call print_msg('Height of pores [m]: '//real2str(this%h_pore),LVL_DBG) 273 msg = 'Pore-ice profile coefs [m3/m3]: {' 274 do i = 1, size(this%coef_poreice) 275 msg = trim(msg)//real2str(this%coef_poreice(i))//', ' 276 end do 277 msg = trim(msg)//'}' 278 call print_msg(trim(msg),LVL_DBG) 163 279 164 280 END SUBROUTINE print_stratum … … 166 282 167 283 !======================================================================= 168 SUBROUTINE add_stratum(this,top_elevation,co2ice,h2oice,dust,pore, poreice)284 SUBROUTINE add_stratum(this,top_elevation,co2ice,h2oice,dust,pore,coef_poreice,form_date) 169 285 !----------------------------------------------------------------------- 170 286 ! NAME … … 187 303 ! ARGUMENTS 188 304 ! --------- 189 type(layering), intent(inout) :: this 190 real(dp), intent(in), optional :: top_elevation, co2ice, h2oice, dust, pore, poreice 305 type(layering), intent(inout) :: this 306 real(dp), intent(in), optional :: top_elevation, co2ice, h2oice, dust, pore 307 real(dp), dimension(:), intent(in), optional :: coef_poreice 308 real(dp), intent(in), optional :: form_date 191 309 192 310 ! LOCAL VARIABLES … … 198 316 ! Creation of the stratum 199 317 allocate(str) 200 nullify(str%up,str%down)201 str%top_elevation = 1._dp202 str%h_co2ice = 0._dp203 str%h_h2oice = 0._dp204 str%h_dust = 1._dp205 str%h_pore = 0._dp206 str%poreice_volfrac = 0._dp207 318 if (present(top_elevation)) str%top_elevation = top_elevation 208 319 if (present(co2ice)) str%h_co2ice = co2ice … … 210 321 if (present(dust)) str%h_dust = dust 211 322 if (present(pore)) str%h_pore = pore 212 if (present(poreice)) str%poreice_volfrac = poreice 323 if (present(coef_poreice)) str%coef_poreice(:) = coef_poreice(:) 324 if (present(form_date)) str%form_date = form_date 213 325 214 326 ! Increment the number of strata … … 229 341 230 342 !======================================================================= 231 SUBROUTINE insert_stratum(this,str_prev,top_elevation,co2ice,h2oice,dust,pore, poreice)343 SUBROUTINE insert_stratum(this,str_prev,top_elevation,co2ice,h2oice,dust,pore,coef_poreice,form_date) 232 344 !----------------------------------------------------------------------- 233 345 ! NAME … … 252 364 type(layering), intent(inout) :: this 253 365 type(stratum), pointer, intent(inout) :: str_prev 254 real(dp), intent(in), optional :: top_elevation, co2ice, h2oice, dust, pore, poreice 366 real(dp), intent(in), optional :: top_elevation, co2ice, h2oice, dust, pore 367 real(dp), dimension(:), intent(in), optional :: coef_poreice 368 real(dp), intent(in), optional :: form_date 255 369 256 370 ! LOCAL VARIABLES … … 261 375 ! ---- 262 376 if (.not. associated(str_prev%up)) then ! If str_prev is the top stratum of the layering 263 call add_stratum(this,top_elevation,co2ice,h2oice,dust,pore, poreice)377 call add_stratum(this,top_elevation,co2ice,h2oice,dust,pore,coef_poreice,form_date) 264 378 else 265 379 ! Creation of the stratum 266 380 allocate(str) 267 nullify(str%up,str%down)268 str%top_elevation = 1._dp269 str%h_co2ice = 0._dp270 str%h_h2oice = 0._dp271 str%h_dust = 1._dp272 str%h_pore = 0._dp273 str%poreice_volfrac = 0._dp274 381 if (present(top_elevation)) str%top_elevation = top_elevation 275 382 if (present(co2ice)) str%h_co2ice = co2ice … … 277 384 if (present(dust)) str%h_dust = dust 278 385 if (present(pore)) str%h_pore = pore 279 if (present(poreice)) str%poreice_volfrac = poreice 386 if (present(coef_poreice)) str%coef_poreice(:) = coef_poreice(:) 387 if (present(form_date)) str%form_date = form_date 280 388 281 389 ! Increment the number of strata … … 359 467 360 468 !======================================================================= 469 FUNCTION are_strata_similar(str_lo,str_hi,frac_tol,coef_tol,date_tol) RESULT(similar) 470 !----------------------------------------------------------------------- 471 ! NAME 472 ! are_strata_similar 473 ! 474 ! DESCRIPTION 475 ! Check if two adjacent strata can be merged based on composition, 476 ! pore-ice profile, and optional formation-date proximity. 477 ! 478 ! AUTHORS & DATE 479 ! JB Clement, 04/2026 480 ! 481 ! NOTES 482 ! Negative value for date_tol means that formation date is not checked 483 ! for similarity. 484 !----------------------------------------------------------------------- 485 486 ! DECLARATION 487 ! ----------- 488 implicit none 489 490 ! ARGUMENTS 491 ! --------- 492 type(stratum), intent(in) :: str_lo, str_hi 493 real(dp), intent(in) :: frac_tol, coef_tol, date_tol 494 495 ! LOCAL VARIABLES 496 ! --------------- 497 logical(k4) :: similar 498 real(dp) :: h_lo, h_hi 499 real(dp) :: f_lo_co2, f_lo_h2o, f_lo_dust, f_lo_pore 500 real(dp) :: f_hi_co2, f_hi_h2o, f_hi_dust, f_hi_pore 501 502 ! CODE 503 ! ---- 504 similar = .false. 505 506 ! Keep the neutral base layer untouched. 507 if (str_lo%top_elevation <= tol) return 508 509 ! Strata must be of the same type to be merged 510 if (get_str_type(str_lo) /= get_str_type(str_hi)) return 511 512 ! Check that the composition fractions are similar within the tolerance 513 h_lo = thickness(str_lo) 514 f_lo_co2 = str_lo%h_co2ice/h_lo 515 f_lo_h2o = str_lo%h_h2oice/h_lo 516 f_lo_dust = str_lo%h_dust/h_lo 517 f_lo_pore = str_lo%h_pore/h_lo 518 h_hi = thickness(str_hi) 519 f_hi_co2 = str_hi%h_co2ice/h_hi 520 f_hi_h2o = str_hi%h_h2oice/h_hi 521 f_hi_dust = str_hi%h_dust/h_hi 522 f_hi_pore = str_hi%h_pore/h_hi 523 if (abs(f_lo_co2 - f_hi_co2) > frac_tol) return 524 if (abs(f_lo_h2o - f_hi_h2o) > frac_tol) return 525 if (abs(f_lo_dust - f_hi_dust) > frac_tol) return 526 if (abs(f_lo_pore - f_hi_pore) > frac_tol) return 527 if (maxval(abs(str_lo%coef_poreice - str_hi%coef_poreice)) > coef_tol) return 528 529 ! Optionally check that the formation dates are close enough 530 if (date_tol >= 0._dp) then 531 if (abs(str_lo%form_date - str_hi%form_date) > date_tol) return 532 end if 533 534 similar = .true. 535 536 END FUNCTION are_strata_similar 537 !======================================================================= 538 539 !======================================================================= 540 SUBROUTINE merge_adjacent_strata(this,frac_tol,coef_tol,date_tol,max_pass) 541 !----------------------------------------------------------------------- 542 ! NAME 543 ! merge_adjacent_strata 544 ! 545 ! DESCRIPTION 546 ! Merge adjacent strata with similar properties to limit unnecessary 547 ! stratification growth. 548 ! 549 ! AUTHORS & DATE 550 ! JB Clement, 04/2026 551 ! 552 ! NOTES 553 ! Negative value for date_tol means that formation date is not checked 554 ! for similarity. 555 !----------------------------------------------------------------------- 556 557 ! DECLARATION 558 ! ----------- 559 implicit none 560 561 ! ARGUMENTS 562 ! --------- 563 type(layering), intent(inout) :: this 564 real(dp), intent(in), optional :: frac_tol, coef_tol, date_tol 565 integer(di), intent(in), optional :: max_pass 566 567 ! LOCAL VARIABLES 568 ! --------------- 569 type(stratum), pointer :: str_lo, str_hi 570 real(dp) :: h_lo, h_hi, h_tot, w_lo, w_hi 571 real(dp) :: frac_tol_loc, coef_tol_loc, date_tol_loc 572 logical(k4) :: merged_in_pass 573 integer(di) :: ipass, max_pass_loc 574 575 ! CODE 576 ! ---- 577 if (this%nb_str <= 2) return 578 579 frac_tol_loc = 1.e-2_dp 580 coef_tol_loc = 1.e-2_dp 581 date_tol_loc = -1._dp 582 if (present(frac_tol)) frac_tol_loc = max(0._dp,frac_tol) 583 if (present(coef_tol)) coef_tol_loc = max(0._dp,coef_tol) 584 if (present(date_tol)) date_tol_loc = date_tol 585 586 max_pass_loc = this%nb_str 587 if (present(max_pass)) max_pass_loc = max(1_di,max_pass) 588 589 do ipass = 1,max_pass_loc 590 merged_in_pass = .false. 591 str_lo => this%bottom 592 593 do while (associated(str_lo) .and. associated(str_lo%up)) 594 str_hi => str_lo%up 595 596 if (are_strata_similar(str_lo,str_hi,frac_tol_loc,coef_tol_loc,date_tol_loc)) then 597 h_lo = thickness(str_lo) 598 h_hi = thickness(str_hi) 599 h_tot = h_lo + h_hi 600 w_lo = h_lo/h_tot 601 w_hi = h_hi/h_tot 602 603 str_lo%top_elevation = str_hi%top_elevation 604 str_lo%h_co2ice = str_lo%h_co2ice + str_hi%h_co2ice 605 str_lo%h_h2oice = str_lo%h_h2oice + str_hi%h_h2oice 606 str_lo%h_dust = str_lo%h_dust + str_hi%h_dust 607 str_lo%h_pore = str_lo%h_pore + str_hi%h_pore 608 str_lo%coef_poreice(:) = w_lo*str_lo%coef_poreice(:) + w_hi*str_hi%coef_poreice(:) 609 str_lo%form_date = w_lo*str_lo%form_date + w_hi*str_hi%form_date 610 611 call del_stratum(this,str_hi) 612 merged_in_pass = .true. 613 cycle 614 end if 615 616 str_lo => str_lo%up 617 end do 618 619 if (.not. merged_in_pass) exit 620 end do 621 622 END SUBROUTINE merge_adjacent_strata 623 !======================================================================= 624 625 !======================================================================= 361 626 FUNCTION get_stratum(lay,i) RESULT(str) 362 627 !----------------------------------------------------------------------- … … 409 674 !----------------------------------------------------------------------- 410 675 ! NAME 411 ! del_layering676 ! ini_layering 412 677 ! 413 678 ! DESCRIPTION … … 421 686 !----------------------------------------------------------------------- 422 687 423 ! DEPENDENCIES424 ! ------------425 use soil, only: do_soil, layer, index_breccia, index_bedrock, regolith_porosity, breccia_porosity426 use geometry, only: nsoil688 !~ ! DEPENDENCIES 689 !~ ! ------------ 690 !~ use soil, only: do_soil, layer, index_breccia, index_bedrock, regolith_porosity, breccia_porosity 691 !~ use geometry, only: nsoil 427 692 428 693 ! DECLARATION … … 436 701 ! LOCAL VARIABLES 437 702 ! --------------- 438 integer(di) :: i 439 real(dp) :: h_soil, h_pore, h_tot 440 441 ! CODE 442 ! ---- 443 ! Creation of strata at the bottom of the layering to describe the sub-surface 444 if (do_soil) then 445 do i = nsoil,index_bedrock,-1 446 h_soil = layer(i) - layer(i - 1) ! No porosity 447 call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,0._dp,0._dp) 448 end do 449 do i = index_bedrock - 1,index_breccia,-1 450 h_tot = layer(i) - layer(i - 1) 451 h_pore = h_tot*breccia_porosity 452 h_soil = h_tot - h_pore 453 call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,h_pore,0._dp) 454 end do 455 do i = index_breccia - 1,2,-1 456 h_tot = layer(i) - layer(i - 1) 457 h_pore = h_tot*regolith_porosity 458 h_soil = h_tot - h_pore 459 call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,h_pore,0._dp) 460 end do 461 h_pore = layer(1)*regolith_porosity 462 h_soil = layer(1) - h_pore 463 call add_stratum(this,0._dp,0._dp,0._dp,h_soil,h_pore,0._dp) 464 end if 703 real(dp), dimension(ncoef_poreice), parameter :: coef_poreice_0 = 0._dp 704 !~ integer(di) :: i 705 !~ real(dp) :: h_soil, h_pore, h_tot 706 707 ! CODE 708 ! ---- 709 !~ ! Creation of strata at the bottom of the layering to describe the sub-surface 710 !~ if (do_soil) then 711 !~ do i = nsoil,index_bedrock,-1 712 !~ h_soil = layer(i) - layer(i - 1) ! No porosity 713 !~ call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,0._dp,coef_poreice_0) 714 !~ end do 715 !~ do i = index_bedrock - 1,index_breccia,-1 716 !~ h_tot = layer(i) - layer(i - 1) 717 !~ h_pore = h_tot*breccia_porosity 718 !~ h_soil = h_tot - h_pore 719 !~ call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,h_pore,coef_poreice_0) 720 !~ end do 721 !~ do i = index_breccia - 1,2,-1 722 !~ h_tot = layer(i) - layer(i - 1) 723 !~ h_pore = h_tot*regolith_porosity 724 !~ h_soil = h_tot - h_pore 725 !~ call add_stratum(this,-layer(i - 1),0._dp,0._dp,h_soil,h_pore,coef_poreice_0) 726 !~ end do 727 !~ h_pore = layer(1)*regolith_porosity 728 !~ h_soil = layer(1) - h_pore 729 !~ call add_stratum(this,0._dp,0._dp,0._dp,h_soil,h_pore,coef_poreice_0) 730 !~ end if 731 732 ! Creation of a neutral base stratum at the surface 733 call add_stratum(this,0._dp,0._dp,0._dp,0._dp,0._dp,coef_poreice_0) 465 734 466 735 END SUBROUTINE ini_layering … … 648 917 k = 1 649 918 do while (associated(current)) 650 layerings_array(ig,islope,k,1) = current%top_elevation 651 layerings_array(ig,islope,k,2) = current%h_co2ice 652 layerings_array(ig,islope,k,3) = current%h_h2oice 653 layerings_array(ig,islope,k,4) = current%h_dust 654 layerings_array(ig,islope,k,5) = current%h_pore 655 layerings_array(ig,islope,k,6) = current%poreice_volfrac 919 layerings_array(ig,islope,k,1) = current%top_elevation 920 layerings_array(ig,islope,k,2) = current%h_co2ice 921 layerings_array(ig,islope,k,3) = current%h_h2oice 922 layerings_array(ig,islope,k,4) = current%h_dust 923 layerings_array(ig,islope,k,5) = current%h_pore 924 layerings_array(ig,islope,k,6:9) = current%coef_poreice(:) 925 layerings_array(ig,islope,k,10) = current%form_date 656 926 current => current%up 657 927 k = k + 1 … … 704 974 layerings_array(ig,islope,k,2),layerings_array(ig,islope,k,3), & 705 975 layerings_array(ig,islope,k,4),layerings_array(ig,islope,k,5), & 706 layerings_array(ig,islope,k,6 ))976 layerings_array(ig,islope,k,6:9),layerings_array(ig,islope,k,10)) 707 977 end do 708 978 end do … … 752 1022 do ig = 1,ngrid 753 1023 do islope = 1,nslope 754 if ( is_co2ice_str(layerings_map(ig,islope)%top)) then1024 if (get_str_type(layerings_map(ig,islope)%top) == STR_TYPE_CO2ICE) then 755 1025 co2_ice(ig,islope) = layerings_map(ig,islope)%top%h_co2ice*rho_co2ice 756 else if ( is_h2oice_str(layerings_map(ig,islope)%top)) then1026 else if (get_str_type(layerings_map(ig,islope)%top) == STR_TYPE_H2OICE) then 757 1027 h2o_ice(ig,islope) = layerings_map(ig,islope)%top%h_h2oice*rho_h2oice 758 1028 else 759 call get_s sice_layering(layerings_map(ig,islope),h2oice_depth(ig,islope),h2o_ice(ig,islope),co2_ice(ig,islope))1029 call get_subice_layering(layerings_map(ig,islope),h2oice_depth(ig,islope),h2o_ice(ig,islope),co2_ice(ig,islope)) 760 1030 end if 761 1031 end do … … 828 1098 ! DEPENDENCIES 829 1099 ! ------------ 830 use utility, only: int2str831 use display, only: print_msg, LVL_DBG832 833 ! DEPENDENCIES834 ! ------------835 1100 use geometry, only: ngrid, nslope 1101 use display, only: print_msg, LVL_DBG 1102 use utility, only: int2str 836 1103 837 1104 ! DECLARATION … … 863 1130 864 1131 !======================================================================= 1132 SUBROUTINE define_str_grid(h,dz,nmin,nmax,z) 1133 !----------------------------------------------------------------------- 1134 ! NAME 1135 ! define_str_grid 1136 ! 1137 ! DESCRIPTION 1138 ! Build a normalized-depth grid with adaptive point count. 1139 ! 1140 ! AUTHORS & DATE 1141 ! JB Clement, 04/2026 1142 ! 1143 ! NOTES 1144 ! 1145 !----------------------------------------------------------------------- 1146 1147 ! DECLARATION 1148 ! ----------- 1149 implicit none 1150 1151 ! ARGUMENTS 1152 ! --------- 1153 real(dp), intent(in) :: h, dz 1154 integer(di), intent(in) :: nmin, nmax 1155 real(dp), dimension(:), allocatable, intent(out) :: z 1156 1157 ! LOCAL VARIABLES 1158 ! --------------- 1159 integer(di) :: n, i 1160 1161 ! CODE 1162 ! ---- 1163 n = nmin 1164 if (dz > eps) n = ceiling(max(0._dp,h)/dz) 1165 n = max(n,nmin) 1166 n = min(n,nmax) 1167 n = max(n,2_di) 1168 1169 if (allocated(z)) deallocate(z) 1170 allocate(z(n)) 1171 do i = 1,n 1172 z(i) = real(i - 1,dp)/real(n - 1,dp) 1173 end do 1174 1175 END SUBROUTINE define_str_grid 1176 !======================================================================= 1177 1178 !======================================================================= 1179 FUNCTION is_poreice_empty(str) RESULT(profile_empty) 1180 !----------------------------------------------------------------------- 1181 ! NAME 1182 ! is_poreice_empty 1183 ! 1184 ! DESCRIPTION 1185 ! Determine if a pore ice profile is empty. 1186 ! 1187 ! AUTHORS & DATE 1188 ! JB Clement, 04/2026 1189 ! 1190 ! NOTES 1191 ! 1192 !----------------------------------------------------------------------- 1193 1194 ! DECLARATION 1195 ! ----------- 1196 implicit none 1197 1198 ! ARGUMENTS 1199 ! --------- 1200 type(stratum), intent(in) :: str 1201 1202 ! LOCAL VARIABLES 1203 ! --------------- 1204 logical(k4) :: profile_empty 1205 integer(di) :: i 1206 1207 ! CODE 1208 ! ---- 1209 profile_empty = .true. 1210 do i = 1,ncoef_poreice 1211 if (abs(str%coef_poreice(i)) > tol) then 1212 profile_empty = .false. 1213 exit 1214 end if 1215 end do 1216 1217 END FUNCTION is_poreice_empty 1218 !======================================================================= 1219 1220 !======================================================================= 1221 FUNCTION has_compact_ice(str) RESULT(has_ice) 1222 !----------------------------------------------------------------------- 1223 ! NAME 1224 ! has_compact_ice 1225 ! 1226 ! DESCRIPTION 1227 ! Check if a stratum contains compact ice. 1228 ! 1229 ! AUTHORS & DATE 1230 ! JB Clement, 04/2026 1231 ! 1232 ! NOTES 1233 ! 1234 !----------------------------------------------------------------------- 1235 1236 ! DECLARATION 1237 ! ----------- 1238 implicit none 1239 1240 ! ARGUMENTS 1241 ! --------- 1242 type(stratum), intent(in) :: str 1243 1244 ! LOCAL VARIABLES 1245 ! --------------- 1246 logical(k4) :: has_ice 1247 1248 ! CODE 1249 ! ---- 1250 has_ice = .false. 1251 if (str%h_co2ice > tol .or. str%h_h2oice > tol) has_ice = .true. 1252 1253 END FUNCTION has_compact_ice 1254 !======================================================================= 1255 1256 !======================================================================= 1257 FUNCTION is_pure_dust(str) RESULT(pure_dust) 1258 !----------------------------------------------------------------------- 1259 ! NAME 1260 ! is_pure_dust 1261 ! 1262 ! DESCRIPTION 1263 ! Check if a stratum contains only dust (not cemented by ice). 1264 ! 1265 ! AUTHORS & DATE 1266 ! JB Clement, 04/2026 1267 ! 1268 ! NOTES 1269 ! 1270 !----------------------------------------------------------------------- 1271 1272 ! DECLARATION 1273 ! ----------- 1274 implicit none 1275 1276 ! ARGUMENTS 1277 ! --------- 1278 type(stratum), intent(in) :: str 1279 1280 ! LOCAL VARIABLES 1281 ! --------------- 1282 logical(k4) :: pure_dust 1283 1284 ! CODE 1285 ! ---- 1286 pure_dust = .false. 1287 if (str%h_dust > tol .and. .not. has_compact_ice(str) .and. is_poreice_empty(str)) pure_dust = .true. 1288 1289 END FUNCTION is_pure_dust 1290 !======================================================================= 1291 1292 !======================================================================= 1293 FUNCTION is_dust_lag(str) RESULT(dust_lag) 1294 !----------------------------------------------------------------------- 1295 ! NAME 1296 ! is_dust_lag 1297 ! 1298 ! DESCRIPTION 1299 ! Check if a stratum is a dust lag. 1300 ! 1301 ! AUTHORS & DATE 1302 ! JB Clement, 04/2026 1303 ! 1304 ! NOTES 1305 ! 1306 !----------------------------------------------------------------------- 1307 1308 ! DECLARATION 1309 ! ----------- 1310 implicit none 1311 1312 ! ARGUMENTS 1313 ! --------- 1314 type(stratum), intent(in) :: str 1315 1316 ! LOCAL VARIABLES 1317 ! --------------- 1318 logical(k4) :: dust_lag 1319 1320 ! CODE 1321 ! ---- 1322 dust_lag = .false. 1323 if (str%h_dust > tol .and. .not. has_compact_ice(str)) dust_lag = .true. 1324 1325 END FUNCTION is_dust_lag 1326 !======================================================================= 1327 1328 !======================================================================= 1329 SUBROUTINE compute_poreice_dustlag(this) 1330 !----------------------------------------------------------------------- 1331 ! NAME 1332 ! compute_poreice_dustlag 1333 ! 1334 ! DESCRIPTION 1335 ! Update top dust-lag pore-ice coefficients from dynamic pore filling. 1336 ! 1337 ! AUTHORS & DATE 1338 ! JB Clement, 04/2026 1339 ! 1340 ! NOTES 1341 ! 1342 !----------------------------------------------------------------------- 1343 1344 ! DEPENDENCIES 1345 ! ------------ 1346 use subsurface_ice, only: dyn_ss_ice_m 1347 1348 ! DECLARATION 1349 ! ----------- 1350 implicit none 1351 1352 ! ARGUMENTS 1353 ! --------- 1354 type(layering), intent(inout) :: this 1355 1356 ! LOCAL VARIABLES 1357 ! --------------- 1358 1359 ! CODE 1360 ! ---- 1361 1362 ! Identify the contiguous top dust-lag column 1363 1364 ! Ensure the top dust-lag column overlays H2O ice 1365 1366 ! Build the grid for each dust-lag stratum 1367 1368 ! If the dust lag is too thin to be resolved by the grid, skip the update 1369 1370 ! Build the full grid and pore-ice profile for the dust-lag column 1371 1372 ! Compute the dynamic grid for the pore filling model, which is finer near the surface to better resolve the expected gradients 1373 1374 ! Interpolate the pore-ice profile onto the dynamic grid, assuming that all pores are filled below the dust lag 1375 1376 ! Get the updated pore-ice profile 1377 1378 ! Interpolate the updated pore-ice profile back onto the stratum-specific grids 1379 1380 ! Update the pore-ice coefficients for each dust-lag stratum 1381 1382 1383 END SUBROUTINE compute_poreice_dustlag 1384 !======================================================================= 1385 1386 !======================================================================= 865 1387 FUNCTION thickness(this) RESULT(h_str) 866 1388 !----------------------------------------------------------------------- … … 898 1420 899 1421 !======================================================================= 900 FUNCTION is_co2ice_str(str) RESULT(co2ice_str) 901 !----------------------------------------------------------------------- 902 ! NAME 903 ! is_co2ice_str 904 ! 905 ! DESCRIPTION 906 ! Check if a stratum can be considered as a CO2 ice layer. 907 ! 908 ! AUTHORS & DATE 909 ! JB Clement, 2024 910 ! 911 ! NOTES 912 ! 913 !----------------------------------------------------------------------- 914 915 ! DECLARATION 916 ! ----------- 917 implicit none 918 919 ! ARGUMENTS 920 ! --------- 921 type(stratum), intent(in) :: str 922 923 ! LOCAL VARIABLES 924 ! --------------- 925 logical(k4) :: co2ice_str 926 927 ! CODE 928 ! ---- 929 co2ice_str = .false. 930 if (str%h_co2ice > str%h_h2oice .and. str%h_co2ice > str%h_dust) co2ice_str = .true. 931 932 END FUNCTION is_co2ice_str 933 !======================================================================= 934 935 !======================================================================= 936 FUNCTION is_h2oice_str(str) RESULT(h2oice_str) 937 !----------------------------------------------------------------------- 938 ! NAME 939 ! is_h2oice_str 940 ! 941 ! DESCRIPTION 942 ! Check if a stratum can be considered as a H2O ice layer. 943 ! 944 ! AUTHORS & DATE 945 ! JB Clement, 2024 946 ! 947 ! NOTES 948 ! 949 !----------------------------------------------------------------------- 950 951 ! DECLARATION 952 ! ----------- 953 implicit none 954 955 ! ARGUMENTS 956 ! --------- 957 type(stratum), intent(in) :: str 958 959 ! LOCAL VARIABLES 960 ! --------------- 961 logical(k4) :: h2oice_str 962 963 ! CODE 964 ! ---- 965 h2oice_str = .false. 966 if (str%h_h2oice > str%h_co2ice .and. str%h_h2oice > str%h_dust) h2oice_str = .true. 967 968 END FUNCTION is_h2oice_str 969 !======================================================================= 970 971 !======================================================================= 972 FUNCTION is_dust_str(str) RESULT(dust_str) 973 !----------------------------------------------------------------------- 974 ! NAME 975 ! is_dust_str 976 ! 977 ! DESCRIPTION 978 ! Check if a stratum can be considered as a dust layer. 979 ! 980 ! AUTHORS & DATE 981 ! JB Clement, 2024 982 ! 983 ! NOTES 984 ! 985 !----------------------------------------------------------------------- 986 987 ! DECLARATION 988 ! ----------- 989 implicit none 990 991 ! ARGUMENTS 992 ! --------- 993 type(stratum), intent(in) :: str 994 995 ! LOCAL VARIABLES 996 ! --------------- 997 logical(k4) :: dust_str 998 999 ! CODE 1000 ! ---- 1001 dust_str = .false. 1002 if (str%h_dust > str%h_co2ice .and. str%h_dust > str%h_h2oice) dust_str = .true. 1003 1004 END FUNCTION is_dust_str 1005 !======================================================================= 1006 1007 !======================================================================= 1008 FUNCTION is_dust_lag(str) RESULT(dust_lag) 1009 !----------------------------------------------------------------------- 1010 ! NAME 1011 ! is_dust_lag 1012 ! 1013 ! DESCRIPTION 1014 ! Check if a stratum is a dust lag (no ice content). 1015 ! 1016 ! AUTHORS & DATE 1017 ! JB Clement, 2024 1018 ! 1019 ! NOTES 1020 ! 1021 !----------------------------------------------------------------------- 1022 1023 ! DECLARATION 1024 ! ----------- 1025 implicit none 1026 1027 ! ARGUMENTS 1028 ! --------- 1029 type(stratum), intent(in) :: str 1030 1031 ! LOCAL VARIABLES 1032 ! --------------- 1033 logical(k4) :: dust_lag 1034 1035 ! CODE 1036 ! ---- 1037 dust_lag = .false. 1038 if (str%h_co2ice < tol .and. str%h_h2oice < tol .and. str%poreice_volfrac < tol) dust_lag = .true. 1039 1040 END FUNCTION is_dust_lag 1041 !======================================================================= 1042 1043 !======================================================================= 1044 SUBROUTINE get_ssice_layering(this,h_toplag,h2o_ice,co2_ice) 1045 !----------------------------------------------------------------------- 1046 ! NAME 1047 ! get_ssice_layering 1422 SUBROUTINE get_subice_layering(this,h_toplag,h2o_ice,co2_ice) 1423 !----------------------------------------------------------------------- 1424 ! NAME 1425 ! get_subice_layering 1048 1426 ! 1049 1427 ! DESCRIPTION … … 1080 1458 do 1081 1459 h_toplag = h_toplag + thickness(current) 1082 if (.not. associated(current%down)) then ! Bottom of thelayering and only dust lag strata1460 if (.not. associated(current%down)) then ! Bottom of layering and only dust lag strata 1083 1461 h_toplag = 0._dp 1084 1462 return … … 1089 1467 1090 1468 ! Is the stratum below the dust lag made of ice? 1091 if (current%h_h2oice > 0._dp.and. current%h_h2oice > current%h_co2ice) then ! Yes, there is more H2O ice than CO2 ice1469 if (current%h_h2oice > tol .and. current%h_h2oice > current%h_co2ice) then ! Yes, there is more H2O ice than CO2 ice 1092 1470 if (h_toplag < h_patchy_dust) then ! But the dust lag is too thin to considered ice as subsurface ice 1093 1471 h_toplag = 0._dp 1094 1472 h2o_ice = current%h_h2oice*rho_h2oice 1095 1473 end if 1096 else if (current%h_co2ice > 0._dp.and. current%h_co2ice > current%h_h2oice) then ! No, there is more CO2 ice than H2O ice1474 else if (current%h_co2ice > tol .and. current%h_co2ice > current%h_h2oice) then ! No, there is more CO2 ice than H2O ice 1097 1475 if (h_toplag < h_patchy_ice) co2_ice = current%h_co2ice*rho_co2ice ! The dust lag is too thin to considered ice as subsurface ice 1098 1476 h_toplag = 0._dp ! Because it matters only for H2O ice 1099 1477 end if 1100 1478 1101 END SUBROUTINE get_s sice_layering1479 END SUBROUTINE get_subice_layering 1102 1480 !======================================================================= 1103 1481 … … 1142 1520 1143 1521 !======================================================================= 1144 SUBROUTINE sublimate_ice(this,str,h2subl_co2ice,h2subl_h2oice,new_lag,zlag )1522 SUBROUTINE sublimate_ice(this,str,h2subl_co2ice,h2subl_h2oice,new_lag,zlag,current_date) 1145 1523 !----------------------------------------------------------------------- 1146 1524 ! NAME … … 1168 1546 real(dp), intent(inout) :: zlag 1169 1547 real(dp), intent(in) :: h2subl_co2ice, h2subl_h2oice 1170 1171 ! LOCAL VARIABLES 1172 ! --------------- 1173 real(dp) :: h2subl, h_ice, h_pore, h_pore_new, h_tot 1174 real(dp) :: hlag_dust, hlag_h2oice 1175 type(stratum), pointer :: current 1548 real(dp), intent(in) :: current_date 1549 1550 ! LOCAL VARIABLES 1551 ! --------------- 1552 real(dp), dimension(ncoef_poreice), parameter :: coef_poreice_0 = 0._dp 1553 real(dp) :: h2subl, h_ice, h_pore, h_pore_new, h_tot 1554 real(dp) :: hlag_dust, hlag_h2oice 1555 type(stratum), pointer :: current 1176 1556 1177 1557 ! CODE … … 1217 1597 ! New stratum above the current stratum as a lag layer 1218 1598 if (new_lag) then 1219 call insert_stratum(this,str,str%top_elevation + h_tot,0._dp,hlag_h2oice,hlag_dust,h_pore_new, 0._dp)1599 call insert_stratum(this,str,str%top_elevation + h_tot,0._dp,hlag_h2oice,hlag_dust,h_pore_new,coef_poreice_0,current_date) 1220 1600 new_lag = .false. 1221 1601 else … … 1268 1648 h_toplag = 0._dp 1269 1649 currentloc => current%up 1270 do while ( is_dust_lag(currentloc) .and. associated(currentloc%up))1650 do while (associated(currentloc%up) .and. is_dust_lag(currentloc)) 1271 1651 h_toplag = h_toplag + thickness(currentloc) 1272 1652 currentloc => currentloc%up … … 1310 1690 ! ARGUMENTS 1311 1691 ! --------- 1312 real(dp), intent(in) :: h2oice_depth1313 real(dp), intent(inout) :: flux_ssice, d_h2oice1314 integer(di), intent(in), optional :: icell, jcell1692 real(dp), intent(in) :: h2oice_depth 1693 real(dp), intent(inout) :: flux_ssice, d_h2oice 1694 integer(di), intent(in), optional :: icell, jcell 1315 1695 1316 1696 ! LOCAL VARIABLES … … 1367 1747 1368 1748 !======================================================================= 1369 SUBROUTINE evolve_layering(this,d_co2ice,d_h2oice,h2oice_depth,flux_ssice,new_str,zshift_surf,new_lag,zlag,current, icell,jcell)1749 SUBROUTINE evolve_layering(this,d_co2ice,d_h2oice,h2oice_depth,flux_ssice,new_str,zshift_surf,new_lag,zlag,current,current_date,icell,jcell) 1370 1750 !----------------------------------------------------------------------- 1371 1751 ! NAME … … 1398 1778 ! ARGUMENTS 1399 1779 ! --------- 1780 real(dp), intent(in) :: h2oice_depth 1781 real(dp), intent(in) :: current_date 1782 integer(di), intent(in), optional :: icell, jcell 1400 1783 real(dp), intent(inout) :: d_co2ice, d_h2oice, flux_ssice 1401 real(dp), intent(in) :: h2oice_depth1402 1784 type(layering), intent(inout) :: this 1403 1785 type(stratum), pointer, intent(inout) :: current 1404 1786 logical(k4), intent(inout) :: new_str, new_lag 1405 1787 real(dp), intent(out) :: zshift_surf, zlag 1406 integer(di), intent(in), optional :: icell, jcell 1407 1408 ! LOCAL VARIABLES1409 ! --------------- 1410 real(dp) :: dh_co2ice, dh_h2oice, dh_dust, dh_dust_co2, dh_dust_h2o1411 real(dp) :: h_co2ice_old, h_h2oice_old1412 real(dp) :: h2subl_co2ice, h2subl_h2oice, h2subl_co2ice_tot, h2subl_h2oice_tot1413 real(dp) :: h2lift, h2lift_tot, h_pore, h_tot!, R_subl1414 logical(k4) :: unexpected1788 1789 ! LOCAL VARIABLES 1790 ! --------------- 1791 real(dp), dimension(ncoef_poreice), parameter :: coef_poreice_0 = 0._dp 1792 real(dp) :: dh_co2ice, dh_h2oice, dh_dust, dh_dust_co2, dh_dust_h2o 1793 real(dp) :: h_co2ice_old, h_h2oice_old 1794 real(dp) :: h2subl_co2ice, h2subl_h2oice, h2subl_co2ice_tot, h2subl_h2oice_tot 1795 real(dp) :: h2lift, h2lift_tot, h_pore, h_tot!, R_subl 1796 logical(k4) :: unexpected 1415 1797 1416 1798 ! CODE … … 1456 1838 ! New stratum at the top of the layering 1457 1839 if (new_str) then 1458 call add_stratum(this,this%top%top_elevation + h_tot,dh_co2ice,dh_h2oice,dh_dust,h_pore, 0._dp)1840 call add_stratum(this,this%top%top_elevation + h_tot,dh_co2ice,dh_h2oice,dh_dust,h_pore,coef_poreice_0,current_date) 1459 1841 new_str = .false. 1460 1842 else … … 1492 1874 end if 1493 1875 ! Ice sublimation 1494 if (h2subl_co2ice > 0._dp .or. h2subl_h2oice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,h2subl_h2oice,new_lag,zlag )1876 if (h2subl_co2ice > 0._dp .or. h2subl_h2oice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,h2subl_h2oice,new_lag,zlag,current_date) 1495 1877 1496 1878 ! Is there dust to lift? 1497 1879 h2lift = 0._dp 1498 if (is_ dust_lag(current) .and. h2lift_tot > 0._dp) then1880 if (is_pure_dust(current) .and. h2lift_tot > 0._dp) then 1499 1881 h2lift = min(h2lift_tot,current%h_dust) 1500 1882 h2lift_tot = h2lift_tot - h2lift … … 1502 1884 if (h2lift > 0._dp) call lift_dust(this,current,h2lift) 1503 1885 else if (associated(current%up)) then 1504 if (is_ dust_lag(current%up) .and. h2lift_tot > 0._dp) then1886 if (is_pure_dust(current%up) .and. h2lift_tot > 0._dp) then 1505 1887 h2lift = min(h2lift_tot,current%up%h_dust) 1506 1888 h2lift_tot = h2lift_tot - h2lift … … 1511 1893 1512 1894 ! Still ice to be sublimated or dust to be lifted and no more ice in the current stratum so we move to the underlying stratum 1513 if ((h2subl_co2ice_tot > 0. .or. h2subl_h2oice_tot > 0._dp .or. h2lift_tot > 0._dp) .and. current%h_co2ice < tol .and. current%h_h2oice < tol .and. current%poreice_volfrac < tol) then 1895 if ((h2subl_co2ice_tot > 0. .or. h2subl_h2oice_tot > 0._dp .or. h2lift_tot > 0._dp) .and. & 1896 current%h_co2ice < tol .and. current%h_h2oice < tol .and. is_poreice_empty(current)) then 1514 1897 current => current%down 1515 1898 new_lag = .true. … … 1550 1933 end if 1551 1934 ! Ice sublimation 1552 if (h2subl_co2ice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,0._dp,new_lag,zlag )1935 if (h2subl_co2ice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,0._dp,new_lag,zlag,current_date) 1553 1936 1554 1937 ! Is there dust to lift? 1555 1938 h2lift = 0._dp 1556 if (is_ dust_lag(current) .and. h2lift_tot > 0._dp) then1939 if (is_pure_dust(current) .and. h2lift_tot > 0._dp) then 1557 1940 h2lift = min(h2lift_tot,current%h_dust) 1558 1941 h2lift_tot = h2lift_tot - h2lift … … 1560 1943 if (h2lift > 0._dp) call lift_dust(this,current,h2lift) 1561 1944 else if (associated(current%up)) then 1562 if (is_ dust_lag(current%up) .and. h2lift_tot > 0._dp) then1945 if (is_pure_dust(current%up) .and. h2lift_tot > 0._dp) then 1563 1946 h2lift = min(h2lift_tot,current%up%h_dust) 1564 1947 h2lift_tot = h2lift_tot - h2lift … … 1585 1968 ! New stratum at the top of the layering 1586 1969 if (new_str) then 1587 call add_stratum(this,this%top%top_elevation + h_tot,0._dp,dh_h2oice,dh_dust_h2o,h_pore, 0._dp)1970 call add_stratum(this,this%top%top_elevation + h_tot,0._dp,dh_h2oice,dh_dust_h2o,h_pore,coef_poreice_0,current_date) 1588 1971 new_str = .false. 1589 1972 else … … 1599 1982 ! New stratum at the top of the layering 1600 1983 if (new_str) then 1601 call add_stratum(this,this%top%top_elevation + h_tot,0._dp,dh_h2oice,dh_dust_h2o,h_pore, 0._dp)1984 call add_stratum(this,this%top%top_elevation + h_tot,0._dp,dh_h2oice,dh_dust_h2o,h_pore,coef_poreice_0,current_date) 1602 1985 new_str = .false. 1603 1986 else … … 1625 2008 end if 1626 2009 ! Ice sublimation 1627 if (h2subl_co2ice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,0._dp,new_lag,zlag )2010 if (h2subl_co2ice > 0._dp) call sublimate_ice(this,current,h2subl_co2ice,0._dp,new_lag,zlag,current_date) 1628 2011 1629 2012 ! Is there dust to lift? 1630 2013 h2lift = 0._dp 1631 if (is_ dust_lag(current) .and. h2lift_tot > 0._dp) then2014 if (is_pure_dust(current) .and. h2lift_tot > 0._dp) then 1632 2015 h2lift = min(h2lift_tot,current%h_dust) 1633 2016 h2lift_tot = h2lift_tot - h2lift … … 1635 2018 if (h2lift > 0._dp) call lift_dust(this,current,h2lift) 1636 2019 else if (associated(current%up)) then 1637 if (is_ dust_lag(current%up) .and. h2lift_tot > 0.) then2020 if (is_pure_dust(current%up) .and. h2lift_tot > 0.) then 1638 2021 h2lift = min(h2lift_tot,current%up%h_dust) 1639 2022 h2lift_tot = h2lift_tot - h2lift 1640 2023 ! Dust lifting 1641 if (h2lift > 0. ) call lift_dust(this,current%up,h2lift)2024 if (h2lift > 0._dp) call lift_dust(this,current%up,h2lift) 1642 2025 end if 1643 2026 end if … … 1662 2045 1663 2046 if (unexpected) then 1664 call print_msg(' dh_co2ice [m/y] = '//real2str(dh_co2ice),LVL_ERR) 1665 call print_msg(' dh_h2oice [m/y] = '//real2str(dh_h2oice),LVL_ERR) 1666 call print_msg(' dh_dust [m/y] = '//real2str(dh_dust),LVL_ERR) 2047 call print_msg(' dh_co2ice [m/y] = '//real2str(dh_co2ice),LVL_ERR) 2048 call print_msg(' dh_h2oice [m/y] = '//real2str(dh_h2oice),LVL_ERR) 2049 call print_msg(' dh_dust [m/y] = '//real2str(dh_dust),LVL_ERR) 2050 call print_msg(' flux_ssice [m/y] = '//real2str(flux_ssice),LVL_ERR) 1667 2051 call stop_clean(__FILE__,__LINE__,'situation for the layering construction not managed!',1) 1668 2052 end if -
trunk/LMDZ.COMMON/libf/evolution/maths.F90
r4189 r4192 416 416 417 417 !======================================================================= 418 FUNCTION clamp(x,xmin,xmax) RESULT(y) 419 !----------------------------------------------------------------------- 420 ! NAME 421 ! clamp 422 ! 423 ! DESCRIPTION 424 ! Clamp a scalar value into an admissible interval. 425 ! 426 ! AUTHORS & DATE 427 ! JB Clement, 04/2026 428 ! 429 ! NOTES 430 ! 431 !----------------------------------------------------------------------- 432 433 ! DECLARATION 434 ! ----------- 435 implicit none 436 437 ! ARGUMENTS 438 ! --------- 439 real(dp), intent(in) :: x 440 real(dp), intent(in) :: xmin, xmax 441 442 ! LOCAL VARIABLES 443 ! --------------- 444 real(dp) :: y 445 446 ! CODE 447 ! ---- 448 if (xmin > xmax) then 449 y = x 450 else 451 y = max(xmin,min(xmax,x)) 452 end if 453 454 END FUNCTION clamp 455 !======================================================================= 456 457 !======================================================================= 458 SUBROUTINE solve_linear_system(a,b,x) 459 !----------------------------------------------------------------------- 460 ! NAME 461 ! solve_linear_system 462 ! 463 ! DESCRIPTION 464 ! Solve a dense linear system by Gaussian elimination. 465 ! 466 ! AUTHORS & DATE 467 ! JB Clement, 04/2026 468 ! 469 ! NOTES 470 ! Uses partial pivoting and returns ok = .false. if singular. 471 !----------------------------------------------------------------------- 472 473 ! DEPENDENCIES 474 ! ------------ 475 use stoppage, only: stop_clean 476 477 ! DECLARATION 478 ! ----------- 479 implicit none 480 481 ! ARGUMENTS 482 ! --------- 483 real(dp), dimension(:,:), intent(in) :: a 484 real(dp), dimension(:), intent(in) :: b 485 real(dp), dimension(:), intent(out) :: x 486 487 ! LOCAL VARIABLES 488 ! --------------- 489 integer(di) :: n, i, j, k, ipiv 490 real(dp) :: pivot, fac 491 real(dp), dimension(:), allocatable :: rhs, rowtmp 492 real(dp), dimension(:,:), allocatable :: m 493 494 ! CODE 495 ! ---- 496 ! Initialization 497 n = size(b) 498 x(:) = 0._dp 499 if (size(a,1) /= n .or. size(a,2) /= n) call stop_clean(__FILE__,__LINE__,"size of a must be n x n!",1) 500 if (size(x) /= n) call stop_clean(__FILE__,__LINE__,"size of x must match size of b!",1) 501 502 allocate(m(n,n),rhs(n),rowtmp(n)) 503 m(:,:) = a(:,:) 504 rhs(:) = b(:) 505 506 ! Gaussian elimination with partial pivoting 507 do k = 1,n - 1 508 ipiv = k 509 pivot = abs(m(k,k)) 510 do i = k + 1,n 511 if (abs(m(i,k)) > pivot) then 512 pivot = abs(m(i,k)) 513 ipiv = i 514 end if 515 end do 516 if (pivot <= eps) call stop_clean(__FILE__,__LINE__,"matrix is singular!",1) 517 518 if (ipiv /= k) then 519 rowtmp(:) = m(k,:) 520 m(k,:) = m(ipiv,:) 521 m(ipiv,:) = rowtmp(:) 522 fac = rhs(k) 523 rhs(k) = rhs(ipiv) 524 rhs(ipiv) = fac 525 end if 526 527 do i = k + 1,n 528 fac = m(i,k)/m(k,k) 529 m(i,k:n) = m(i,k:n) - fac*m(k,k:n) 530 rhs(i) = rhs(i) - fac*rhs(k) 531 end do 532 end do 533 534 if (abs(m(n,n)) <= eps) call stop_clean(__FILE__,__LINE__,"matrix is singular!",1) 535 536 do i = n,1,-1 537 x(i) = rhs(i) 538 do j = i + 1,n 539 x(i) = x(i) - m(i,j)*x(j) 540 end do 541 if (abs(m(i,i)) <= eps) call stop_clean(__FILE__,__LINE__,"matrix is singular!",1) 542 x(i) = x(i)/m(i,i) 543 end do 544 545 END SUBROUTINE solve_linear_system 546 !======================================================================= 547 548 !======================================================================= 549 SUBROUTINE coef2prof(coef,x,fx) 550 !----------------------------------------------------------------------- 551 ! NAME 552 ! coef2prof 553 ! 554 ! DESCRIPTION 555 ! Evaluate polynomial coefficients on a 1D profile. 556 ! 557 ! AUTHORS & DATE 558 ! JB Clement, 04/2026 559 ! 560 ! NOTES 561 ! Coefficients are ordered from degree 0 to degree n-1. 562 !----------------------------------------------------------------------- 563 564 ! DEPENDENCIES 565 ! ----------- 566 use stoppage, only: stop_clean 567 568 ! DECLARATION 569 ! ----------- 570 implicit none 571 572 ! ARGUMENTS 573 ! --------- 574 real(dp), dimension(:), intent(in) :: coef 575 real(dp), dimension(:), intent(in) :: x 576 real(dp), dimension(:), intent(out) :: fx 577 578 ! LOCAL VARIABLES 579 ! --------------- 580 integer(di) :: i, j, ncoef 581 real(dp) :: yloc 582 583 ! CODE 584 ! ---- 585 ! Initialization 586 fx(:) = 0._dp 587 if (size(fx) /= size(x)) call stop_clean(__FILE__,__LINE__,"size of fx must match size of x!",1) 588 589 ncoef = int(size(coef),di) 590 591 ! Evaluate the polynomial at each point using Horner's method for numerical stability 592 do i = 1,size(x) 593 yloc = coef(ncoef) 594 do j = ncoef - 1,1,-1 595 yloc = yloc*x(i) + coef(j) 596 end do 597 fx(i) = yloc 598 end do 599 600 END SUBROUTINE coef2prof 601 !======================================================================= 602 603 !======================================================================= 604 SUBROUTINE prof2coef(x,fx,coef) 605 !----------------------------------------------------------------------- 606 ! NAME 607 ! prof2coef 608 ! 609 ! DESCRIPTION 610 ! Fit polynomial coefficients from a 1D profile by least squares. 611 ! 612 ! AUTHORS & DATE 613 ! JB Clement, 04/2026 614 ! 615 ! NOTES 616 ! Coefficients are ordered from degree 0 to degree n-1. 617 !----------------------------------------------------------------------- 618 619 ! DEPENDENCIES 620 ! ------------ 621 use stoppage, only: stop_clean 622 623 ! DECLARATION 624 ! ----------- 625 implicit none 626 627 ! ARGUMENTS 628 ! --------- 629 real(dp), dimension(:), intent(in) :: x 630 real(dp), dimension(:), intent(in) :: fx 631 real(dp), dimension(:), intent(out) :: coef 632 633 ! LOCAL VARIABLES 634 ! --------------- 635 integer(di) :: i, j, k, ncoef 636 real(dp), dimension(:), allocatable :: rhs, basis 637 real(dp), dimension(:,:), allocatable :: normal 638 639 ! CODE 640 ! ---- 641 ! Initialization 642 coef(:) = 0._dp 643 ncoef = int(size(coef),di) 644 if (size(fx) /= size(x)) call stop_clean(__FILE__,__LINE__,"size of fx must match size of x!",1) 645 if (size(x) < ncoef) call stop_clean(__FILE__,__LINE__,"size of x must be at least as large as the number of coefficients!",1) 646 647 allocate(rhs(ncoef),basis(ncoef),normal(ncoef,ncoef)) 648 normal(:,:) = 0._dp 649 rhs(:) = 0._dp 650 651 ! Set up the normal equations for least squares fitting 652 do i = 1,size(x) 653 basis(1) = 1._dp 654 do j = 2,ncoef 655 basis(j) = basis(j - 1)*x(i) 656 end do 657 658 do j = 1,ncoef 659 rhs(j) = rhs(j) + basis(j)*fx(i) 660 do k = 1,ncoef 661 normal(j,k) = normal(j,k) + basis(j)*basis(k) 662 end do 663 end do 664 end do 665 666 ! Solve the linear system 667 call solve_linear_system(normal,rhs,coef) 668 669 END SUBROUTINE prof2coef 670 !======================================================================= 671 672 !======================================================================= 673 FUNCTION interp_profile(x_nodes,y_nodes,x_eval) RESULT(y_eval) 674 !----------------------------------------------------------------------- 675 ! NAME 676 ! interp_profile 677 ! 678 ! DESCRIPTION 679 ! Interpolate a profile given at nodes. 680 ! 681 ! AUTHORS & DATE 682 ! JB Clement, 04/2026 683 ! 684 ! NOTES 685 ! Performs piecewise linear interpolation. 686 !----------------------------------------------------------------------- 687 688 ! DECLARATION 689 ! ----------- 690 implicit none 691 692 ! ARGUMENTS 693 ! --------- 694 real(dp), dimension(:), intent(in) :: x_nodes, y_nodes 695 real(dp), intent(in) :: x_eval 696 697 ! LOCAL VARIABLES 698 ! --------------- 699 real(dp) :: y_eval, weight 700 integer(di) :: i, n 701 702 ! CODE 703 ! ---- 704 ! Initialization 705 n = int(size(x_nodes),di) 706 y_eval = 0._dp 707 if (n <= 0_di .or. size(y_nodes) /= n) return 708 709 ! Handle out-of-bounds cases by clamping to the nearest node value 710 if (x_eval <= x_nodes(1)) then 711 y_eval = y_nodes(1) 712 return 713 end if 714 if (x_eval >= x_nodes(n)) then 715 y_eval = y_nodes(n) 716 return 717 end if 718 719 ! Find the interval containing x_eval and perform linear interpolation 720 do i = 1,n - 1 721 if (x_eval <= x_nodes(i + 1)) then 722 if (abs(x_nodes(i + 1) - x_nodes(i)) <= eps) then ! Avoid division by zero if nodes are too close 723 y_eval = y_nodes(i) 724 else 725 weight = (x_eval - x_nodes(i))/(x_nodes(i + 1) - x_nodes(i)) 726 y_eval = weight*(y_nodes(i + 1) - y_nodes(i)) + y_nodes(i) 727 end if 728 return 729 end if 730 end do 731 732 y_eval = y_nodes(n) 733 734 END FUNCTION interp_profile 735 !======================================================================= 736 737 !======================================================================= 418 738 FUNCTION limit_slope(dqL,dqR,limiter) RESULT(limited_slope) 419 739 !----------------------------------------------------------------------- … … 426 746 ! 427 747 ! AUTHORS & DATE 428 ! JB Clement, 02/ s2026748 ! JB Clement, 02/2026 429 749 ! 430 750 ! NOTES … … 480 800 ! 481 801 ! AUTHORS & DATE 482 ! JB Clement, 02/ s2026802 ! JB Clement, 02/2026 483 803 ! 484 804 ! NOTES -
trunk/LMDZ.COMMON/libf/evolution/pem.F90
r4191 r4192 36 36 use glaciers, only: h2oice_flow, co2ice_flow, flow_co2glaciers, flow_h2oglaciers 37 37 use ice_table, only: icetable_equilibrium, icetable_dynamic, evolve_ice_table 38 use layered_deposits, only: do_layering, del_layering, evolve_layering, ptrarray, layering2surfice, surfice2layering, print_layering 38 use layered_deposits, only: do_layering, del_layering, evolve_layering, ptrarray, layering2surfice, surfice2layering, & 39 print_layering, compute_poreice_dustlag 39 40 use maths, only: pi 40 41 use numerics, only: dp, qp, di, li, k4, eps, eps_qp … … 238 239 do i = 1,ngrid 239 240 call evolve_layering(layerings_map(i,islope),d_co2ice(i,islope),d_h2oice(i,islope),h2oice_depth(i,islope),flux_ssice_avg(i,islope), & 240 new_str(i,islope),zshift_surf(i,islope),new_lag(i,islope),zlag(i,islope),current(i,islope)%p,i,islope) 241 new_str(i,islope),zshift_surf(i,islope),new_lag(i,islope),zlag(i,islope),current(i,islope)%p, & 242 pem_ini_date + n_yr_sim + n_yr_run + dt,i,islope) 243 !if (do_soil .and. icetable_dynamic) then 244 ! call compute_poreice_dustlag(layerings_map(i,islope)) 245 !end if 241 246 if (is_lvl_enabled(LVL_DBG)) call print_layering(layerings_map(i,islope)) 242 247 end do -
trunk/LMDZ.COMMON/libf/evolution/soil.F90
r4188 r4192 287 287 if (size(soildepth_PCM_in) /= nsoil_PCM) call stop_clean(__FILE__,__LINE__,'soil size mismatch between "startfi.nc" and geometry dimensions!',1) 288 288 289 ! Set PCM soil depths read from "startfi.nc" to the mid-layer depth array289 ! Set PCM soil layer depths read from "startfi.nc" to the mid-layer depth array 290 290 mlayer(0:nsoil_PCM - 1) = soildepth_PCM_in(:) 291 291 … … 335 335 ! --------------- 336 336 integer(di) :: ig, iloop, islope 337 real(dp) :: alpha, lay1 ! Coefficients for building layers338 real(dp) :: index_powerlaw ! Coefficient to match the PEM power law grid to the PCM grid337 real(dp) :: alpha, lay1 ! Coefficients to build soil layer depths 338 real(dp) :: index_powerlaw ! Coefficient to match the PEM power law grid to the PCM grid 339 339 340 340 ! CODE -
trunk/LMDZ.COMMON/libf/evolution/subsurface_ice.F90
r4110 r4192 14 14 ! 15 15 ! NOTES 16 ! Based on Norbert Schorgofer's code. Updated for PEM. 16 ! Wrapper for Norbert Schorgofer's code that has been slightly updated 17 ! to work with the PEM. 17 18 !----------------------------------------------------------------------- 18 19 … … 39 40 !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 40 41 41 42 !======================================================================= 43 !============================ dyn_ss_ice_m ============================= 44 !======================================================================= 45 46 42 !======================================================================= 43 !----------------------------------------------------------------------- 44 ! NAME 45 ! dyn_ss_ice_m 46 ! 47 ! DESCRIPTION 48 ! Advance subsurface ice state for one column (NP=1) using the fast 49 ! dynamic method: update pore filling, ice table depth, and soil 50 ! temperatures over a short integration. 51 ! 52 ! INPUTS 53 ! ssi_depth_in Initial ice-table depth [m] 54 ! T1 Mean/representative surface temperature [K] 55 ! Tb(nz) Initial soil temperature profile [K] 56 ! nz Number of vertical levels 57 ! thIn(1) Thermal inertia [SI] 58 ! p0(1) Total atmospheric pressure [Pa] 59 ! pfrost(1) H2O vapor partial pressure proxy [Pa] 60 ! porefill_in(nz,1) Initial pore-ice filling fraction [0-1] 61 ! 62 ! OUTPUTS 63 ! ssi_depth Updated ice-table depth [m] 64 ! porefill(nz,1) Updated pore-ice filling fraction [0-1] 65 ! Tb(nz) Updated soil temperature profile [K] 66 ! 67 ! NOTES 68 ! - This wrapper is hard-coded for NP=1. 69 ! - avrho1 is prescribed as pfrost/T1 in the underlying solver. 70 !----------------------------------------------------------------------- 47 71 SUBROUTINE dyn_ss_ice_m(ssi_depth_in,T1,Tb,nz,thIn,p0,pfrost,porefill_in,porefill,ssi_depth) 48 49 72 !*********************************************************************** 50 73 ! Retreat and growth of subsurface ice on Mars … … 53 76 use orbit, only: sol_len_s 54 77 use maths, only: pi 55 ! DECLARATION 56 ! ----------- 57 implicit none 78 implicit none 58 79 integer, parameter :: NP=1 ! # of sites 59 80 integer nz, i, k, iloop … … 208 229 209 230 end subroutine dyn_ss_ice_m 210 211 !======================================================================= 212 231 !======================================================================= 232 233 !======================================================================= 213 234 subroutine soilthprop(porosity,fill,rhocobs,tiobs,layertype, & 214 235 & newrhoc,newti,icefrac) … … 226 247 ! output are newti and newrhoc 227 248 !*********************************************************************** 228 229 ! DEPENDENCIES 230 ! ------------ 231 use stoppage, only: stop_clean 232 233 ! DECLARATION 234 ! ----------- 235 implicit none 249 use stoppage, only: stop_clean 250 implicit none 236 251 integer, intent(IN) :: layertype 237 252 real(8), intent(IN) :: porosity, fill, rhocobs, tiobs … … 295 310 296 311 end subroutine soilthprop 297 298 299 !======================================================================= 300 312 !======================================================================= 313 314 !======================================================================= 301 315 real*8 function frostpoint(p) 302 316 ! inverse of psv 303 317 ! input is partial pressure [Pascal] 304 318 ! output is temperature [Kelvin] 305 ! DECLARATION 306 ! ----------- 307 implicit none 319 implicit none 308 320 real*8 p 309 321 … … 326 338 327 339 end function frostpoint 328 329 330 !======================================================================= 331 !=========================== fast_subs_mars ============================ 332 !======================================================================= 333 334 340 !======================================================================= 341 342 !======================================================================= 335 343 !***************************************************** 336 344 ! Subroutines for fast method 337 345 ! written by Norbert Schorghofer 2007-2011 338 346 !***************************************************** 339 340 341 347 subroutine icelayer_mars(bigstep,nz,NP,thIn,rhoc,z,porosity,pfrost, & 342 348 & Tb,zdepthF,zdepthE,porefill,Tmean1,Tmean3,zdepthG, & … … 347 353 ! latitude [degree] 348 354 !************************************************************************* 349 !use ice_table, only: rho_ice355 !use ice_table, only: rho_ice 350 356 !use omp_lib 351 ! DECLARATION 352 ! ----------- 353 implicit none 357 implicit none 354 358 integer, intent(IN) :: nz, NP 355 359 real(8), intent(IN) :: bigstep … … 453 457 !$omp end parallel 454 458 end subroutine icelayer_mars 455 456 457 459 !======================================================================= 460 461 !======================================================================= 458 462 subroutine ajsub_mars(typeT, albedo0, pfrost, nz, z, ti, rhocv, & 459 463 & fracIR, fracDust, patm, avdrho, avdrhoP, avrho1, & … … 467 471 !*********************************************************************** 468 472 use orbit, only: yr_len_sol, sol_len_s 469 ! DECLARATION 470 ! ----------- 471 implicit none 473 implicit none 472 474 integer, intent(IN) :: nz, typeT 473 475 ! real(8), intent(IN) :: latitude ! in radians … … 616 618 617 619 end subroutine ajsub_mars 618 619 620 620 !======================================================================= 621 622 !======================================================================= 621 623 real*8 function tfrostco2(p) 622 624 ! the inverse of function psvco2 623 625 ! input is pressure in Pascal, output is temperature in Kelvin 624 ! DECLARATION625 ! -----------626 626 implicit none 627 627 real*8 p 628 628 tfrostco2 = 3182.48/(23.3494+log(100./p)) 629 629 end function 630 631 !======================================================================= 632 630 !======================================================================= 631 632 !======================================================================= 633 633 real*8 function psv(T) 634 634 ! saturation vapor pressure of H2O ice [Pascal] 635 635 ! input is temperature [Kelvin] 636 ! DECLARATION 637 ! ----------- 638 implicit none 636 implicit none 639 637 real*8 T 640 638 … … 659 657 660 658 end function psv 661 662 663 !======================================================================= 664 !=========================== fast_subs_univ ============================ 665 !======================================================================= 666 667 659 !======================================================================= 660 661 !======================================================================= 668 662 !***************************************************** 669 663 ! Commonly used subroutines for fast method 670 664 ! written by Norbert Schorghofer 2007-2011 671 665 !***************************************************** 672 673 666 pure function zint(y1,y2,z1,z2) 674 667 ! interpolate between two values, y1*y2<0 675 ! DECLARATION 676 ! ----------- 677 implicit none 668 implicit none 678 669 real(8), intent(IN) :: y1,y2,z1,z2 679 670 real(8) zint 680 671 zint = (y1*z2 - y2*z1)/(y1-y2) 681 672 end function zint 682 683 684 673 !======================================================================= 674 675 !======================================================================= 685 676 pure function equildepth(nz, z, rhosatav, rhosatav0, avrho1) 686 677 !*********************************************************************** … … 688 679 ! this is not the true (final) equilibrium depth 689 680 !*********************************************************************** 690 ! DECLARATION 691 ! ----------- 692 implicit none 681 implicit none 693 682 integer, intent(IN) :: nz 694 683 real(8), intent(IN) :: z(nz), rhosatav(nz) … … 711 700 if (equildepth>z(nz)) equildepth=-9999. ! happens very rarely 712 701 end function equildepth 713 714 715 702 !======================================================================= 703 704 !======================================================================= 716 705 subroutine depths_avmeth(typeT, nz, z, rhosatav, rhosatav0, rlow, avrho1, & 717 706 & porefill, typeF, zdepthF, B, ypp, typeG, zdepthG) … … 724 713 ! B = Diff*bigstep/(porosity*icedensity) [SI units] 725 714 !*********************************************************************** 726 use maths, only: deriv2_simple, deriv1_onesided, deriv1, colint 727 ! DECLARATION 728 ! ----------- 729 implicit none 715 use maths, only: deriv2_simple, deriv1_onesided, deriv1, colint 716 implicit none 730 717 integer, intent(IN) :: nz, typeT 731 718 real(8), intent(IN), dimension(nz) :: z, rhosatav, porefill … … 846 833 ! if typeG>0, then all ice at and below typeG should be erased 847 834 end subroutine depths_avmeth 848 849 850 835 !======================================================================= 836 837 !======================================================================= 851 838 pure subroutine icechanges(nz,z,typeF,avdrho,avdrhoP,ypp, & 852 839 & Diff,porosity,icefrac,bigstep,zdepthT,porefill,typeG) … … 856 843 ! a crucial subroutine 857 844 !*********************************************************** 858 use maths, only: colint845 use maths, only: colint 859 846 !use ice_table, only: rho_ice 860 ! DECLARATION 861 ! ----------- 862 implicit none 847 implicit none 863 848 integer, intent(IN) :: nz, typeF, typeG 864 849 real(8), intent(IN) :: z(nz), ypp(nz), avdrho, avdrhoP … … 940 925 end if 941 926 end subroutine icechanges 927 !======================================================================= 942 928 943 929 !======================================================================= -
trunk/LMDZ.COMMON/libf/misc/job.F90
r4191 r4192 9 9 ! 10 10 ! AUTHORS & DATE 11 ! JB Clement, 07/07/202511 ! JB Clement, 10/06/2024 12 12 ! 13 13 ! NOTES … … 38 38 ! 39 39 ! AUTHORS & DATE 40 ! JB Clement, 07/07/202540 ! JB Clement, 10/06/2024 41 41 ! 42 42 ! NOTES … … 90 90 ! 91 91 ! AUTHORS & DATE 92 ! JB Clement, 07/07/202592 ! JB Clement, 10/06/2024 93 93 ! 94 94 ! NOTES -
trunk/LMDZ.COMMON/makelmdz_fcm
r4191 r4192 659 659 ! 660 660 ! AUTHORS & DATE 661 ! JB Clement, 07/07/2025661 ! JB Clement, 13/01/2025 662 662 ! 663 663 ! NOTES … … 703 703 ! 704 704 ! AUTHORS & DATE 705 ! JB Clement, 07/07/2025705 ! JB Clement, 13/01/2025 706 706 ! 707 707 ! NOTES
Note: See TracChangeset
for help on using the changeset viewer.
