251 lines
9.0 KiB
Python
251 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
AMSS-NCKU GW150914 Simulation Regression Test Script (Comprehensive Version)
|
||
|
||
Verification Requirements:
|
||
1. RMS errors < 1% for:
|
||
- 3D Vector Total RMS
|
||
- X Component RMS
|
||
- Y Component RMS
|
||
- Z Component RMS
|
||
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_all_rms_errors(bh_data_ref, bh_data_target):
|
||
"""
|
||
Calculate 3D Vector RMS and component-wise RMS (X, Y, Z) independently.
|
||
Uses r = sqrt(x^2 + y^2) as the denominator for all error normalizations.
|
||
Returns the maximum error between BH1 and BH2 for each category.
|
||
"""
|
||
M = min(len(bh_data_ref['time']), len(bh_data_target['time']))
|
||
|
||
if M < 10:
|
||
return None, "Insufficient data points for comparison"
|
||
|
||
results = {}
|
||
|
||
for bh in ['1', '2']:
|
||
x_r, y_r, z_r = bh_data_ref[f'x{bh}'][:M], bh_data_ref[f'y{bh}'][:M], bh_data_ref[f'z{bh}'][:M]
|
||
x_n, y_n, z_n = bh_data_target[f'x{bh}'][:M], bh_data_target[f'y{bh}'][:M], bh_data_target[f'z{bh}'][:M]
|
||
|
||
# 核心修改:根据组委会的邮件指示,分母统一使用 r = sqrt(x^2 + y^2)
|
||
r_ref = np.sqrt(x_r**2 + y_r**2)
|
||
r_new = np.sqrt(x_n**2 + y_n**2)
|
||
denom_max = np.maximum(r_ref, r_new)
|
||
|
||
valid = denom_max > 1e-15
|
||
if np.sum(valid) < 10:
|
||
results[f'BH{bh}'] = { '3D_Vector': 0.0, 'X_Component': 0.0, 'Y_Component': 0.0, 'Z_Component': 0.0 }
|
||
continue
|
||
|
||
def calc_rms(delta):
|
||
# 将对应分量的偏差除以统一的轨道半径分母 denom_max
|
||
return np.sqrt(np.mean((delta[valid] / denom_max[valid])**2)) * 100
|
||
|
||
# 1. Total 3D Vector RMS
|
||
delta_vec = np.sqrt((x_r - x_n)**2 + (y_r - y_n)**2 + (z_r - z_n)**2)
|
||
rms_3d = calc_rms(delta_vec)
|
||
|
||
# 2. Component-wise RMS (分离计算各轴,但共用半径分母)
|
||
rms_x = calc_rms(np.abs(x_r - x_n))
|
||
rms_y = calc_rms(np.abs(y_r - y_n))
|
||
rms_z = calc_rms(np.abs(z_r - z_n))
|
||
|
||
results[f'BH{bh}'] = {
|
||
'3D_Vector': rms_3d,
|
||
'X_Component': rms_x,
|
||
'Y_Component': rms_y,
|
||
'Z_Component': rms_z
|
||
}
|
||
|
||
# 获取 BH1 和 BH2 中的最大误差
|
||
max_rms = {
|
||
'3D_Vector': max(results['BH1']['3D_Vector'], results['BH2']['3D_Vector']),
|
||
'X_Component': max(results['BH1']['X_Component'], results['BH2']['X_Component']),
|
||
'Y_Component': max(results['BH1']['Y_Component'], results['BH2']['Y_Component']),
|
||
'Z_Component': max(results['BH1']['Z_Component'], results['BH2']['Z_Component'])
|
||
}
|
||
|
||
return max_rms, 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("\n" + Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET)
|
||
print(Color.BOLD + " AMSS-NCKU GW150914 Comprehensive Regression Test" + Color.RESET)
|
||
print(Color.BLUE + Color.BOLD + "=" * 65 + Color.RESET)
|
||
|
||
def print_rms_results(rms_dict, error, threshold=1.0):
|
||
print(f"\n{Color.BOLD}1. RMS Error Analysis (Maximums of BH1 & BH2){Color.RESET}")
|
||
print("-" * 65)
|
||
|
||
if error:
|
||
print(f" {Color.RED}Error: {error}{Color.RESET}")
|
||
return False
|
||
|
||
all_passed = True
|
||
print(f" Requirement: < {threshold}%\n")
|
||
|
||
for key, val in rms_dict.items():
|
||
passed = val < threshold
|
||
all_passed = all_passed and passed
|
||
status = get_status_text(passed)
|
||
print(f" {key:15}: {val:8.4f}% | Status: {status}")
|
||
|
||
return all_passed
|
||
|
||
def print_constraint_results(results, threshold=2.0):
|
||
print(f"\n{Color.BOLD}2. ADM Constraint Violation Analysis (Grid Level 0){Color.RESET}")
|
||
print("-" * 65)
|
||
|
||
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("\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] Comprehensive RMS 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():
|
||
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")
|
||
|
||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||
reference_dir = os.path.join(script_dir, "GW150914-origin/AMSS_NCKU_output")
|
||
|
||
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")
|
||
|
||
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(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}")
|
||
|
||
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)
|
||
|
||
# Output modified RMS results
|
||
rms_dict, error = calculate_all_rms_errors(bh_data_ref, bh_data_target)
|
||
rms_passed = print_rms_results(rms_dict, error)
|
||
|
||
# Output constraint results
|
||
constraint_results = analyze_constraint_violation(constraint_data)
|
||
constraint_passed = print_constraint_results(constraint_results)
|
||
|
||
all_passed = print_summary(rms_passed, constraint_passed)
|
||
sys.exit(0 if all_passed else 1)
|
||
|
||
if __name__ == "__main__":
|
||
main()
|