Changeset 3883


Ignore:
Timestamp:
Aug 8, 2025, 7:50:27 PM (19 hours ago)
Author:
jbclement
Message:

PEM:

  • Replacing the Bash script "concat_diagpem.sh" by a Python script "concat_pem.py" which is much faster.
  • Corrections and updates for "visu_evol_layering.py".

JBC

Location:
trunk/LMDZ.COMMON/libf/evolution
Files:
1 added
1 deleted
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/LMDZ.COMMON/libf/evolution/changelog.txt

    r3869 r3883  
    766766== 28/07/2025 == JBC
    767767Bug correction to detect the job time limit with PBS/TORQUE. Making it more robust and automatic for the launching script and the Fortran code.
     768
     769== 08/08/2025 == JBC
     770- Replacing the Bash script "concat_diagpem.sh" by a Python script "concat_pem.py" which is much faster.
     771- Corrections and updates for "visu_evol_layering.py".
  • trunk/LMDZ.COMMON/libf/evolution/deftank/README

    r3861 r3883  
    103103  Bash script file to set the orbital parameters of a file "startfi.nc" from Laskar's data contained in "obl_ecc_lsp.asc" according to the initial date 'year_earth_bp_ini' defined in "run_PEM.def". See also "modify_startfi_orbit.sh".
    104104
    105 # concat_diagpem.sh:
    106   Bash script file to concatenate along the variable 'Time' all the "diagpem.nc" files of the PEM into one NetCDF file. 'Time' is re-indexed with the numbering of Martian years simulated by the PEM run.
     105# concat_pem.py:
     106  Python script file to concatenate the NetCDF files of the PEM along the dimension 'Time' into one NetCDF file. 'Time' is re-indexed by increment.
    107107
    108108# clean.sh:
  • trunk/LMDZ.COMMON/libf/evolution/deftank/visu_evol_layering.py

    r3860 r3883  
    509509                output_folder, f"layering_evolution_ig{ig+1}_is{isl+1}.png"
    510510            )
    511             fig.savefig(fname, dpi=150)
     511            fig.savefig(fname, dpi=1200, bbox_inches='tight')
    512512
    513513
     
    577577            rgb = np.ones((nz, ntime, 3), dtype=float)
    578578
    579             frac_all = np.zeros((nz, ntime, 3), dtype=float)  # store fH2O, fCO2, fDust
     579            frac_all = np.full((nz, ntime, 3), np.nan, dtype=float)  # store fH2O, fCO2, fDust
    580580            for t in range(ntime):
    581581                depth = ti[ig, t, isl]
     
    598598            display_frac = frac_all[elevation_mask, :, :]
    599599
    600             display_rgb = rgb[elevation_mask, :, :]
    601 
    602600            # Compute edges for pcolormesh
    603601            dt = date_time[1] - date_time[0] if len(date_time) > 1 else 1
     
    609607            # Create figure with legend
    610608            fig, (ax_main, ax_leg) = plt.subplots(
    611                 1, 2, figsize=(8, 4), dpi=200,
     609                1, 2, figsize=(8, 4), dpi=150,
    612610                gridspec_kw={'width_ratios': [5, 1]}
    613611            )
     
    630628                i = np.searchsorted(x_edges, x) - 1
    631629                j = np.searchsorted(y_edges, y) - 1
    632                 i = np.clip(i, 0, display_rgb.shape[1]-1)
    633                 j = np.clip(j, 0, display_rgb.shape[0]-1)
     630                i = np.clip(i, 0, display_rgb.shape[1] - 1)
     631                j = np.clip(j, 0, display_rgb.shape[0] - 1)
    634632                # get fractions
    635633                fH2O, fCO2, fDust = display_frac[j, i]
    636                 return f"Time={x:.2f}, Elev={y:.2f}, H2O={fH2O:.2f}, CO2={fCO2:.2f}, Dust={fDust:.2f}"
     634                return f"Time={x:.2f}, Elev={y:.2f}, H2O={fH2O:.4f}, CO2={fCO2:.4f}, Dust={fDust:.4f}"
    637635            ax_main.format_coord = main_format
    638636            ax_main.set_facecolor('white')
     
    684682            plt.tight_layout()
    685683            fname = os.path.join(output_folder, f"layering_rgb_evolution_ig{ig+1}_is{isl+1}.png")
    686             fig.savefig(fname, dpi=150, bbox_inches='tight')
     684            fig.savefig(fname, dpi=1200, bbox_inches='tight')
    687685
    688686
     
    722720    vmin, vmax = -2, 1
    723721    epsilon = 1e-6
    724    
     722
    725723    # Loop over grids and slopes
    726724    for ig in range(ngrid):
    727725        for isl in range(nslope):
    728726            ti = top_index[ig, :, isl].copy().astype(int)
    729             for t in range(1, ntime):
    730                 if ti[t] <= 0:
    731                     ti[t] = ti[t-1]
    732727
    733728            # Compute log10(dust/ice) profile at each time step
    734729            log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32)
    735730            for t in range(ntime):
     731                if ti[t] <= 0:
     732                    ti[t] = ti[t-1]
    736733                zmax = ti[t]
    737734                if zmax <= 0:
    738735                    continue
    739736
    740                 h2o_profile = np.clip(h2o[ig, t, isl, :zmax], 0, None)
    741                 dust_profile = np.clip(dust[ig, t, isl, :zmax], 0, None)
     737                cH2O = np.clip(h2o[ig, t, isl, :zmax], 0, None)
     738                cCO2 = np.clip(co2[ig, t, isl, :zmax], 0, None)
     739                cDust = np.clip(dust[ig, t, isl, :zmax], 0, None)
    742740
    743741                with np.errstate(divide='ignore', invalid='ignore'):
    744                     ratio_profile = np.where(
    745                         h2o_profile > 0,
    746                         dust_profile / h2o_profile,
     742                    ratio = np.where(
     743                        cH2O > 0,
     744                        cDust / cH2O,
    747745                        10**(vmax + 1)
    748746                    )
    749                     log_ratio = np.log10(ratio_profile + epsilon)
     747                    log_ratio = np.log10(ratio + epsilon)
    750748                    log_ratio = np.clip(log_ratio, vmin, vmax)
    751749
    752750                log_ratio_array[:zmax, t] = log_ratio
    753751
    754             # Convert back to linear ratio and apply elevation mask
     752            # Mask and prepare for display
    755753            ratio_array = 10**log_ratio_array
    756754            ratio_display = ratio_array[elevation_mask, :]
     755            x_edges = np.concatenate([date_time, [date_time[-1] + (date_time[-1] - date_time[-2])]])
     756            y_edges = np.concatenate([elev, [elev[-1] + (elev[-1] - elev[-2])]])
    757757
    758758            # Plot
     
    766766                norm=LogNorm(vmin=10**vmin, vmax=10**vmax),
    767767            )
    768             x_edges = np.concatenate([date_time, [date_time[-1] + (date_time[-1]-date_time[-2])]])
    769             attach_format_coord(ax, ratio_display, x_edges, np.concatenate([elev, [elev[-1] + (elev[-1]-elev[-2])]]), is_pcolormesh=True)
     768            attach_format_coord(ax, ratio_display, x_edges, y_edges, is_pcolormesh=True)
    770769            ax.set_title(f"Dust-to-Ice ratio over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold')
    771770            ax.set_xlabel('Time (Mars years)')
    772771            ax.set_ylabel('Elevation (m)')
    773772
    774             # Add colorbar with simplified ratio labels
    775             cbar = fig.colorbar(im, ax=ax, orientation='vertical')
     773            # Add colorbar
     774            cbar = fig.colorbar(im, ax=ax, orientation='vertical', pad=0.15)
    776775            cbar.set_label('Dust / H₂O ice (ratio)')
    777 
    778             # Define custom ticks and labels
    779             ticks = [1e-2, 1e-1, 1, 1e1]
    780             labels = ['1:100', '1:10', '1:1', '10:1']
    781             cbar.set_ticks(ticks)
    782             cbar.set_ticklabels(labels)
     776            cbar.set_ticks([1e-2, 1e-1, 1, 1e1])
     777            cbar.set_ticklabels(['1:100', '1:10', '1:1', '10:1'])
    783778
    784779            # Save figure
    785780            plt.tight_layout()
    786             fname = os.path.join(
     781            outname = os.path.join(
    787782                output_folder,
    788783                f"dust_to_ice_ratio_grid{ig+1}_slope{isl+1}.png"
    789784            )
    790             fig.savefig(fname, dpi=150)
     785            fig.savefig(outname, dpi=1200, bbox_inches='tight')
    791786
    792787
     
    10601055        elev = ref_grid
    10611056
    1062     # Custom colormap: blue (ice) to orange (dust)
    1063     blue = np.array([0, 0, 255]) / 255
    1064     orange = np.array([255, 165, 0]) / 255
     1057    # Define custom blue-to-orange colormap
     1058    blue = np.array([0, 0, 255], dtype=float) / 255
     1059    orange = np.array([255, 165, 0], dtype=float) / 255
    10651060    custom_cmap = LinearSegmentedColormap.from_list('BlueOrange', [blue, orange], N=256)
    10661061
     
    10721067    for ig in range(ngrid):
    10731068        for isl in range(nslope):
     1069            # Prepare fraction and ratio arrays
    10741070            ti = top_index[ig, :, isl].copy().astype(int)
    1075             frac_all = np.zeros((nz, ntime, 3), dtype=float)  # store fH2O, fCO2, fDust
    1076             for t in range(1, ntime):
     1071            log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32)
     1072            frac_all = np.full((nz, ntime, 3), np.nan, dtype=float)  # store fH2O, fCO2, fDust
     1073            for t in range(ntime):
    10771074                if ti[t] <= 0:
    10781075                    ti[t] = ti[t-1]
    1079 
    1080             # Compute log10(dust/ice) profile at each time step
    1081             log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32)
    1082             for t in range(ntime):
    10831076                zmax = ti[t]
    10841077                if zmax <= 0:
    10851078                    continue
     1079
    10861080                cH2O = np.clip(h2o[ig, t, isl, :zmax], 0, None)
    10871081                cCO2 = np.clip(co2[ig, t, isl, :zmax], 0, None)
     
    10941088                frac_all[:zmax, t, :] = np.stack([fH2O, fCO2, fDust], axis=1)
    10951089
    1096                 h2o_profile = np.clip(h2o[ig, t, isl, :zmax], 0, None)
    1097                 dust_profile = np.clip(dust[ig, t, isl, :zmax], 0, None)
    1098 
    10991090                with np.errstate(divide='ignore', invalid='ignore'):
    1100                     ratio_profile = np.where(
    1101                         h2o_profile > 0,
    1102                         dust_profile / h2o_profile,
     1091                    ratio = np.where(
     1092                        cH2O > 0,
     1093                        cDust / cH2O,
    11031094                        10**(vmax + 1)
    11041095                    )
    1105                     log_ratio = np.log10(ratio_profile + epsilon)
     1096                    log_ratio = np.log10(ratio + epsilon)
    11061097                    log_ratio = np.clip(log_ratio, vmin, vmax)
    11071098
    11081099                log_ratio_array[:zmax, t] = log_ratio
    11091100
    1110             # Convert back to linear ratio and mask
     1101            # Mask elevation
    11111102            ratio_array = 10**log_ratio_array
    11121103            ratio_display = ratio_array[elevation_mask, :]
    11131104            display_frac = frac_all[elevation_mask, :, :]
    11141105
     1106            # Compute edges for pcolormesh
     1107            dt = date_time[1] - date_time[0] if len(date_time) > 1 else 1
     1108            x_edges = np.concatenate([date_time, [date_time[-1] + dt]])
     1109            d_e = np.diff(elev)
     1110            last_e = elev[-1] + (d_e[-1] if len(d_e)>0 else 1)
     1111            y_edges = np.concatenate([elev, [last_e]])
     1112
    11151113            # Plot
    11161114            fig, ax = plt.subplots(figsize=(8, 6), dpi=150)
    11171115            im = ax.pcolormesh(
    1118                 date_time,
    1119                 elev,
     1116                x_edges,
     1117                y_edges,
    11201118                ratio_display,
    11211119                shading='auto',
     
    11231121                norm=LogNorm(vmin=10**vmin, vmax=10**vmax),
    11241122            )
    1125             x_edges = np.concatenate([date_time, [date_time[-1] + (date_time[-1]-date_time[-2])]])
    1126             y_edges = np.concatenate([elev, [elev[-1] + (elev[-1]-elev[-2])]])
    1127             def format_coord_all(x, y):
     1123
     1124            def format_coord_custom(x_input, y_input):
     1125                # map onto the main axis
     1126                if plt.gca() is ax2:
     1127                    x_pix, y_pix = ax2.transData.transform((x_input, y_input))
     1128                    x, y = ax.transData.inverted().transform((x_pix, y_pix))
     1129                else:
     1130                    x, y = x_input, y_input
    11281131                # check bounds
    11291132                if x < x_edges[0] or x > x_edges[-1] or y < y_edges[0] or y > y_edges[-1]:
     
    11321135                i = np.searchsorted(x_edges, x) - 1
    11331136                j = np.searchsorted(y_edges, y) - 1
    1134                 i = np.clip(i, 0, display_frac.shape[1]-1)
    1135                 j = np.clip(j, 0, display_frac.shape[0]-1)
    1136                 # get fractions
    1137                 fH2O  = display_frac[j, i, 0]
    1138                 fDust = display_frac[j, i, 2]
     1137                i = np.clip(i, 0, ratio_display.shape[1] - 1)
     1138                j = np.clip(j, 0, ratio_display.shape[0] - 1)
     1139                # get fractions and obliquity
     1140                fH2O, fCO2, fDust = display_frac[j, i]
    11391141                obl   = np.interp(x, date_time, obliquity)
    1140                 return f"Time={x:.2f}, Elev={y:.2f}, H2O={fH2O:.2f}, Dust={fDust:.2f}, Obl={obl:.2f}°"
    1141 
    1142             ax.format_coord = format_coord_all
     1142                return f"Time={x:.2f}, Elev={y:.2f}, H2O={fH2O:.4f}, Dust={fDust:.4f}, Obl={obl:.2f}°"
     1143
    11431144            ax.set_title(f"Dust-to-Ice ratio over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold')
    11441145            ax.set_xlabel('Time (Mars years)')
     
    11611162                    linewidth=1.5
    11621163                )
    1163             ax2.format_coord = format_coord_all
     1164            ax2.format_coord = format_coord_custom
    11641165            ax2.set_ylabel('Obliquity (°)')
    11651166            ax2.tick_params(axis='y')
    11661167            ax2.grid(False)
    11671168
    1168             # Save
    1169             os.makedirs(output_folder, exist_ok=True)
     1169            # Save figure
     1170            plt.tight_layout()
    11701171            outname = os.path.join(
    11711172                output_folder,
    11721173                f'dust_ice_obliquity_grid{ig+1}_slope{isl+1}.png'
    11731174            )
    1174             plt.tight_layout()
    1175             fig.savefig(outname, dpi=150)
     1175            fig.savefig(outname, dpi=1200, bbox_inches='tight')
    11761176
    11771177
Note: See TracChangeset for help on using the changeset viewer.