- Timestamp:
- Apr 20, 2026, 3:58:44 PM (4 weeks ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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
Note: See TracChangeset
for help on using the changeset viewer.
