################################################# ## ## Plotting utilities for AMSS-NCKU numerical relativity outputs ## Author: Xiaoqu ## 2024/10/01 --- 2025/09/14 ## ################################################# import numpy ## numpy for array operations import matplotlib matplotlib.use('Agg') ## use non-interactive backend for multiprocessing safety import matplotlib.pyplot as plt ## matplotlib for plotting from mpl_toolkits.mplot3d import Axes3D ## needed for 3D plots import glob import os ## operating system utilities import plot_binary_data import AMSS_NCKU_Input as input_data import subprocess import sys import multiprocessing # plt.rcParams['text.usetex'] = True ## enable LaTeX fonts in plots #################################################################################### ## Generate all 2D plots from AMSS-NCKU binary output def generate_binary_data_plot( binary_outdir, figure_outdir ): # create directories to store generated figures surface_plot_outdir = os.path.join( figure_outdir, "surface plot" ) os.mkdir( surface_plot_outdir ) density_plot_outdir = os.path.join( figure_outdir, "density plot" ) os.mkdir( density_plot_outdir ) contour_plot_outdir = os.path.join( figure_outdir, "contour plot" ) os.mkdir( contour_plot_outdir ) print( ) print( " Reading AMSS-NCKU Binary Data From Output " ) print( ) print( " List of binary data " ) ## Set which files to plot (here: all .bin files) globby = glob.glob( os.path.join(binary_outdir, '*.bin') ) file_list = [] for x in sorted(globby): file_list.append(x) print(x) ## Plot each file in parallel using subprocesses. ## Each subprocess is a fresh Python process where the BLAS thread-count ## environment variables (set at the top of plot_binary_data.py) take ## effect before numpy is imported. This avoids the thread explosion ## that occurs when multiprocessing.Pool with 'fork' context inherits ## already-initialized multi-threaded BLAS from the parent. script = os.path.join( os.path.dirname(__file__), "plot_binary_data.py" ) max_workers = min( multiprocessing.cpu_count(), len(file_list) ) if file_list else 0 running = [] failed = [] for filename in file_list: print(filename) proc = subprocess.Popen( [sys.executable, script, filename, binary_outdir, figure_outdir], ) running.append( (proc, filename) ) ## Keep at most max_workers subprocesses active at a time if len(running) >= max_workers: p, fn = running.pop(0) p.wait() if p.returncode != 0: failed.append(fn) ## Wait for all remaining subprocesses to finish for p, fn in running: p.wait() if p.returncode != 0: failed.append(fn) if failed: print( " WARNING: the following binary data plots failed:" ) for fn in failed: print( " ", fn ) print( ) print( " Binary Data Plot Has been Finished " ) print( ) return #################################################################################### #################################################################################### ## Plot black-hole puncture trajectories (2D) def generate_puncture_orbit_plot( outdir, figure_outdir ): print( ) print( " Plotting the black holes' trajectory (2D plot)" ) print( ) # path to data file file0 = os.path.join(outdir, "bssn_BH.dat") print( " Corresponding data file = ", file0 ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # print(data[:,0]) # print(data[:,2]) # initialize min/max arrays for black-hole coordinates BH_Xmin = numpy.zeros(input_data.puncture_number) BH_Xmax = numpy.zeros(input_data.puncture_number) BH_Ymin = numpy.zeros(input_data.puncture_number) BH_Ymax = numpy.zeros(input_data.puncture_number) BH_Zmin = numpy.zeros(input_data.puncture_number) BH_Zmax = numpy.zeros(input_data.puncture_number) # -------------------------- # Plot black-hole displacement trajectory (XY) plt.figure( figsize=(8,8) ) ## figsize sets the figure size plt.title( " Black Hole Trajectory ", fontsize=18 ) ## fontsize sets the title size for i in range(input_data.puncture_number): BH_x = data[:, 3*i+1] BH_y = data[:, 3*i+2] BH_z = data[:, 3*i+3] BH_Xmin[i] = min( BH_x ) BH_Xmax[i] = max( BH_x ) BH_Ymin[i] = min( BH_y ) BH_Ymax[i] = max( BH_y ) if i==0: plt.plot( BH_x, BH_y, color='red', label="BH"+str(i+1), linewidth=2 ) elif i==1: plt.plot( BH_x, BH_y, color='green', label="BH"+str(i+1), linewidth=2 ) elif i==2: plt.plot( BH_x, BH_y, color='blue', label="BH"+str(i+1), linewidth=2 ) elif i==3: plt.plot( BH_x, BH_y, color='gray', label="BH"+str(i+1), linewidth=2 ) plt.xlabel( "X [M]", fontsize=16 ) plt.ylabel( "Y [M]", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Xmin0 = min( BH_Xmin ) Xmax0 = max( BH_Xmax ) Ymin0 = min( BH_Ymin ) Ymax0 = max( BH_Ymax ) Xmin = min( Xmin0-2.0, -5.0 ) Xmax = max( Xmax0+2.0, +5.0 ) Ymin = min( Ymin0-2.0, -5.0 ) Ymax = max( Ymax0+2.0, +5.0 ) plt.xlim( Xmin, Xmax ) # x axis range from Xmin to Xmax plt.ylim( Ymin, Ymax ) # y axis range from Ymin to Ymax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines # plt.show( ) plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_XY.pdf") ) plt.close( ) # -------------------------- # Plot black-hole displacement trajectory (XZ) plt.figure( figsize=(8,8) ) ## figsize sets the figure size plt.title( " Black Hole Trajectory ", fontsize=18 ) ## fontsize sets the title size for i in range(input_data.puncture_number): BH_x = data[:, 3*i+1] BH_y = data[:, 3*i+2] BH_z = data[:, 3*i+3] BH_Xmin[i] = min( BH_x ) BH_Xmax[i] = max( BH_x ) BH_Zmin[i] = min( BH_z ) BH_Zmax[i] = max( BH_z ) if i==0: plt.plot( BH_x, BH_z, color='red', label="BH"+str(i+1), linewidth=2 ) elif i==1: plt.plot( BH_x, BH_z, color='green', label="BH"+str(i+1), linewidth=2 ) elif i==2: plt.plot( BH_x, BH_z, color='blue', label="BH"+str(i+1), linewidth=2 ) elif i==3: plt.plot( BH_x, BH_z, color='gray', label="BH"+str(i+1), linewidth=2 ) plt.xlabel( "X [M]", fontsize=16 ) plt.ylabel( "Z [M]", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Xmin0 = min( BH_Xmin ) Xmax0 = max( BH_Xmax ) Zmin0 = min( BH_Zmin ) Zmax0 = max( BH_Zmax ) Xmin = min( Xmin0-2.0, -5.0 ) Xmax = max( Xmax0+2.0, +5.0 ) Zmin = min( Zmin0-2.0, -5.0 ) Zmax = max( Zmax0+2.0, +5.0 ) plt.xlim( Xmin, Xmax ) # x axis range from Xmin to Xmax plt.ylim( Zmin, Zmax ) # z axis range from Zmin to Zmax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines # plt.show( ) plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_XZ.pdf") ) plt.close( ) # -------------------------- # Plot black-hole displacement trajectory (YZ) plt.figure( figsize=(8,8) ) ## figsize sets the figure size plt.title( " Black Hole Trajectory ", fontsize=18 ) ## fontsize sets the title size for i in range(input_data.puncture_number): BH_x = data[:, 3*i+1] BH_y = data[:, 3*i+2] BH_z = data[:, 3*i+3] BH_Ymin[i] = min( BH_y ) BH_Ymax[i] = max( BH_y ) BH_Zmin[i] = min( BH_z ) BH_Zmax[i] = max( BH_z ) if i==0: plt.plot( BH_y, BH_z, color='red', label="BH"+str(i+1), linewidth=2 ) elif i==1: plt.plot( BH_y, BH_z, color='green', label="BH"+str(i+1), linewidth=2 ) elif i==2: plt.plot( BH_y, BH_z, color='blue', label="BH"+str(i+1), linewidth=2 ) elif i==3: plt.plot( BH_y, BH_z, color='gray', label="BH"+str(i+1), linewidth=2 ) plt.xlabel( "Y [M]", fontsize=16 ) plt.ylabel( "Z [M]", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Ymin0 = min( BH_Ymin ) Ymax0 = max( BH_Ymax ) Zmin0 = min( BH_Zmin ) Zmax0 = max( BH_Zmax ) Ymin = min( Ymin0-2.0, -5.0 ) Ymax = max( Ymax0+2.0, +5.0 ) Zmin = min( Zmin0-2.0, -5.0 ) Zmax = max( Zmax0+2.0, +5.0 ) plt.xlim( Ymin, Ymax ) # x axis range from Ymin to Ymax plt.ylim( Zmin, Zmax ) # z axis range from Zmin to Zmax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines # plt.show( ) plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_YZ.pdf") ) plt.close( ) # -------------------------- # extract coordinates for BH1 and BH2 BH_x1 = data[:, 1] BH_y1 = data[:, 2] BH_z1 = data[:, 3] BH_x2 = data[:, 4] BH_y2 = data[:, 5] BH_z2 = data[:, 6] # -------------------------- # Plot relative trajectory: (X2-X1) vs (Y2-Y1) plt.figure( figsize=(8,8) ) plt.title( " Black Hole Trajectory ", fontsize=18 ) plt.plot( (BH_x2-BH_x1), (BH_y2-BH_y1), color='blue', linewidth=2 ) plt.xlabel( " $X_{2}$ - $X_{1}$ [M] ", fontsize=16 ) plt.ylabel( " $Y_{2}$ - $Y_{1}$ [M] ", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Xmin0 = min( (BH_x2 - BH_x1) ) Xmax0 = max( (BH_x2 - BH_x1) ) Ymin0 = min( (BH_y2 - BH_y1) ) Ymax0 = max( (BH_y2 - BH_y1) ) Xmin = min( Xmin0-2.0, -5.0 ) Xmax = max( Xmax0+2.0, +5.0 ) Ymin = min( Ymin0-2.0, -5.0 ) Ymax = max( Ymax0+2.0, +5.0 ) plt.xlim( Xmin, Xmax ) # x axis range from Xmin to Xmax plt.ylim( Ymin, Ymax ) # y axis range from Ymin to Ymax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # show grid lines plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_21_XY.pdf") ) plt.close( ) # -------------------------- # plot BH displacement trajectory (X2-X1 Z2-Z1) plt.figure( figsize=(8,8) ) plt.title( " Black Hole Trajectory ", fontsize=18 ) plt.plot( (BH_x2-BH_x1), (BH_z2-BH_z1), color='blue', linewidth=2 ) plt.xlabel( " $X_{2}$ - $X_{1}$ [M] ", fontsize=16 ) plt.ylabel( " $Z_{2}$ - $Z_{1}$ [M] ", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Xmin0 = min( (BH_x2 - BH_x1) ) Xmax0 = max( (BH_x2 - BH_x1) ) Zmin0 = min( (BH_z2 - BH_z1) ) Zmax0 = max( (BH_z2 - BH_z1) ) Xmin = min( Xmin0-2.0, -5.0 ) Xmax = max( Xmax0+2.0, +5.0 ) Zmin = min( Zmin0-2.0, -5.0 ) Zmax = max( Zmax0+2.0, +5.0 ) plt.xlim( Xmin, Xmax ) # x axis range from Xmin to Xmax plt.ylim( Zmin, Zmax ) # z axis range from Zmin to Zmax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # show grid lines plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_21_XZ.pdf") ) plt.close( ) # -------------------------- # plot BH displacement trajectory (Y2-Y1 Z2-Z1) plt.figure( figsize=(8,8) ) plt.title( " Black Hole Trajectory ", fontsize=18 ) plt.plot( (BH_y2-BH_y1), (BH_z2-BH_z1), color='blue', linewidth=2 ) plt.xlabel( " $Y_{2}$ - $Y_{1}$ [M] ", fontsize=16 ) plt.ylabel( " $Z_{2}$ - $Z_{1}$ [M] ", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Ymin0 = min( (BH_y2 - BH_y1) ) Ymax0 = max( (BH_y2 - BH_y1) ) Zmin0 = min( (BH_z2 - BH_z1) ) Zmax0 = max( (BH_z2 - BH_z1) ) Ymin = min( Ymin0-2.0, -5.0 ) Ymax = max( Ymax0+2.0, +5.0 ) Zmin = min( Zmin0-2.0, -5.0 ) Zmax = max( Zmax0+2.0, +5.0 ) plt.xlim( Ymin, Ymax ) # x axis range from Ymin to Ymax plt.ylim( Zmin, Zmax ) # z axis range from Zmin to Zmax plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # show grid lines plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_21_YZ.pdf") ) plt.close( ) # -------------------------- # NOTE: file0 is only a filename string here; no file object to close print( ) print( " Black holes' trajectory plot has been finished (2D plot)" ) print( ) return #################################################################################### #################################################################################### ## Plot relative distances between black holes def generate_puncture_distence_plot( outdir, figure_outdir ): print( ) print( " Plotting the black hole relative distance " ) print( ) # path to data file file0 = os.path.join(outdir, "bssn_BH.dat") print( " Corresponding data file = ", file0 ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # -------------------------- # -------------------------- # Plot each black hole's distance R from the origin as a function of time # initialize min/max arrays for BH distances BH_Rmin = numpy.zeros(input_data.puncture_number) BH_Rmax = numpy.zeros(input_data.puncture_number) # create a new figure fig = plt.figure( figsize=(8,8) ) plt.title( " Black Hole Position R ", fontsize=18 ) # title BH_time = data[:, 0] for i in range(input_data.puncture_number): BH_x = data[:, 3*i+1] BH_y = data[:, 3*i+2] BH_z = data[:, 3*i+3] BH_R = (BH_x*BH_x + BH_y*BH_y + BH_z*BH_z)**0.5 # compute distance R using numpy BH_Rmin[i] = min( BH_R ) BH_Rmax[i] = max( BH_R ) if i==0: plt.plot( BH_time, BH_R, color='red', label="BH"+str(i+1), linewidth=2 ) elif i==1: plt.plot( BH_time, BH_R, color='green', label="BH"+str(i+1), linewidth=2 ) elif i==2: plt.plot( BH_time, BH_R, color='blue', label="BH"+str(i+1), linewidth=2 ) elif i==3: plt.plot( BH_time, BH_R, color='gray', label="BH"+str(i+1), linewidth=2 ) # set axis labels plt.xlabel( " $T$ [M] ", fontsize=16 ) plt.ylabel( " $R$ [M] ", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges R_min0 = min( BH_Rmin ) R_max0 = max( BH_Rmax ) R_min = max( R_min0-2.0, 0.0 ) R_max = max( R_max0+2.0, +5.0 ) plt.ylim( R_min, R_max ) # y axis range from R_min to R_max plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines # plt.show( ) plt.savefig( os.path.join(figure_outdir, "BH_Position_R.pdf") ) plt.close( ) # -------------------------- # extract coordinates for BH1 and BH2 BH_x1 = data[:, 1] BH_y1 = data[:, 2] BH_z1 = data[:, 3] BH_x2 = data[:, 4] BH_y2 = data[:, 5] BH_z2 = data[:, 6] # compute relative distance R12 between BH1 and BH2 BH_R12 = ( (BH_x2-BH_x1)**2 + (BH_y2-BH_y1)**2 + (BH_z2-BH_z1)**2 )**0.5 # -------------------------- # plot relative distance R12 between BH1 and BH2 as a function of time plt.figure( figsize=(8,8) ) plt.title( " Black Hole Distance ", fontsize=18 ) plt.plot( BH_time, BH_R12, color='blue', linewidth=2 ) plt.xlabel( " $T$ [M] ", fontsize=16 ) plt.ylabel( " $R_{12}$ [M] ", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges R12_min0 = min( BH_R12 ) R12_max0 = max( BH_R12 ) R12_min = max( R12_min0-2.0, 0.0 ) R12_max = max( R12_max0+2.0, +5.0 ) plt.ylim( R12_min, R12_max ) # y axis range from R12_min to R12_max plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # show grid lines plt.savefig( os.path.join(figure_outdir, "BH_Distance_21.pdf") ) plt.close( ) print( ) print( " black hole relative distance plot has been finished " ) print( ) # -------------------------- return #################################################################################### #################################################################################### ## Plot black-hole puncture trajectories (3D) def generate_puncture_orbit_plot3D( outdir, figure_outdir ): print( ) print( " Plotting the black holes' trajectory (3D plot) " ) print( ) # path to data file file0 = os.path.join(outdir, "bssn_BH.dat") print( " Corresponding data file = ", file0 ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # initialize min/max arrays for black-hole coordinates BH_Xmin = numpy.zeros(input_data.puncture_number) BH_Xmax = numpy.zeros(input_data.puncture_number) BH_Ymin = numpy.zeros(input_data.puncture_number) BH_Ymax = numpy.zeros(input_data.puncture_number) BH_Zmin = numpy.zeros(input_data.puncture_number) BH_Zmax = numpy.zeros(input_data.puncture_number) # create a new figure fig = plt.figure( figsize=(8,8) ) # create a 3D axes ax = fig.add_subplot(111, projection='3d') # set title ax.set_title( " Black Hole Trajectory ", fontsize=18 ) for i in range(input_data.puncture_number): BH_x = data[:, 3*i+1] BH_y = data[:, 3*i+2] BH_z = data[:, 3*i+3] BH_Xmin[i] = min( BH_x ) BH_Xmax[i] = max( BH_x ) BH_Ymin[i] = min( BH_y ) BH_Ymax[i] = max( BH_y ) BH_Zmin[i] = min( BH_z ) BH_Zmax[i] = max( BH_z ) if i==0: ax.plot( BH_x, BH_y, BH_z, color='red', label="BH"+str(i+1), linewidth=2 ) elif i==1: ax.plot( BH_x, BH_y, BH_z, color='green', label="BH"+str(i+1), linewidth=2 ) elif i==2: ax.plot( BH_x, BH_y, BH_z, color='blue', label="BH"+str(i+1), linewidth=2 ) elif i==3: ax.plot( BH_x, BH_y, BH_z, color='gray', label="BH"+str(i+1), linewidth=2 ) # set axis labels ax.set_xlabel( "X [M]", fontsize=16 ) ax.set_ylabel( "Y [M]", fontsize=16 ) ax.set_zlabel( "Z [M]", fontsize=16 ) plt.legend( loc='upper right' ) # set axis ranges Xmin0 = min( BH_Xmin ) Xmax0 = max( BH_Xmax ) Ymin0 = min( BH_Ymin ) Ymax0 = max( BH_Ymax ) Zmin0 = min( BH_Zmin ) Zmax0 = max( BH_Zmax ) Xmin = min( Xmin0-2.0, -5.0 ) Xmax = max( Xmax0+2.0, +5.0 ) Ymin = min( Ymin0-2.0, -5.0 ) Ymax = max( Ymax0+2.0, +5.0 ) Zmin = min( Zmin0-2.0, -5.0 ) Zmax = max( Zmax0+2.0, +5.0 ) ax.set_xlim( [Xmin, Xmax] ) ax.set_ylim( [Ymin, Ymax] ) ax.set_zlim( [Zmin, Zmax] ) plt.savefig( os.path.join(figure_outdir, "BH_Trajectory_3D.pdf") ) plt.close( ) print( ) print( " Black holes' trajectory plot has been finished (3D plot)" ) print( ) return #################################################################################### #################################################################################### ## Plot gravitational-wave waveform Psi4 def generate_gravitational_wave_psi4_plot( outdir, figure_outdir, detector_number_i ): # path to data file file0 = os.path.join(outdir, "bssn_psi4.dat") if ( detector_number_i == 0 ): print( ) print( " Plotting the Weyl conformal component Psi4 " ) print( ) print( " corresponding data file = ", file0 ) print( ) print( " Begin the Weyl conformal Psi4 plot for detector number = ", detector_number_i ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # extract columns from the Phi4 file time = data[:,0] psi4_l2m2m_real = data[:,1] psi4_l2m2m_imaginary = data[:,2] psi4_l2m1m_real = data[:,3] psi4_l2m1m_imaginary = data[:,4] psi4_l2m0_real = data[:,5] psi4_l2m0_imaginary = data[:,6] psi4_l2m1_real = data[:,7] psi4_l2m1_imaginary = data[:,8] psi4_l2m2_real = data[:,9] psi4_l2m2_imaginary = data[:,10] # NOTE: file0 is only a filename string here; no file object to close # In Python division returns float; use integer division here length = len(time) // input_data.Detector_Number time2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m2m_real2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m2m_imaginary2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m1m_real2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m1m_imaginary2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m0_real2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m0_imaginary2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m1_real2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m1_imaginary2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m2_real2 = numpy.zeros( (input_data.Detector_Number, length) ) psi4_l2m2_imaginary2 = numpy.zeros( (input_data.Detector_Number, length) ) # split data into arrays corresponding to each detector radius for i in range(input_data.Detector_Number): for j in range(length): time2[i,j] = time[ j*input_data.Detector_Number + i ] psi4_l2m2m_real2[i,j] = psi4_l2m2m_real[ j*input_data.Detector_Number + i ] psi4_l2m2m_imaginary2[i,j] = psi4_l2m2m_imaginary[ j*input_data.Detector_Number + i ] psi4_l2m1m_real2[i,j] = psi4_l2m1m_real[ j*input_data.Detector_Number + i ] psi4_l2m1m_imaginary2[i,j] = psi4_l2m1m_imaginary[ j*input_data.Detector_Number + i ] psi4_l2m0_real2[i,j] = psi4_l2m0_real[ j*input_data.Detector_Number + i ] psi4_l2m0_imaginary2[i,j] = psi4_l2m0_imaginary[ j*input_data.Detector_Number + i ] psi4_l2m1_real2[i,j] = psi4_l2m1_real[ j*input_data.Detector_Number + i ] psi4_l2m1_imaginary2[i,j] = psi4_l2m1_imaginary[ j*input_data.Detector_Number + i ] psi4_l2m2_real2[i,j] = psi4_l2m2_real[ j*input_data.Detector_Number + i ] psi4_l2m2_imaginary2[i,j] = psi4_l2m2_imaginary[ j*input_data.Detector_Number + i ] # compute detector distance from input parameters Detector_Interval = ( input_data.Detector_Rmax - input_data.Detector_Rmin ) / ( input_data.Detector_Number - 1 ) Detector_Distance_R = input_data.Detector_Rmax - Detector_Interval * detector_number_i plt.figure( figsize=(8,8) ) ## figsize sets the figure size plt.title( f" Gravitational Wave $\Psi_{4}$ Detector Distance = { Detector_Distance_R } ", fontsize=18 ) ## fontsize sets the title size plt.plot( time2[detector_number_i], psi4_l2m0_real2[detector_number_i], \ color='red', label="l=2 m=0 real", linewidth=2 ) plt.plot( time2[detector_number_i], psi4_l2m0_imaginary2[detector_number_i], \ color='orange', label="l=2 m=0 imaginary", linestyle='--', linewidth=2 ) plt.plot( time2[detector_number_i], psi4_l2m1_real2[detector_number_i], \ color='green', label="l=2 m=1 real", linewidth=2 ) plt.plot( time2[detector_number_i], psi4_l2m1_imaginary2[detector_number_i], \ color='cyan', label="l=2 m=1 imaginary", linestyle='--', linewidth=2 ) plt.plot( time2[detector_number_i], psi4_l2m2_real2[detector_number_i], \ color='black', label="l=2 m=2 real", linewidth=2 ) plt.plot( time2[detector_number_i], psi4_l2m2_imaginary2[detector_number_i], \ color='gray', label="l=2 m=2 imaginary", linestyle='--', linewidth=2 ) plt.xlabel( "T [M]", fontsize=16 ) plt.ylabel( r"$R*\Psi$", fontsize=16 ) plt.legend( loc='upper right' ) plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines plt.savefig( os.path.join(figure_outdir, "Gravitational_Psi4_Detector_" + str(detector_number_i) + ".pdf") ) print( " The Weyl Conformal component Psi4 plot has been finished ", " detector number ", detector_number_i ) print( ) if ( detector_number_i == (input_data.Detector_Number-1) ): print( ) print( " The Weyl conformal component Psi4 plots have been finished " ) print( ) return #################################################################################### #################################################################################### ## Plot ADM mass and angular momentum def generate_ADMmass_plot( outdir, figure_outdir, detector_number_i ): # path to data file file0 = os.path.join(outdir, "bssn_ADMQs.dat") if ( detector_number_i == 0 ): print( ) print( " Plotting the ADM mass and angular momentum " ) print( ) print( " corresponding data file = ", file0 ) print( ) print( " Begin the ADM momentum plot for detector number = ", detector_number_i ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # extract columns from the ADM momentum file time = data[:,0] ADM_mass = data[:,1] ADM_Px = data[:,2] ADM_Py = data[:,3] ADM_Pz = data[:,4] ADM_Jx = data[:,5] ADM_Jy = data[:,6] ADM_Jz = data[:,7] # NOTE: file0 is only a filename string here; no file object to close # In Python division returns a float; use integer division here length = len(time) // input_data.Detector_Number ''' # split data into arrays corresponding to each detector radius (disabled) # time2 = time.reshape( (input_data.Detector_Number, length) ) # ADM_mass2 = ADM_mass.reshape( (input_data.Detector_Number, length) ) # ADM_Px2 = ADM_Px.reshape( (input_data.Detector_Number, length) ) # ADM_Py2 = ADM_Py.reshape( (input_data.Detector_Number, length) ) # ADM_Pz2 = ADM_Pz.reshape( (input_data.Detector_Number, length) ) # ADM_Jx2 = ADM_Jx.reshape( (input_data.Detector_Number, length) ) # ADM_Jy2 = ADM_Jy.reshape( (input_data.Detector_Number, length) ) # ADM_Jz2 = ADM_Jz.reshape( (input_data.Detector_Number, length) ) ''' # Rows/cols in reshape were unclear; use straightforward indexing instead time2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_mass2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Px2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Py2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Pz2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Jx2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Jy2 = numpy.zeros( (input_data.Detector_Number, length) ) ADM_Jz2 = numpy.zeros( (input_data.Detector_Number, length) ) # split data into arrays corresponding to each detector radius for i in range(input_data.Detector_Number): for j in range(length): time2[i,j] = time[ j*input_data.Detector_Number + i ] ADM_mass2[i,j] = ADM_mass[ j*input_data.Detector_Number + i ] ADM_Px2[i,j] = ADM_Px[ j*input_data.Detector_Number + i ] ADM_Py2[i,j] = ADM_Py[ j*input_data.Detector_Number + i ] ADM_Pz2[i,j] = ADM_Pz[ j*input_data.Detector_Number + i ] ADM_Jx2[i,j] = ADM_Jx[ j*input_data.Detector_Number + i ] ADM_Jy2[i,j] = ADM_Jy[ j*input_data.Detector_Number + i ] ADM_Jz2[i,j] = ADM_Jz[ j*input_data.Detector_Number + i ] # compute detector distance from input parameters Detector_Interval = ( input_data.Detector_Rmax - input_data.Detector_Rmin ) / ( input_data.Detector_Number - 1 ) Detector_Distance_R = input_data.Detector_Rmax - Detector_Interval * detector_number_i # Plot ADM momentum for the current detector radius plt.figure( figsize=(8,8) ) plt.title(f" ADM Momentum Detector Distence = {Detector_Distance_R}", fontsize=18 ) plt.plot( time2[detector_number_i], ADM_mass2[detector_number_i], color='red', label="ADM Mass", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Px2[detector_number_i], color='green', label="ADM Px", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Py2[detector_number_i], color='cyan', label="ADM Py", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Pz2[detector_number_i], color='blue', label="ADM Pz", linewidth=2 ) plt.xlabel( "T [M]", fontsize=16 ) plt.ylabel( "ADM Momentum [M]", fontsize=16 ) plt.legend( loc='upper right' ) plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines plt.savefig( os.path.join(figure_outdir, "ADM_Mass_Dector_" + str(detector_number_i) + ".pdf") ) # Plot ADM angular momentum for the current detector radius plt.figure( figsize=(8,8) ) plt.title(f" ADM Angular Momentum Detector Distence = {Detector_Distance_R}", fontsize=18 ) # plt.plot( time2[detector_number_i], ADM_mass2[detector_number_i], color='red', label="ADM Mass", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Jx2[detector_number_i], color='green', label="ADM Jx", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Jy2[detector_number_i], color='cyan', label="ADM Jy", linewidth=2 ) plt.plot( time2[detector_number_i], ADM_Jz2[detector_number_i], color='blue', label="ADM Jz", linewidth=2 ) plt.xlabel( "T [M]", fontsize=16 ) plt.ylabel( "ADM Angular Momentum [$M^2$]", fontsize=16 ) plt.legend( loc='upper right' ) plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines plt.savefig( os.path.join(figure_outdir, "ADM_Angular_Momentum_Dector_" + str(detector_number_i) + ".pdf") ) print( " ADM momentum plot has been finished, detector number = ", detector_number_i ) print( ) if ( detector_number_i == (input_data.Detector_Number-1) ): print( " The ADM mass and augular momentum plots have been finished " ) print( ) return #################################################################################### #################################################################################### ## Plot constraint violation for each grid level def generate_constraint_check_plot( outdir, figure_outdir, input_level_number ): # path to data file file0 = os.path.join(outdir, "bssn_constraint.dat") if ( input_level_number == 0 ): print( ) print( " Plotting the constraint violation for each grid level" ) print( ) print( " corresponding data file = ", file0 ) print( ) print( " Begin the constraint violation plot for grid level number = ", input_level_number ) # load the full data file (assumed whitespace-separated floats) data = numpy.loadtxt(file0) # extract columns from the constraint data file time = data[:,0] Constraint_H = data[:,1] Constraint_Px = data[:,2] Constraint_Py = data[:,3] Constraint_Pz = data[:,4] Constraint_Gx = data[:,5] Constraint_Gy = data[:,6] Constraint_Gz = data[:,7] # NOTE: file0 is only a filename string here; no file object to close # initialize arrays for different quantities if (input_data.basic_grid_set == "Patch"): level_number = input_level_number length0 = input_data.grid_level # In Python division returns a float; use integer division here length1 = len(time) // length0 elif (input_data.basic_grid_set == "Shell-Patch"): # If grid type is Shell-Patch, increment the grid-level count level_number = input_level_number + 1 length0 = input_data.grid_level + 1 # In Python division returns a float; use integer division here length1 = len(time) // length0 time2 = numpy.zeros( (length0, length1) ) Constraint_H2 = numpy.zeros( (length0, length1) ) Constraint_Px2 = numpy.zeros( (length0, length1) ) Constraint_Py2 = numpy.zeros( (length0, length1) ) Constraint_Pz2 = numpy.zeros( (length0, length1) ) Constraint_Gx2 = numpy.zeros( (length0, length1) ) Constraint_Gy2 = numpy.zeros( (length0, length1) ) Constraint_Gz2 = numpy.zeros( (length0, length1) ) # split data into arrays corresponding to each grid level for i in range(length0): for j in range(length1): time2[i,j] = time[ j*length0 + i ] Constraint_H2[i,j] = Constraint_H[ j*length0 + i ] Constraint_Px2[i,j] = Constraint_Px[ j*length0 + i ] Constraint_Py2[i,j] = Constraint_Py[ j*length0 + i ] Constraint_Pz2[i,j] = Constraint_Pz[ j*length0 + i ] # Plot constraint violation for the outermost grid level plt.figure( figsize=(8,8) ) plt.title( f" ADM Constraint Grid Level = {input_level_number}", fontsize=18 ) plt.plot( time2[level_number], Constraint_H2[level_number], color='red', label="ADM Constraint H", linewidth=2 ) plt.plot( time2[level_number], Constraint_Px2[level_number], color='green', label="ADM Constraint Px", linewidth=2 ) plt.plot( time2[level_number], Constraint_Py2[level_number], color='cyan', label="ADM Constraint Py", linewidth=2 ) plt.plot( time2[level_number], Constraint_Pz2[level_number], color='blue', label="ADM Constraint Pz", linewidth=2 ) plt.xlabel( "T [M]", fontsize=16 ) plt.ylabel( "ADM Constraint", fontsize=16 ) plt.legend( loc='upper right' ) plt.grid( color='gray', linestyle='--', linewidth=0.5 ) # display grid lines plt.savefig( os.path.join(figure_outdir, "ADM_Constraint_Grid_Level_" + str(input_level_number) + ".pdf") ) print( " Constraint violation plot has been finished, grid level number = ", input_level_number ) print( ) if ( input_level_number == (input_data.grid_level-1) ): print( " Constraint violation plot has been finished " ) print( ) return #################################################################################### #################################################################################### # Standalone examples ''' outdir = "./BBH_q=1" generate_puncture_orbit_plot( outdir, outdir ) generate_puncture_orbit_plot3D( outdir, outdir ) generate_puncture_distence_plot( outdir, outdir ) for i in range(input_data.grid_level): generate_constraint_check_plot( outdir, outdir, i ) for i in range(input_data.Detector_Number): generate_ADMmass_plot( outdir, outdir, i ) for i in range(input_data.Detector_Number): generate_gravitational_wave_psi4_plot( outdir, outdir, i ) ''' ####################################################################################