- Timestamp:
- Jul 1, 2025, 11:39:58 AM (5 days ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LMDZ.COMMON/libf/evolution/deftank/visu_evol_layering.py
r3809 r3819 11 11 from netCDF4 import Dataset 12 12 import matplotlib.pyplot as plt 13 from mpl_toolkits.axes_grid1.inset_locator import inset_axes 14 from matplotlib.colors import LinearSegmentedColormap, LogNorm 13 15 from scipy.interpolate import interp1d 14 16 … … 414 416 fig.suptitle( 415 417 f"Content variation over time for (Grid point {ig+1}, Slope {isl+1})", 416 fontsize=14 418 fontsize=14, 419 fontweight='bold' 417 420 ) 418 421 … … 456 459 457 460 fig.subplots_adjust(right=0.88) 461 fig.tight_layout(rect=[0, 0, 0.88, 1.0]) 458 462 cbar_ax = fig.add_axes([0.90, 0.15, 0.02, 0.7]) 459 463 fig.colorbar(im, cax=cbar_ax, orientation='vertical', label="Content") 460 fig.tight_layout(rect=[0, 0, 0.88, 1.0])461 464 462 465 fname = os.path.join( 463 466 output_folder, f"layering_evolution_ig{ig+1}_is{isl+1}.png" 467 ) 468 fig.savefig(fname, dpi=150) 469 470 471 def plot_stratification_rgb_over_time( 472 gridded_data, 473 ref_grid, 474 top_index, 475 heights_data, 476 date_time, 477 exclude_sub=False, 478 output_folder="." 479 ): 480 """ 481 Plot stratification over time colored using RGB ternary mix of H2O ice (blue), CO2 ice (violet), and dust (orange). 482 Includes a triangular legend showing the mix proportions. 483 """ 484 485 # Define constant colors 486 violet = np.array([255, 0, 255], dtype=float) / 255 487 blue = np.array([0, 0, 255], dtype=float) / 255 488 orange = np.array([255, 165, 0], dtype=float) / 255 489 490 # Prepare elevation mask 491 mask_elev = (ref_grid >= 0.0) if exclude_sub else np.ones_like(ref_grid, dtype=bool) 492 elev = ref_grid[mask_elev] 493 494 # Generate legend image once 495 res = 300 496 u = np.linspace(0, 1, res) 497 v = np.linspace(0, np.sqrt(3)/2, res) 498 X, Y = np.meshgrid(u, v) 499 V_bary = 2 * Y / np.sqrt(3) 500 U_bary = X - 0.5 * V_bary 501 W_bary = 1 - U_bary - V_bary 502 mask_triangle = (U_bary >= 0) & (V_bary >= 0) & (W_bary >= 0) 503 504 legend_rgb = ( 505 U_bary[..., None] * violet 506 + V_bary[..., None] * orange 507 + W_bary[..., None] * blue 508 ) 509 legend_rgb = np.clip(legend_rgb, 0.0, 1.0) 510 legend_rgba = np.zeros((res, res, 4)) 511 legend_rgba[..., :3] = legend_rgb 512 legend_rgba[..., 3] = mask_triangle.astype(float) 513 514 # Loop over grid and slope 515 h2o = gridded_data['h2o_ice'] 516 co2 = gridded_data['co2_ice'] 517 dust = gridded_data['dust'] 518 ngrid, ntime, nslope, nz = h2o.shape 519 520 for ig in range(ngrid): 521 for isl in range(nslope): 522 # Compute RGB stratification over time 523 rgb = np.ones((nz, ntime, 3), dtype=float) 524 for t in range(ntime): 525 mask_z = np.arange(nz) < top_index[ig, t, isl] 526 if not mask_z.any(): 527 continue 528 cH2O = np.clip(h2o[ig, t, isl, mask_z], 0, None) 529 cCO2 = np.clip(co2[ig, t, isl, mask_z], 0, None) 530 cDust = np.clip(dust[ig, t, isl, mask_z], 0, None) 531 total = cH2O + cCO2 + cDust 532 total[total == 0] = 1.0 533 fH2O = cH2O / total 534 fCO2 = cCO2 / total 535 fDust = cDust / total 536 mix = ( 537 np.outer(fH2O, blue) 538 + np.outer(fCO2, violet) 539 + np.outer(fDust, orange) 540 ) 541 mix = np.clip(mix, 0.0, 1.0) 542 rgb[mask_z, t, :] = mix 543 544 display_rgb = rgb[mask_elev, :, :] 545 546 # Create figure with legend 547 fig, (ax_main, ax_leg) = plt.subplots( 548 1, 2, figsize=(12, 5), dpi=200, 549 gridspec_kw={'width_ratios': [5, 1]} 550 ) 551 552 # Main stratification panel 553 ax_main.imshow( 554 display_rgb, 555 aspect='auto', 556 extent=[date_time[0], date_time[-1], elev.min(), elev.max()], 557 interpolation='nearest', 558 origin='lower' 559 ) 560 ax_main.set_facecolor('white') 561 ax_main.set_title(f"Ternary mix over time (Grid point {ig+1}, Slope {isl+1})", fontweight='bold') 562 ax_main.set_xlabel("Time (Mars years)") 563 ax_main.set_ylabel("Elevation (m)") 564 565 # Legend panel 566 ax_leg.imshow( 567 legend_rgba, 568 extent=[0, 1, 0, np.sqrt(3)/2], 569 origin='lower', 570 interpolation='nearest' 571 ) 572 573 # Draw triangle border 574 triangle = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)/2], [0, 0]]) 575 ax_leg.plot(triangle[:, 0], triangle[:, 1], 'k-', linewidth=1) 576 577 # Dashed gridlines 578 ticks = np.linspace(0.25, 0.75, 3) 579 for f in ticks: 580 ax_leg.plot([1 - f, 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5) 581 ax_leg.plot([f, f + 0.5 * (1 - f)], [0, (1 - f)*np.sqrt(3)/2], '--', color='k', linewidth=0.5) 582 y = (np.sqrt(3)/2) * f 583 ax_leg.plot([0.5 * f, 1 - 0.5 * f], [y, y], '--', color='k', linewidth=0.5) 584 585 # Legend labels 586 ax_leg.text(0, -0.05, 'H2O ice', ha='center', va='top', fontsize=8) 587 ax_leg.text(1, -0.05, 'CO2 ice', ha='center', va='top', fontsize=8) 588 ax_leg.text(0.5, np.sqrt(3)/2 + 0.05, 'Dust', ha='center', va='bottom', fontsize=8) 589 ax_leg.axis('off') 590 591 plt.tight_layout() 592 593 # Save figure 594 fname = os.path.join(output_folder, f"layering_rgb_evolution_ig{ig+1}_is{isl+1}.png") 595 fig.savefig(fname, dpi=150, bbox_inches='tight') 596 597 598 def plot_dust_to_ice_ratio_over_time( 599 gridded_data, 600 ref_grid, 601 top_index, 602 heights_data, 603 date_time, 604 exclude_sub=False, 605 output_folder="." 606 ): 607 """ 608 Plot the dust-to-ice ratio in the stratification over time, 609 using a blue-to-orange colormap: 610 - blue: ice-dominated (low dust-to-ice ratio) 611 - orange: dust-dominated (high dust-to-ice ratio) 612 """ 613 h2o = gridded_data['h2o_ice'] 614 dust = gridded_data['dust'] 615 ngrid, ntime, nslope, nz = h2o.shape 616 617 # Elevation mask 618 if exclude_sub: 619 elevation_mask = (ref_grid >= 0.0) 620 elev = ref_grid[elevation_mask] 621 else: 622 elevation_mask = np.ones_like(ref_grid, dtype=bool) 623 elev = ref_grid 624 625 # Define custom blue-to-orange colormap 626 blue = np.array([0, 0, 255], dtype=float) / 255 627 orange = np.array([255, 165, 0], dtype=float) / 255 628 custom_cmap = LinearSegmentedColormap.from_list('BlueOrange', [blue, orange], N=256) 629 630 # Log‑ratio bounds and small epsilon to avoid log(0) 631 vmin, vmax = -2, 1 632 epsilon = 1e-6 633 634 # Loop over grids and slopes 635 for ig in range(ngrid): 636 for isl in range(nslope): 637 log_ratio_array = np.full((nz, ntime), np.nan, dtype=np.float32) 638 639 # Compute log10(dust/ice) profile at each time step 640 for t in range(ntime): 641 zmax = top_index[ig, t, isl] 642 if zmax <= 0: 643 continue 644 645 h2o_profile = np.clip(h2o[ig, t, isl, :zmax], 0, None) 646 dust_profile = np.clip(dust[ig, t, isl, :zmax], 0, None) 647 648 with np.errstate(divide='ignore', invalid='ignore'): 649 ratio_profile = np.where( 650 h2o_profile > 0, 651 dust_profile / h2o_profile, 652 10**(vmax + 1) 653 ) 654 log_ratio = np.log10(ratio_profile + epsilon) 655 log_ratio = np.clip(log_ratio, vmin, vmax) 656 657 log_ratio_array[:zmax, t] = log_ratio 658 659 # Convert back to linear ratio and apply elevation mask 660 ratio_array = 10**log_ratio_array 661 ratio_display = ratio_array[elevation_mask, :] 662 663 # Plot 664 fig, ax = plt.subplots(figsize=(8, 6), dpi=150) 665 im = ax.imshow( 666 ratio_display, 667 aspect='auto', 668 extent=[date_time[0], date_time[-1], elev.min(), elev.max()], 669 origin='lower', 670 interpolation='nearest', 671 cmap='managua_r', 672 norm=LogNorm(vmin=10**vmin, vmax=10**vmax) 673 ) 674 675 # Add colorbar with simplified ratio labels 676 cbar = fig.colorbar(im, ax=ax, orientation='vertical') 677 cbar.set_label('Dust / H₂O ice (ratio)') 678 679 # Define custom ticks and labels 680 ticks = [1e-2, 1e-1, 1, 1e1] 681 labels = ['1:100', '1:10', '1:1', '10:1'] 682 cbar.set_ticks(ticks) 683 cbar.set_ticklabels(labels) 684 685 # Save figure 686 plt.tight_layout() 687 fname = os.path.join( 688 output_folder, 689 f"dust_to_ice_ratio_grid{ig+1}_slope{isl+1}.png" 464 690 ) 465 691 fig.savefig(fname, dpi=150) … … 493 719 fig.suptitle( 494 720 f"Strata count & total height over time for (Grid point {ig+1}, Slope {isl+1})", 495 fontsize=14 721 fontsize=14, 722 fontweight='bold' 496 723 ) 497 724 … … 549 776 # Plot 550 777 fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True) 551 fig.suptitle("Orbital Parameters vs Simulated Time", fontsize=14 )778 fig.suptitle("Orbital Parameters vs Simulated Time", fontsize=14, fontweight='bold') 552 779 553 780 axes[0].plot(date_time, obl_interp, 'r+', linestyle='-') … … 626 853 print(f"Warning: {date_time.size} timestamps vs {ntime} NetCDF files.") 627 854 628 # 13) Plot stratification over time855 # 13) Plot stratification data over time 629 856 plot_stratification_over_time( 630 857 gridded_data, ref_grid, top_index, heights_data, date_time, 631 858 exclude_sub=exclude_sub, output_folder="." 632 859 ) 633 634 # 14) Plot strata count & total height vs time 860 plot_stratification_rgb_over_time( 861 gridded_data, ref_grid, top_index, heights_data, date_time, 862 exclude_sub=exclude_sub, output_folder="." 863 ) 864 plot_dust_to_ice_ratio_over_time( 865 gridded_data, ref_grid, top_index, heights_data, date_time, 866 exclude_sub=exclude_sub, output_folder="." 867 ) 635 868 plot_strata_count_and_total_height(heights_data, date_time, output_folder=".") 636 869 637 # 1 5) Plot orbital parameters870 # 14) Plot orbital parameters 638 871 plot_orbital_parameters(infofile, orbfile, date_time, output_folder=".") 639 872 640 # 1 6) Show all figures873 # 15) Show all figures 641 874 plt.show() 642 875
Note: See TracChangeset
for help on using the changeset viewer.