#!/usr/bin/env python3 """ AMSS-NCKU GW150914 Simulation Regression Test Script Verification Requirements: 1. XY-plane trajectory RMS error < 1% (Optimized vs. baseline, max of BH1 and BH2) 2. ADM constraint violation < 2 (Grid Level 0) RMS Calculation Method: - Computes trajectory deviation on the XY plane independently for BH1 and BH2 - For each black hole: RMS = sqrt((1/M) * sum((Δr_i / r_i^max)^2)) × 100% - Final RMS = max(RMS_BH1, RMS_BH2) Usage: python3 AMSS_NCKU_Verify_ASC26.py [output_dir] Default: output_dir = GW150914/AMSS_NCKU_output Reference: GW150914-origin (baseline simulation) """ import numpy as np import sys import os # ANSI Color Codes class Color: GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' BLUE = '\033[94m' BOLD = '\033[1m' RESET = '\033[0m' def get_status_text(passed): if passed: return f"{Color.GREEN}{Color.BOLD}PASS{Color.RESET}" else: return f"{Color.RED}{Color.BOLD}FAIL{Color.RESET}" def load_bh_trajectory(filepath): """Load black hole trajectory data""" data = np.loadtxt(filepath) return { 'time': data[:, 0], 'x1': data[:, 1], 'y1': data[:, 2], 'z1': data[:, 3], 'x2': data[:, 4], 'y2': data[:, 5], 'z2': data[:, 6] } def load_constraint_data(filepath): """Load constraint violation data""" data = [] with open(filepath, 'r') as f: for line in f: if line.startswith('#'): continue parts = line.split() if len(parts) >= 8: data.append([float(x) for x in parts[:8]]) return np.array(data) def calculate_rms_error(bh_data_ref, bh_data_target): """ Calculate trajectory-based RMS error on the XY plane between baseline and optimized simulations. This function computes the RMS error independently for BH1 and BH2 trajectories, then returns the maximum of the two as the final RMS error metric. For each black hole, the RMS is calculated as: RMS = sqrt( (1/M) * sum( (Δr_i / r_i^max)^2 ) ) × 100% where: Δr_i = sqrt((x_ref,i - x_new,i)^2 + (y_ref,i - y_new,i)^2) r_i^max = max(sqrt(x_ref,i^2 + y_ref,i^2), sqrt(x_new,i^2 + y_new,i^2)) Args: bh_data_ref: Reference (baseline) trajectory data bh_data_target: Target (optimized) trajectory data Returns: rms_value: Final RMS error as a percentage (max of BH1 and BH2) error: Error message if any """ # Align data: truncate to the length of the shorter dataset M = min(len(bh_data_ref['time']), len(bh_data_target['time'])) if M < 10: return None, "Insufficient data points for comparison" # Extract XY coordinates for both black holes x1_ref = bh_data_ref['x1'][:M] y1_ref = bh_data_ref['y1'][:M] x2_ref = bh_data_ref['x2'][:M] y2_ref = bh_data_ref['y2'][:M] x1_new = bh_data_target['x1'][:M] y1_new = bh_data_target['y1'][:M] x2_new = bh_data_target['x2'][:M] y2_new = bh_data_target['y2'][:M] # Calculate RMS for BH1 delta_r1 = np.sqrt((x1_ref - x1_new)**2 + (y1_ref - y1_new)**2) r1_ref = np.sqrt(x1_ref**2 + y1_ref**2) r1_new = np.sqrt(x1_new**2 + y1_new**2) r1_max = np.maximum(r1_ref, r1_new) # Calculate RMS for BH2 delta_r2 = np.sqrt((x2_ref - x2_new)**2 + (y2_ref - y2_new)**2) r2_ref = np.sqrt(x2_ref**2 + y2_ref**2) r2_new = np.sqrt(x2_new**2 + y2_new**2) r2_max = np.maximum(r2_ref, r2_new) # Avoid division by zero for BH1 valid_mask1 = r1_max > 1e-15 if np.sum(valid_mask1) < 10: return None, "Insufficient valid data points for BH1" terms1 = (delta_r1[valid_mask1] / r1_max[valid_mask1])**2 rms_bh1 = np.sqrt(np.mean(terms1)) * 100 # Avoid division by zero for BH2 valid_mask2 = r2_max > 1e-15 if np.sum(valid_mask2) < 10: return None, "Insufficient valid data points for BH2" terms2 = (delta_r2[valid_mask2] / r2_max[valid_mask2])**2 rms_bh2 = np.sqrt(np.mean(terms2)) * 100 # Final RMS is the maximum of BH1 and BH2 rms_final = max(rms_bh1, rms_bh2) return rms_final, None def analyze_constraint_violation(constraint_data, n_levels=9): """ Analyze ADM constraint violation Return maximum constraint violation for Grid Level 0 """ # Extract Grid Level 0 data (first entry for each time step) level0_data = constraint_data[::n_levels] # Calculate maximum absolute value for each constraint results = { 'Ham': np.max(np.abs(level0_data[:, 1])), 'Px': np.max(np.abs(level0_data[:, 2])), 'Py': np.max(np.abs(level0_data[:, 3])), 'Pz': np.max(np.abs(level0_data[:, 4])), 'Gx': np.max(np.abs(level0_data[:, 5])), 'Gy': np.max(np.abs(level0_data[:, 6])), 'Gz': np.max(np.abs(level0_data[:, 7])) } results['max_violation'] = max(results.values()) return results def print_header(): """Print report header""" print("\n" + Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET) print(Color.BOLD + " AMSS-NCKU GW150914 Simulation Regression Test Report" + Color.RESET) print(Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET) def print_rms_results(rms_rel, error, threshold=1.0): """Print RMS error results""" print(f"\n{Color.BOLD}1. RMS Error Analysis (Baseline vs Optimized){Color.RESET}") print("-" * 45) if error: print(f" {Color.RED}Error: {error}{Color.RESET}") return False passed = rms_rel < threshold print(f" RMS relative error: {rms_rel:.4f}%") print(f" Requirement: < {threshold}%") print(f" Status: {get_status_text(passed)}") return passed def print_constraint_results(results, threshold=2.0): """Print constraint violation results""" print(f"\n{Color.BOLD}2. ADM Constraint Violation Analysis (Grid Level 0){Color.RESET}") print("-" * 45) names = ['Ham', 'Px', 'Py', 'Pz', 'Gx', 'Gy', 'Gz'] for i, name in enumerate(names): print(f" Max |{name:3}|: {results[name]:.6f}", end=" ") if (i + 1) % 2 == 0: print() if len(names) % 2 != 0: print() passed = results['max_violation'] < threshold print(f"\n Maximum violation: {results['max_violation']:.6f}") print(f" Requirement: < {threshold}") print(f" Status: {get_status_text(passed)}") return passed def print_summary(rms_passed, constraint_passed): """Print summary""" print("\n" + Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET) print(Color.BOLD + "Verification Summary" + Color.RESET) print(Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET) all_passed = rms_passed and constraint_passed res_rms = get_status_text(rms_passed) res_con = get_status_text(constraint_passed) print(f" [1] RMS trajectory check: {res_rms}") print(f" [2] ADM constraint check: {res_con}") final_status = f"{Color.GREEN}{Color.BOLD}ALL CHECKS PASSED{Color.RESET}" if all_passed else f"{Color.RED}{Color.BOLD}SOME CHECKS FAILED{Color.RESET}" print(f"\n Overall result: {final_status}") print(Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET + "\n") return all_passed def main(): # Determine target (optimized) output directory if len(sys.argv) > 1: target_dir = sys.argv[1] else: script_dir = os.path.dirname(os.path.abspath(__file__)) target_dir = os.path.join(script_dir, "GW150914/AMSS_NCKU_output") # Determine reference (baseline) directory script_dir = os.path.dirname(os.path.abspath(__file__)) reference_dir = os.path.join(script_dir, "GW150914-origin/AMSS_NCKU_output") # Data file paths bh_file_ref = os.path.join(reference_dir, "bssn_BH.dat") bh_file_target = os.path.join(target_dir, "bssn_BH.dat") constraint_file = os.path.join(target_dir, "bssn_constraint.dat") # Check if files exist if not os.path.exists(bh_file_ref): print(f"{Color.RED}{Color.BOLD}Error:{Color.RESET} Baseline trajectory file not found: {bh_file_ref}") sys.exit(1) if not os.path.exists(bh_file_target): print(f"{Color.RED}{Color.BOLD}Error:{Color.RESET} Target trajectory file not found: {bh_file_target}") sys.exit(1) if not os.path.exists(constraint_file): print(f"{Color.RED}{Color.BOLD}Error:{Color.RESET} Constraint data file not found: {constraint_file}") sys.exit(1) # Print header print_header() print(f"\n{Color.BOLD}Reference (Baseline):{Color.RESET} {Color.BLUE}{reference_dir}{Color.RESET}") print(f"{Color.BOLD}Target (Optimized): {Color.RESET} {Color.BLUE}{target_dir}{Color.RESET}") # Load data bh_data_ref = load_bh_trajectory(bh_file_ref) bh_data_target = load_bh_trajectory(bh_file_target) constraint_data = load_constraint_data(constraint_file) # Calculate RMS error rms_rel, error = calculate_rms_error(bh_data_ref, bh_data_target) rms_passed = print_rms_results(rms_rel, error) # Analyze constraint violation constraint_results = analyze_constraint_violation(constraint_data) constraint_passed = print_constraint_results(constraint_results) # Print summary all_passed = print_summary(rms_passed, constraint_passed) # Return exit code sys.exit(0 if all_passed else 1) if __name__ == "__main__": main()