Changeset 3850
- Timestamp:
- Jul 15, 2025, 2:43:41 PM (21 hours ago)
- Location:
- trunk/LMDZ.COMMON/libf/evolution
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LMDZ.COMMON/libf/evolution/changelog.txt
r3842 r3850 733 733 - Correction of the incorporation of the sub-surface sublimation tendency into the overall tendency to be more robust. 734 734 - Revision of the layering initialization in the case where there is no "startpem.nc" file. 735 736 == 15/07/2025 == JBC 737 - Handling the case where the new date is positive (present-day climate) in "recomp_orb_param_mod.F90", especially because of rounding errors. 738 - Correction in "visu_evol_layering.py" to get the proper extent of the stratication + replacing 'imshow' by 'pcolormesh' to better process the data. 739 - Addition of a graphic to plot the changes in obliquity directly onto the evolution of the stratification over time for better analysis. Obliquity curve is colored according to stratification gain/loss. 740 - Correction of numbering in "lib_launchPEM.sh" when requesting a relaunch. 741 - Update of "PEMrun.job" due to new parsing of arguments introduced in r3836/r3837. -
trunk/LMDZ.COMMON/libf/evolution/deftank/PEMrun.job
r3667 r3850 26 26 27 27 # Argument for the PEM execution (for SLURM: "$SLURM_JOB_ID" | for PBD/TORQUE: "$PBS_JOBID" | "" when the script is not run as a job): 28 arg_pem=" $SLURM_JOB_ID"28 arg_pem="--jobid $SLURM_JOB_ID" 29 29 ######################################################################## 30 30 -
trunk/LMDZ.COMMON/libf/evolution/deftank/lib_launchPEM.sh
r3840 r3850 172 172 fi 173 173 if [ $(echo "$i_myear < $n_myear" | bc -l) -eq 1 ]; then 174 echo "Run \"PCM $iPCM\" ($ii/$3) ..."174 echo "Run \"PCM $iPCM\" ($ii/$3)" 175 175 if [ $1 -eq 0 ]; then # Mode: processing scripts 176 176 sed -i "s/^k=-\?[0-9]\+$/k=$(echo "$ii - $3 + 2" | bc)/" PCMrun.job … … 199 199 for ((i = $ii; i <= $3; i++)); do 200 200 if [ $(echo "$i_myear < $n_myear" | bc -l) -eq 1 ]; then 201 echo "Run \"PCM $iPCM\" ($i/$3) ..."201 echo "Run \"PCM $iPCM\" ($i/$3)" 202 202 if [ $1 -eq 0 ]; then # Mode: processing scripts 203 203 sed -i "s/^k=-\?[0-9]\+$/k=$(echo "$i - $3 + 2" | bc)/" PCMrun.job … … 366 366 if [ $il -eq $(($nPCM - 1)) ]; then # Second to last PCM run 367 367 cp diags/data2reshape${irelaunch}.nc data2reshape_Y1.nc 368 cyclelaunch $1 $2 $nPCM $ il368 cyclelaunch $1 $2 $nPCM $(($il + 1)) 369 369 elif [ $il -eq $nPCM ]; then # Last PCM run so the next job is a PEM run 370 370 cp diags/data2reshape$(($irelaunch - 1)).nc data2reshape_Y1.nc … … 372 372 submitPEM $1 373 373 else 374 cyclelaunch $1 $2 $nPCM $ il374 cyclelaunch $1 $2 $nPCM $(($il + 1)) 375 375 fi 376 376 fi -
trunk/LMDZ.COMMON/libf/evolution/deftank/visu_evol_layering.py
r3840 r3850 525 525 Includes a triangular legend showing the mix proportions. 526 526 """ 527 528 527 # Define constant colors 529 violet = np.array([255, 0, 255], dtype=float) / 255 530 blue = np.array([0, 0, 255], dtype=float) / 255 531 orange = np.array([255, 165, 0], dtype=float) / 255 532 533 # Prepare elevation mask 534 mask_elev = (ref_grid >= 0.0) if exclude_sub else np.ones_like(ref_grid, dtype=bool) 535 elev = ref_grid[mask_elev] 536 537 # Generate legend image once 528 violet = np.array([255, 0, 255], dtype=float) / 255 529 blue = np.array([ 0, 0, 255], dtype=float) / 255 530 orange = np.array([255, 165, 0], dtype=float) / 255 531 532 # Elevation mask and array 533 if exclude_sub: 534 elevation_mask = (ref_grid >= 0.0) 535 elev = ref_grid[elevation_mask] 536 else: 537 elevation_mask = np.ones_like(ref_grid, dtype=bool) 538 elev = ref_grid 539 540 # Pre-compute legend triangle 538 541 res = 300 539 542 u = np.linspace(0, 1, res) … … 544 547 W_bary = 1 - U_bary - V_bary 545 548 mask_triangle = (U_bary >= 0) & (V_bary >= 0) & (W_bary >= 0) 546 547 549 legend_rgb = ( 548 550 U_bary[..., None] * violet … … 555 557 legend_rgba[..., 3] = mask_triangle.astype(float) 556 558 557 # Loop over grid and slope559 # Extract data arrays 558 560 h2o = gridded_data['h2o_ice'] 559 561 co2 = gridded_data['co2_ice'] … … 561 563 ngrid, ntime, nslope, nz = h2o.shape 562 564 565 # Fill missing depths 566 ti = top_index.copy().astype(int) 567 for ig in range(ngrid): 568 for isl in range(nslope): 569 for t in range(1, ntime): 570 if ti[ig, t, isl] <= 0: 571 ti[ig, t, isl] = ti[ig, t-1, isl] 572 573 # Loop over grid and slope 563 574 for ig in range(ngrid): 564 575 for isl in range(nslope): 565 576 # Compute RGB stratification over time 566 577 rgb = np.ones((nz, ntime, 3), dtype=float) 578 579 frac_all = np.zeros((nz, ntime, 3), dtype=float) # store fH2O, fCO2, fDust 567 580 for t in range(ntime): 568 mask_z = np.arange(nz) < top_index[ig, t, isl]569 if not mask_z.any():581 depth = ti[ig, t, isl] 582 if depth <= 0: 570 583 continue 571 cH2O = np.clip(h2o[ig, t, isl, mask_z], 0, None)572 cCO2 = np.clip(co2[ig, t, isl, mask_z], 0, None)573 cDust = np.clip(dust[ig, t, isl, mask_z], 0, None)584 cH2O = np.clip(h2o[ig, t, isl, :depth], 0, None) 585 cCO2 = np.clip(co2[ig, t, isl, :depth], 0, None) 586 cDust = np.clip(dust[ig, t, isl, :depth], 0, None) 574 587 total = cH2O + cCO2 + cDust 575 588 total[total == 0] = 1.0 … … 577 590 fCO2 = cCO2 / total 578 591 fDust = cDust / total 579 mix = ( 580 np.outer(fH2O, blue) 581 + np.outer(fCO2, violet) 582 + np.outer(fDust, orange) 583 ) 584 mix = np.clip(mix, 0.0, 1.0) 585 rgb[mask_z, t, :] = mix 586 587 display_rgb = rgb[mask_elev, :, :] 592 frac_all[:depth, t, :] = np.stack([fH2O, fCO2, fDust], axis=1) 593 mix = np.outer(fH2O, blue) + np.outer(fCO2, violet) + np.outer(fDust, orange) 594 rgb[:depth, t, :] = np.clip(mix, 0, 1) 595 596 # Mask elevation 597 display_rgb = rgb[elevation_mask, :, :] 598 display_frac = frac_all[elevation_mask, :, :] 599 600 display_rgb = rgb[elevation_mask, :, :] 601 602 # Compute edges for pcolormesh 603 dt = date_time[1] - date_time[0] if len(date_time) > 1 else 1 604 x_edges = np.concatenate([date_time, [date_time[-1] + dt]]) 605 d_e = np.diff(elev) 606 last_e = elev[-1] + (d_e[-1] if len(d_e)>0 else 1) 607 y_edges = np.concatenate([elev, [last_e]]) 588 608 589 609 # Create figure with legend 590 610 fig, (ax_main, ax_leg) = plt.subplots( 591 1, 2, figsize=( 12, 5), dpi=200,611 1, 2, figsize=(8, 4), dpi=200, 592 612 gridspec_kw={'width_ratios': [5, 1]} 593 613 ) 594 614 595 615 # Main stratification panel 596 ax_main.imshow( 616 mesh = ax_main.pcolormesh( 617 x_edges, 618 y_edges, 597 619 display_rgb, 598 aspect='auto', 599 extent=[date_time[0], date_time[-1], elev.min(), elev.max()], 600 interpolation='nearest', 601 origin='lower' 620 shading='auto', 621 edgecolors='none' 602 622 ) 603 x_centers = np.linspace(date_time[0], date_time[-1], display_rgb.shape[1]) 604 y_centers = np.linspace(elev.min(), elev.max(), display_rgb.shape[0]) 605 attach_format_coord(ax_main, display_rgb, x_centers, y_centers, is_pcolormesh=False) 623 624 # Custom coordinate formatter: show time, elevation, and mixture fractions 625 def main_format(x, y): 626 # check bounds 627 if x < x_edges[0] or x > x_edges[-1] or y < y_edges[0] or y > y_edges[-1]: 628 return '' 629 # locate cell 630 i = np.searchsorted(x_edges, x) - 1 631 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) 634 # get fractions 635 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}" 637 ax_main.format_coord = main_format 606 638 ax_main.set_facecolor('white') 607 639 ax_main.set_title(f"Ternary mix over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold') … … 609 641 ax_main.set_ylabel("Elevation (m)") 610 642 611 # Legend panel 612 ax_leg.imshow( 643 # Legend panel using proper edges 644 u_edges = np.linspace(0, 1, res+1) 645 v_edges = np.linspace(0, np.sqrt(3)/2, res+1) 646 ax_leg.pcolormesh( 647 u_edges, 648 v_edges, 613 649 legend_rgba, 614 extent=[0, 1, 0, np.sqrt(3)/2], 615 origin='lower', 616 interpolation='nearest' 650 shading='auto', 651 edgecolors='none' 617 652 ) 618 attach_format_coord(ax_leg, legend_rgba, np.array([0, 1]), np.array([0, np.sqrt(3)/2]), is_pcolormesh=False) 619 620 # Draw triangle border 653 ax_leg.set_aspect('equal') 654 655 # Custom coordinate formatter for legend: show barycentric fractions 656 def legend_format(x, y): 657 # compute barycentric coords from cartesian (x,y) 658 V = 2 * y / np.sqrt(3) 659 U = x - 0.5 * V 660 W = 1 - U - V 661 if U >= 0 and V >= 0 and W >= 0: 662 return f"H2O: {W:.2f}, Dust: {V:.2f}, CO2: {U:.2f}" 663 else: 664 return '' 665 ax_leg.format_coord = legend_format 666 667 # Draw triangle border and gridlines 621 668 triangle = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)/2], [0, 0]]) 622 ax_leg.plot(triangle[:, 0], triangle[:, 1], 'k-', linewidth=1) 623 624 # Dashed gridlines 669 ax_leg.plot(triangle[:, 0], triangle[:, 1], 'k-', linewidth=1, clip_on=False, zorder=10) 625 670 ticks = np.linspace(0.25, 0.75, 3) 626 671 for f in ticks: 627 ax_leg.plot([1 - f, 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5 )628 ax_leg.plot([f, f + 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5 )672 ax_leg.plot([1 - f, 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5, clip_on=False, zorder=9) 673 ax_leg.plot([f, f + 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5, clip_on=False, zorder=9) 629 674 y = (np.sqrt(3)/2) * f 630 ax_leg.plot([0.5 * f, 1 - 0.5 * f], [y, y], '--', color='k', linewidth=0.5 )675 ax_leg.plot([0.5 * f, 1 - 0.5 * f], [y, y], '--', color='k', linewidth=0.5, clip_on=False, zorder=9) 631 676 632 677 # Legend labels … … 636 681 ax_leg.axis('off') 637 682 683 # Save figure 638 684 plt.tight_layout() 639 640 # Save figure641 685 fname = os.path.join(output_folder, f"layering_rgb_evolution_ig{ig+1}_is{isl+1}.png") 642 686 fig.savefig(fname, dpi=150, bbox_inches='tight') … … 678 722 vmin, vmax = -2, 1 679 723 epsilon = 1e-6 680 724 681 725 # Loop over grids and slopes 682 726 for ig in range(ngrid): 683 727 for isl in range(nslope): 728 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] 732 733 # Compute log10(dust/ice) profile at each time step 684 734 log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32) 685 686 # Compute log10(dust/ice) profile at each time step687 735 for t in range(ntime): 688 zmax = t op_index[ig, t, isl]736 zmax = ti[t] 689 737 if zmax <= 0: 690 738 continue … … 710 758 # Plot 711 759 fig, ax = plt.subplots(figsize=(8, 6), dpi=150) 760 im = ax.pcolormesh( 761 date_time, 762 elev, 763 ratio_display, 764 shading='auto', 765 cmap='managua_r', 766 norm=LogNorm(vmin=10**vmin, vmax=10**vmax), 767 ) 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) 712 770 ax.set_title(f"Dust-to-Ice ratio over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold') 713 im = ax.imshow( 714 ratio_display, 715 aspect='auto', 716 extent=[date_time[0], date_time[-1], elev.min(), elev.max()], 717 origin='lower', 718 interpolation='nearest', 719 cmap='managua_r', 720 norm=LogNorm(vmin=10**vmin, vmax=10**vmax) 721 ) 722 x_centers = np.linspace(date_time[0], date_time[-1], ratio_display.shape[1]) 723 y_centers = np.linspace(elev.min(), elev.max(), ratio_display.shape[0]) 724 attach_format_coord(ax, ratio_display, x_centers, y_centers, is_pcolormesh=False) 771 ax.set_xlabel('Time (Mars years)') 772 ax.set_ylabel('Elevation (m)') 725 773 726 774 # Add colorbar with simplified ratio labels … … 827 875 # Plot 828 876 fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True) 829 fig.suptitle("Orbital Parameters vs Simulated Time", fontsize=14, fontweight='bold')830 831 axes[0].plot(date_time, obl_interp, 'r +', linestyle='-')877 fig.suptitle("Orbital parameters vs simulated time", fontsize=14, fontweight='bold') 878 879 axes[0].plot(date_time, obl_interp, 'r-', marker='+') 832 880 axes[0].set_ylabel("Obliquity (°)") 833 881 axes[0].grid(True) 834 882 835 axes[1].plot(date_time, ecc_interp, 'b +', linestyle='-')883 axes[1].plot(date_time, ecc_interp, 'b-', marker='+') 836 884 axes[1].set_ylabel("Eccentricity") 837 885 axes[1].grid(True) 838 886 839 axes[2].plot(date_time, lsp_interp, 'g +', linestyle='-')840 axes[2].set_ylabel("Ls p(°)")887 axes[2].plot(date_time, lsp_interp, 'g-', marker='+') 888 axes[2].set_ylabel("Ls of perihelion (°)") 841 889 axes[2].set_xlabel("Time (Mars years)") 842 890 axes[2].grid(True) 843 891 844 892 plt.tight_layout(rect=[0, 0, 1, 0.96]) 845 fname = os.path.join(output_folder, "orbital_parameters .png")893 fname = os.path.join(output_folder, "orbital_parameters_laskar.png") 846 894 fig.savefig(fname, dpi=150) 895 896 897 def mars_ls(pday, peri_day, e_elips, year_day, lsperi=0.0): 898 """ 899 Compute solar longitude (Ls) in radians for a given Mars date array 'pday'. 900 Returns Ls in degrees [0, 360). 901 """ 902 zz = (pday - peri_day) / year_day 903 zanom = 2 * np.pi * (zz - np.round(zz)) 904 xref = np.abs(zanom) 905 906 # Solve Kepler's equation via Newton–Raphson 907 zx0 = xref + e_elips * np.sin(xref) 908 for _ in range(10): 909 f = zx0 - e_elips * np.sin(zx0) - xref 910 fp = 1 - e_elips * np.cos(zx0) 911 dz = -f / fp 912 zx0 += dz 913 if np.all(np.abs(dz) <= 1e-7): 914 break 915 916 zx0 = np.where(zanom < 0, -zx0, zx0) 917 zteta = 2 * np.arctan( 918 np.sqrt((1 + e_elips) / (1 - e_elips)) * np.tan(zx0 / 2) 919 ) 920 psollong = np.mod(zteta + lsperi, 2 * np.pi) 921 922 return np.degrees(psollong) 923 924 925 def read_orbital_data_nc(starts_folder, infofile=None): 926 """ 927 Read orbital parameters from restartfi_postPEM*.nc files in starts_folder. 928 """ 929 if not os.path.isdir(starts_folder): 930 raise ValueError(f"Invalid starts_folder '{starts_folder}': not a directory.") 931 932 # Read simulation time mapping if provided 933 if infofile: 934 dates_yr, martian_to_earth = read_infofile(infofile) 935 else: 936 dates_yr = None 937 938 pattern = os.path.join(starts_folder, "restartfi_postPEM*.nc") 939 files = glob(pattern) 940 if not files: 941 raise FileNotFoundError(f"No NetCDF restart files found matching {pattern}") 942 943 def extract_number(path): 944 name = os.path.basename(path) 945 prefix = 'restartfi_postPEM' 946 if name.startswith(prefix) and name.endswith('.nc'): 947 num_str = name[len(prefix):-3] 948 if num_str.isdigit(): 949 return int(num_str) 950 return float('inf') 951 952 files = sorted(files, key=extract_number) 953 954 all_year_day, all_peri, all_aphe, all_date_peri, all_obl = [], [], [], [], [] 955 for nc_path in files: 956 with Dataset(nc_path, 'r') as nc: 957 ctrl = nc.variables['controle'][:] 958 all_year_day.append(ctrl[13]) 959 all_peri.append(ctrl[14]) 960 all_aphe.append(ctrl[15]) 961 all_date_peri.append(ctrl[16]) 962 all_obl.append(ctrl[17]) 963 964 year_day = np.array(all_year_day) 965 perihelion = np.array(all_peri) 966 aphelion = np.array(all_aphe) 967 date_peri_day = np.array(all_date_peri) 968 obliquity = np.array(all_obl) 969 970 eccentricity = (aphelion - perihelion) / (aphelion + perihelion) 971 ls_perihelion = mars_ls( 972 date_peri_day, 973 date_peri_day, 974 eccentricity, 975 year_day 976 ) 977 978 return dates_yr, obliquity, eccentricity, ls_perihelion 979 980 981 def plot_orbital_parameters_nc(starts_folder, infofile, date_time, output_folder="."): 982 """ 983 Plot the evolution of obliquity, eccentricity and Ls p coming from simulation data 984 versus simulated time. 985 """ 986 # Read orbital data 987 times_yr, obl, ecc, lsp = read_orbital_data_nc(starts_folder, infofile) 988 989 fargs = dict(kind='linear', bounds_error=False, fill_value='extrapolate') 990 obl_i = interp1d(times_yr, obl, **fargs)(date_time) 991 ecc_i = interp1d(times_yr, ecc, **fargs)(date_time) 992 lsp_i = interp1d(times_yr, lsp, **fargs)(date_time) 993 994 fig, axes = plt.subplots(3,1, figsize=(8,10), sharex=True) 995 fig.suptitle("Orbital parameters vs simulated time", fontsize=14, fontweight='bold') 996 997 # Plot 998 axes[0].plot(date_time, obl_i, 'r-', marker='+') 999 axes[0].set_ylabel("Obliquity (°)") 1000 axes[0].grid(True) 1001 1002 axes[1].plot(date_time, ecc_i, 'b-', marker='+') 1003 axes[1].set_ylabel("Eccentricity") 1004 axes[1].grid(True) 1005 1006 axes[2].plot(date_time, lsp_i, 'g-', marker='+') 1007 axes[2].set_ylabel("Ls of perihelion (°)") 1008 axes[2].set_xlabel("Time (Mars years)") 1009 axes[2].grid(True) 1010 1011 plt.tight_layout(rect=[0,0,1,0.96]) 1012 outname = os.path.join(output_folder, "orbital_parameters_simu.png") 1013 fig.savefig(outname, dpi=150) 1014 1015 1016 def plot_dust_to_ice_ratio_with_obliquity( 1017 starts_folder, 1018 infofile, 1019 gridded_data, 1020 ref_grid, 1021 top_index, 1022 heights_data, 1023 date_time, 1024 exclude_sub=False, 1025 output_folder="." 1026 ): 1027 """ 1028 Plot the dust-to-ice ratio over time as a heatmap, and overlay the evolution of 1029 obliquity on a secondary y-axis. 1030 """ 1031 h2o = gridded_data['h2o_ice'] 1032 dust = gridded_data['dust'] 1033 ngrid, ntime, nslope, nz = h2o.shape 1034 1035 # Read orbital data 1036 times_yr, obl, _, _ = read_orbital_data_nc(starts_folder, infofile) 1037 fargs = dict(kind='linear', bounds_error=False, fill_value='extrapolate') 1038 obliquity = interp1d(times_yr, obl, **fargs)(date_time) 1039 1040 # Computed total height 1041 for ig in range(ngrid): 1042 for isl in range(nslope): 1043 total_height_t = np.zeros(ntime, dtype=float) 1044 1045 for t_idx in range(ntime): 1046 h_mat = heights_data[t_idx][isl] 1047 raw_h = h_mat[ig, :] 1048 valid_mask = (~np.isnan(raw_h)) & (raw_h != 0.0) 1049 if np.any(valid_mask): 1050 h_valid = raw_h[valid_mask] 1051 total_height_t[t_idx] = np.max(h_valid) 1052 1053 # Compute the per-interval sign of height change 1054 dh = np.diff(total_height_t) 1055 signs = np.sign(dh) 1056 color_map = { 1: 'green', -1: 'red', 0: 'orange' } 1057 1058 # Elevation mask 1059 if exclude_sub: 1060 elevation_mask = (ref_grid >= 0.0) 1061 elev = ref_grid[elevation_mask] 1062 else: 1063 elevation_mask = np.ones_like(ref_grid, dtype=bool) 1064 elev = ref_grid 1065 1066 # Custom colormap: blue (ice) to orange (dust) 1067 blue = np.array([0, 0, 255]) / 255 1068 orange = np.array([255, 165, 0]) / 255 1069 custom_cmap = LinearSegmentedColormap.from_list('BlueOrange', [blue, orange], N=256) 1070 1071 # Log‑ratio bounds and small epsilon to avoid log(0) 1072 vmin, vmax = -2, 1 1073 epsilon = 1e-6 1074 1075 # Loop over grids and slopes 1076 for ig in range(ngrid): 1077 for isl in range(nslope): 1078 ti = top_index[ig, :, isl].copy().astype(int) 1079 for t in range(1, ntime): 1080 if ti[t] <= 0: 1081 ti[t] = ti[t-1] 1082 1083 # Compute log10(dust/ice) profile at each time step 1084 log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32) 1085 for t in range(ntime): 1086 zmax = ti[t] 1087 if zmax <= 0: 1088 continue 1089 1090 h2o_profile = np.clip(h2o[ig, t, isl, :zmax], 0, None) 1091 dust_profile = np.clip(dust[ig, t, isl, :zmax], 0, None) 1092 1093 with np.errstate(divide='ignore', invalid='ignore'): 1094 ratio_profile = np.where( 1095 h2o_profile > 0, 1096 dust_profile / h2o_profile, 1097 10**(vmax + 1) 1098 ) 1099 log_ratio = np.log10(ratio_profile + epsilon) 1100 log_ratio = np.clip(log_ratio, vmin, vmax) 1101 1102 log_ratio_array[:zmax, t] = log_ratio 1103 1104 # Convert back to linear ratio and mask 1105 ratio_array = 10**log_ratio_array 1106 ratio_display = ratio_array[elevation_mask, :] 1107 1108 # Plot 1109 fig, ax = plt.subplots(figsize=(8, 6), dpi=150) 1110 im = ax.pcolormesh( 1111 date_time, 1112 elev, 1113 ratio_display, 1114 shading='auto', 1115 cmap='managua_r', 1116 norm=LogNorm(vmin=10**vmin, vmax=10**vmax), 1117 ) 1118 x_edges = np.concatenate([date_time, [date_time[-1] + (date_time[-1]-date_time[-2])]]) 1119 attach_format_coord(ax, ratio_display, x_edges, np.concatenate([elev, [elev[-1] + (elev[-1]-elev[-2])]]), is_pcolormesh=True) 1120 ax.set_title(f"Dust-to-Ice ratio over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold') 1121 ax.set_xlabel('Time (Mars years)') 1122 ax.set_ylabel('Elevation (m)') 1123 1124 # Add colorbar 1125 cbar = fig.colorbar(im, ax=ax, orientation='vertical', pad=0.15) 1126 cbar.set_label('Dust / H₂O ice (ratio)') 1127 cbar.set_ticks([1e-2, 1e-1, 1, 1e1]) 1128 cbar.set_ticklabels(['1:100', '1:10', '1:1', '10:1']) 1129 1130 # Overlay obliquity on secondary y-axis 1131 ax2 = ax.twinx() 1132 for i in range(len(dh)): 1133 ax2.plot( 1134 [date_time[i], date_time[i+1]], 1135 [obliquity[i], obliquity[i+1]], 1136 color=color_map[signs[i]], 1137 marker='+', 1138 linewidth=1.5 1139 ) 1140 ax2.set_ylabel('Obliquity (°)') 1141 ax2.tick_params(axis='y') 1142 ax2.grid(False) 1143 1144 # Save 1145 os.makedirs(output_folder, exist_ok=True) 1146 outname = os.path.join( 1147 output_folder, 1148 f'dust_ice_obliquity_grid{ig+1}_slope{isl+1}.png' 1149 ) 1150 plt.tight_layout() 1151 fig.savefig(outname, dpi=150) 847 1152 848 1153 … … 913 1218 exclude_sub=exclude_sub, output_folder="." 914 1219 ) 915 plot_dust_to_ice_ratio_over_time( 1220 #plot_dust_to_ice_ratio_over_time( 1221 # gridded_data, ref_grid, top_index, heights_data, date_time, 1222 # exclude_sub=exclude_sub, output_folder="." 1223 #) 1224 plot_dust_to_ice_ratio_with_obliquity( 1225 folder_path, infofile, 916 1226 gridded_data, ref_grid, top_index, heights_data, date_time, 917 1227 exclude_sub=exclude_sub, output_folder="." 918 1228 ) 919 plot_strata_count_and_total_height(heights_data, date_time, output_folder=".")1229 #plot_strata_count_and_total_height(heights_data, date_time, output_folder=".") 920 1230 921 1231 # 14) Plot orbital parameters 922 plot_orbital_parameters(infofile, orbfile, date_time, output_folder=".") 1232 #plot_orbital_parameters(infofile, orbfile, date_time, output_folder=".") 1233 plot_orbital_parameters_nc(folder_path, infofile, date_time, output_folder=".") 923 1234 924 1235 # 15) Show all figures -
trunk/LMDZ.COMMON/libf/evolution/pem.F90
r3842 r3850 69 69 use writediagpem_mod, only: writediagpem, writediagsoilpem 70 70 use co2condens_mod, only: CO2cond_ps 71 use layering_mod, only: layering, del_layering, make_layering, layering_algo, subsurface_ice_layering, 72 ptrarray, stratum, get_nb_str_max, nb_str_max, is_dust_lag, is_co2ice_str, is_h2oice_str , print_layering71 use layering_mod, only: layering, del_layering, make_layering, layering_algo, subsurface_ice_layering, & 72 ptrarray, stratum, get_nb_str_max, nb_str_max, is_dust_lag, is_co2ice_str, is_h2oice_str 73 73 use dyn_ss_ice_m_mod, only: dyn_ss_ice_m 74 74 use parse_args_mod, only: parse_args -
trunk/LMDZ.COMMON/libf/evolution/pemetat0.F90
r3842 r3850 393 393 do islope = 1,nslope 394 394 call ini_layering(layerings_map(ig,islope)) 395 print*, 'coucou', 1.05*ini_huge_h2oice/rho_h2oice, layerings_map(ig,islope)%top%top_elevation396 395 call add_stratum(layerings_map(ig,islope),1.05*ini_huge_h2oice/rho_h2oice,0.,ini_huge_h2oice/rho_h2oice,0.05*ini_huge_h2oice/rho_h2oice,0.,0.) 397 396 enddo -
trunk/LMDZ.COMMON/libf/evolution/recomp_orb_param_mod.F90
r3742 r3850 31 31 ! Input Variables 32 32 !-------------------------------------------------------- 33 real, intent(in) :: i_myear ! Number of simulated Martian years33 real, intent(in) :: i_myear ! Number of simulated Martian years 34 34 real, intent(in) :: i_myear_leg ! Number of iterations of the PEM 35 35 … … 62 62 Lsp = lsperi*180./pi ! We convert in degree 63 63 64 print*, year_bp_ini,i_myear,i_myear_leg 65 64 66 write(*,*) 'recomp_orb_param, Old year =', Year - i_myear_leg 65 67 write(*,*) 'recomp_orb_param, Old obl. =', obliquit … … 69 71 70 72 found_year = .false. 71 do ilask = last_ilask,2,-1 72 xa = yearlask(ilask) 73 xb = yearlask(ilask - 1) 74 if (xa <= Year .and. Year < xb) then 75 ! Obliquity 76 if (var_obl) then 77 ya = obllask(ilask) 78 yb = obllask(ilask - 1) 79 obliquit = (Year - xa)*(yb - ya)/(xb - xa) + ya 73 if (Year < 0.) then 74 do ilask = last_ilask,2,-1 75 xa = yearlask(ilask) 76 xb = yearlask(ilask - 1) 77 if (xa <= Year .and. Year < xb) then 78 ! Obliquity 79 if (var_obl) then 80 ya = obllask(ilask) 81 yb = obllask(ilask - 1) 82 obliquit = (Year - xa)*(yb - ya)/(xb - xa) + ya 83 endif 84 85 ! Eccentricity 86 if (var_ecc) then 87 ya = ecclask(ilask) 88 yb = ecclask(ilask - 1) 89 e_elips = (Year - xa)*(yb - ya)/(xb - xa) + ya 90 endif 91 92 ! Lsp 93 if (var_lsp) then 94 ya = lsplask(ilask) 95 yb = lsplask(ilask - 1) 96 if (abs(yb - ya) > 300.) then ! If modulo is "crossed" through the interval 97 if (ya < yb) then ! Lsp should be decreasing 98 ya = ya + 360. 99 else ! Lsp should be increasing 100 yb = yb + 360. 101 endif 102 endif 103 Lsp = modulo((Year - xa)*(yb - ya)/(xb - xa) + ya,360.) 104 endif 105 found_year = .true. 106 exit ! The loop is left as soon as the right interval is found 80 107 endif 81 82 ! Eccentricity 83 if (var_ecc) then 84 ya = ecclask(ilask) 85 yb = ecclask(ilask - 1) 86 e_elips = (Year - xa)*(yb - ya)/(xb - xa) + ya 87 endif 88 89 ! Lsp 90 if (var_lsp) then 91 ya = lsplask(ilask) 92 yb = lsplask(ilask - 1) 93 if (abs(yb - ya) > 300.) then ! If modulo is "crossed" through the interval 94 if (ya < yb) then ! Lsp should be decreasing 95 ya = ya + 360. 96 else ! Lsp should be increasing 97 yb = yb + 360. 98 endif 99 endif 100 Lsp = modulo((Year - xa)*(yb - ya)/(xb - xa) + ya,360.) 101 endif 102 found_year = .true. 103 exit ! The loop is left as soon as the right interval is found 104 endif 105 enddo 108 enddo 109 else if (Year >= 0 .and. Year < 100.) then ! For present orbital characteristics 110 if (var_obl) obliquit = 25.19 111 if (var_ecc) e_elips = 0.0934 112 if (var_lsp) Lsp = 251. 113 found_year = .true. 114 endif 106 115 107 116 if (.not. found_year) error stop 'The new year could not be found in the file "obl_ecc_lsp.asc".' 117 118 write(*,*) 'recomp_orb_param, New obl. =', obliquit 119 write(*,*) 'recomp_orb_param, New ecc. =', e_elips 120 write(*,*) 'recomp_orb_param, New Lsp =', Lsp 108 121 109 122 halfaxe = 227.94 … … 118 131 #endif 119 132 120 write(*,*) 'recomp_orb_param, New obl. =', obliquit121 write(*,*) 'recomp_orb_param, New ecc. =', e_elips122 write(*,*) 'recomp_orb_param, New Lsp =', Lsp123 124 133 END SUBROUTINE recomp_orb_param 125 134
Note: See TracChangeset
for help on using the changeset viewer.