RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
score_six_targets.py
Go to the documentation of this file.
1"""Score a results .jsonl against the six-observable target set.
2
3Usage: python score_six_targets.py results/S8_15dpa.jsonl [design/S8_labels.json]
4
5The six are N_100, d_100, N_111, d_111, N_voids, d_cavity at EUROFER97
6330 C / 15 dpa (targets_330C_15dpa.json). f_100 is NOT scored as an
7independent observable -- it is N_100/(N_100+N_111), so it is reported as a
8derived check only.
9
10A row "passes" an observable when the model value lies inside [lo, hi]. The
11headline number is how many of the six are in range; ratio-to-target is shown
12so a near-miss is distinguishable from a three-order-of-magnitude miss.
13
14Rows that did not reach the target dose, or that piled at the grid ceiling,
15are marked -- their size readouts are not measurements and must not be scored
16as if they were.
17"""
18import sys, json, math
19from pathlib import Path
20sys.stdout.reconfigure(encoding='utf-8', errors='replace')
21
22HERE = Path(__file__).resolve().parent
23TGT = json.load(open(HERE / 'targets_330C_15dpa.json'))
24OBS = TGT['observables']
25ORDER = ['N_loops_100', 'd_100_nm', 'N_loops_111', 'd_111_nm', 'N_voids', 'd_cavity_nm']
26SHORT = {'N_loops_100': 'N_100', 'd_100_nm': 'd_100', 'N_loops_111': 'N_111',
27 'd_111_nm': 'd_111', 'N_voids': 'N_void', 'd_cavity_nm': 'd_void'}
28
29res = Path(sys.argv[1]) if len(sys.argv) > 1 else HERE / 'results/S8_15dpa.jsonl'
30labf = Path(sys.argv[2]) if len(sys.argv) > 2 else res.parent.parent / 'design/S8_labels.json'
31labels = json.load(open(labf)) if labf.exists() else {}
32rows = sorted((json.loads(l) for l in open(res)), key=lambda r: r['row_id'])
33
34print('target set: %s %s C / %s dpa (%s)' % (
35 TGT['condition']['material'], TGT['condition']['T_C'],
36 TGT['condition']['dose_dpa'], TGT['condition']['irradiation']))
37print()
38hdr = '%-20s %6s' % ('row', 'dose') + ''.join('%12s' % SHORT[k] for k in ORDER) + '%7s %8s %6s' % ('in-rng', 'f_100', 'flags')
39print(hdr); print('-' * len(hdr))
40
41for r in rows:
42 lab = labels.get(str(r['row_id']), str(r['row_id']))
43 cells, n_ok = [], 0
44 for k in ORDER:
45 v = r.get(k)
46 o = OBS[k]
47 if v is None:
48 cells.append('%12s' % '-'); continue
49 ok = o['lo'] <= v <= o['hi']
50 n_ok += ok
51 rat = v / o['target'] if o['target'] else float('nan')
52 s = ('%.2e' % v) if v >= 1e4 else ('%.2f' % v)
53 cells.append('%12s' % (s + ('*' if ok else '')))
54 N1, N0 = r.get('N_loops_111'), r.get('N_loops_100')
55 f100 = N0 / (N0 + N1) if (N0 and N1) else float('nan')
56 flags = []
57 if r.get('grid_limited'):
58 flags.append('GRID')
59 if abs(r.get('dose_reached', 0) - TGT['condition']['dose_dpa']) > 0.02 * TGT['condition']['dose_dpa']:
60 flags.append('DOSE')
61 if r.get('starved'):
62 flags.append('STARV')
63 print('%-20s %6.3g' % (lab, r.get('dose_reached', 0)) + ''.join(cells)
64 + '%7s %8.3f %6s' % ('%d/6' % n_ok, f100, ','.join(flags) or '-'))
65
66print()
67print(' * = inside the experimental range. GRID = piled at the grid ceiling')
68print(' (size readout invalid). DOSE = did not reach 15 dpa (nothing is')
69print(' comparable). Ratios to target:')
70print()
71print('%-20s' % 'row' + ''.join('%12s' % SHORT[k] for k in ORDER))
72for r in rows:
73 lab = labels.get(str(r['row_id']), str(r['row_id']))
74 out = []
75 for k in ORDER:
76 v, t = r.get(k), OBS[k]['target']
77 out.append('%12s' % ('-' if v is None else '%.3gx' % (v / t)))
78 print('%-20s' % lab + ''.join(out))