yushuang88's picture
Upload folder using huggingface_hub
eae86f2 verified
Raw History Blame Contribute Delete
6.25 kB
#!/usr/bin/env python3
"""Scientific field, error and convergence plots from actual run artifacts."""
import argparse
import csv
import json
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
from scripts.common import load_config, atomic_json
def save(fig, directory, stem):
for ext in ('png', 'pdf'):
fig.savefig(directory / f'{stem}.{ext}', dpi=300, bbox_inches='tight')
plt.close(fig)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--config', default='config/config.yaml')
args = parser.parse_args()
cfg, root = load_config(args.config)
results = root / cfg['paths']['results']
metrics = json.loads((results / 'metrics.json').read_text())
manifest = []
for label in metrics['checkpoints']:
data = np.load(results / f'predictions_{label}.npz')
xy, pred, ref = data['xy'], data['prediction_uvp'], data['reference_uvp']
tri = mtri.Triangulation(xy[:, 0], xy[:, 1])
fig, axes = plt.subplots(3, 1, figsize=(15, 5.5), constrained_layout=True)
lower, upper = min(ref[:, 0].min(), pred[:, 0].min()), max(ref[:, 0].max(), pred[:, 0].max())
values = [ref[:, 0], pred[:, 0], abs(pred[:, 0] - ref[:, 0])]
titles = ['SU2 reference u', f'Adaptive pseudo-time PINN u ({label}, step {int(data["checkpoint_step"])})', '|u_PINN - u_SU2|']
for i, (ax, val, title) in enumerate(zip(axes, values, titles)):
kwargs = dict(cmap='viridis', shading='gouraud')
if i < 2:
kwargs.update(vmin=lower, vmax=upper)
c = ax.tripcolor(tri, val, **kwargs)
fig.colorbar(c, ax=ax, pad=0.01)
ax.set(xlim=(0, 15), ylim=(-.5, .5), xlabel='x / H', ylabel='y / H', title=title)
ax.set_aspect('equal')
fig.suptitle(f'Re = 800; relative L2(u) = {metrics["checkpoints"][label]["relative_l2_u"]:.6g}')
save(fig, results, f'velocity_u_{label}')
manifest += [f'velocity_u_{label}.png', f'velocity_u_{label}.pdf']
fig, axes = plt.subplots(2, 1, figsize=(15, 4), constrained_layout=True)
for ax, val, title, cmap in [(axes[0], np.linalg.norm(pred[:, :2], axis=1), 'Predicted speed', 'viridis'),
(axes[1], pred[:, 2], 'Predicted pressure', 'coolwarm')]:
c = ax.tripcolor(tri, val, cmap=cmap, shading='gouraud')
fig.colorbar(c, ax=ax, pad=.01)
ax.set(xlabel='x / H', ylabel='y / H', title=title, xlim=(0, 15), ylim=(-.5, .5))
ax.set_aspect('equal')
save(fig, results, f'speed_pressure_{label}')
manifest += [f'speed_pressure_{label}.png', f'speed_pressure_{label}.pdf']
rows = list(csv.DictReader((results / 'training_history.csv').open()))
def series(key):
valid = [r for r in rows if r.get(key, '')]
return np.array([float(r['step']) for r in valid]), np.array([float(r[key]) for r in valid])
fig, axes = plt.subplots(2, 2, figsize=(11, 7), constrained_layout=True)
x, y = series('loss')
axes[0, 0].semilogy(x, y, label='Weighted relaxed loss')
for key in ('raw_u_res', 'raw_v_res', 'raw_p_res'):
x, y = series(key)
axes[0, 0].semilogy(x, y, label=key)
axes[0, 0].legend(fontsize=8)
x, y = series('relative_l2_u')
axes[0, 1].semilogy(x, y, label='Actual SU2 relative L2(u)')
axes[0, 1].axhline(cfg['evaluation']['paper_relative_l2_u'], color='gray', linestyle='--', label='Table 1: 2.32e-3')
axes[0, 1].legend(fontsize=8)
for key in ('tau_u', 'tau_v', 'tau_p'):
x, y = series(key)
axes[1, 0].semilogy(x, y, label=key)
axes[1, 0].legend()
x, y = series('learning_rate')
axes[1, 1].plot(x, y)
axes[1, 1].set_ylabel('Learning rate')
for ax in axes.flat:
ax.set_xlabel('Actual optimizer iterations')
ax.grid(alpha=.2)
save(fig, results, 'training_curves')
manifest += ['training_curves.png', 'training_curves.pdf']
summary = json.loads((results / 'training_summary.json').read_text())
report = ['# BFS Re=800 formal reproduction', '',
f'Actual completed steps: {summary["completed_steps"]} / {summary["requested_steps"]}.',
f'Slurm job: {summary["slurm_job_id"]}; host: {summary["host"]}.',
f'Elapsed training seconds: {summary["elapsed_seconds"]:.1f}.', '',
'| Selection | Checkpoint step | Relative L2(u) |', '|---|---:|---:|']
for key, value in metrics['checkpoints'].items():
report.append(f'| {key} | {value["checkpoint_step"]} | {value["relative_l2_u"]:.8g} |')
report += ['', 'Paper Table 1 adaptive value: 0.00232. This is a comparison target, not a measured result.',
'Training used only analytic boundary conditions and resampled PDE collocation points.',
'The SU2 reference was used only for reporting. Checkpoint selection used an independent unlabeled physics validation set.',
'Full parameter provenance and paper/repository conflicts are recorded in the reproduction specification.',
'', 'Figures:', ''] + [f'- {f}' for f in manifest if f.endswith('.png')]
error_history = results / 'error_history.jsonl'
if error_history.exists():
report += ['', 'Execution errors and repairs:', '']
for line in error_history.read_text().splitlines():
event = json.loads(line)
report.append(f'- Job {event["job_id"]}, actual formal steps {event["completed_steps"]}: '
f'{event["error"]}. Repair: {event["repair"]}.')
(results / 'report.md').write_text('\n'.join(report) + '\n')
atomic_json(results / 'visualization_manifest.json', {'status': 'completed', 'figures': manifest,
'reference': metrics['reference'], 'dpi': 300, 'interpolation': 'triangulation only for display; metrics use original nodes'})
print(json.dumps({'event': 'visualization_completed', 'figures': manifest, 'report': str(results / 'report.md')}), flush=True)
if __name__ == '__main__':
main()