RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
radcluster_code
digital_twin
verification
make_M_latex.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
"""make_M_latex.py — LaTeX table bodies for campaign M.
3
4
Writes docs/Formulation/verification_campaign/tables/M*.tex, which campaign_results.tex \\input{}s.
5
Generated rather than transcribed: this report has already had one table carry
6
a fabricated N_eq (28006 for a run whose value was 8006) because a number was
7
filled in from memory when the tool left a blank.
8
9
Every table refuses to print a metric for a run that missed its comparison dose
10
(plan S3.4.1); such a cell prints \\dnr instead.
11
"""
12
from
__future__
import
annotations
13
14
import
json
15
import
sys
16
from
pathlib
import
Path
17
18
HERE = Path(__file__).resolve().parent
19
DT = HERE.parent
20
MOD = DT.parent
21
REPO = MOD.parent
22
for
p
in
(str(REPO), str(DT), str(HERE)):
23
if
p
not
in
sys.path:
24
sys.path.insert(0, p)
25
26
import
runs
as
M
# noqa: E402
27
28
SYNC = HERE /
".sync"
/
"claims"
29
OUT = REPO /
"docs"
/
"Formulation"
/
"verification_campaign"
/
"tables"
30
DOSE =
"20"
31
32
33
def
board
():
34
out = {}
35
for
f
in
sorted(SYNC.glob(
"*.json"
)):
36
try
:
37
j = json.loads(f.read_text(encoding=
"utf-8"
))
38
out[j.get(
"run_id"
, f.stem)] = j
39
except
Exception:
40
pass
41
return
out
42
43
44
def
cell
(rec):
45
if
not
rec
or
rec.get(
"status"
) !=
"done"
:
46
return
None
47
v = (rec.get(
"scored"
)
or
{}).get(DOSE)
48
return
None
if
(
not
v
or
v.get(
"missing"
)
or
v.get(
"off_grid"
))
else
v
49
50
51
def
why_blank
(rec):
52
"""WHY a cell is empty -- the three reasons are not the same thing.
53
54
A run that timed out short of the comparison dose is a cost result; a
55
configuration the bin layout cannot realise is a statement about the grid,
56
not about cost; and a cell never attempted is neither. Printing one symbol
57
for all three would let a reader read the M2D grid's expensive corner as if
58
the method had failed there.
59
"""
60
if
rec
is
None
:
61
return
r"\dnr"
62
if
rec.get(
"status"
) ==
"failed"
:
63
return
r"\nreal"
64
return
r"\dnr"
65
66
67
def
w
(name, lines):
68
OUT.mkdir(parents=
True
, exist_ok=
True
)
69
(OUT / name).write_text(
"\n"
.join(lines) +
"\n"
, encoding=
"utf-8"
)
70
print(f
" wrote docs/Formulation/verification_campaign/tables/{name}"
)
71
72
73
def
main
():
74
B =
board
()
75
ref =
cell
(B.get(M.M_REF_RUN))
76
77
# ── M1: the cost cliff ──────────────────────────────────────────────────
78
L = [
r"\begin{tabular}{lrrrr}"
,
r"\toprule"
,
79
r"Run & $I=V$ & $N_{\rm eq}$ & Dose reached & Wall \\"
,
r"\midrule"
]
80
# M1_LADDER is local to manifest(); enumerate from the manifest itself so
81
# the table cannot drift from the runs that exist.
82
ent = {x[
"run_id"
]: x
for
x
in
M.manifest()}
83
m1 = [x
for
x
in
M.manifest()
if
x[
"table"
]
in
(
"M1"
,
"M1REF"
)]
84
for
e
in
sorted(m1, key=
lambda
x: x[
"I"
]):
85
rid = e[
"run_id"
]
86
r = B.get(rid)
87
if
not
r:
88
continue
89
wall = r.get(
"wall_s"
)
90
name = rid.replace(
"_"
, chr(92) +
"_"
)
91
if
not
wall:
92
# Claimed but never published -- the ladder was stopped by hand once
93
# 2000 had failed, because 4000 and up cannot succeed and each costs
94
# an hour to confirm it. Say that, rather than printing an empty
95
# row a reader would take for a missing measurement.
96
L.append(f
"{name} & {e['I']} & --- & \\multicolumn{{2}}{{c}}"
97
f
"{{\\itshape not run --- see text}} \\\\"
)
98
continue
99
reach = r.get(
"dose_reached"
, float(
"nan"
))
100
bold =
r"\bfseries "
if
reach >= 0.99 * e[
"dose"
]
else
""
101
L.append(f
"{bold}{name} & {e['I']} "
102
f
"& {r.get('N_eq', '---')} "
103
f
"& {reach:.4g} / {e['dose']:g} "
104
f
"& {wall/60:.1f} min \\\\"
)
105
L += [
r"\bottomrule"
,
r"\end{tabular}"
]
106
w
(
"M1_cost.tex"
, L)
107
108
# ── M2R: one-dimensional ladder, deviations ─────────────────────────────
109
L = [
r"\begin{tabular}{lrrrrrrrr}"
,
r"\toprule"
,
110
r"Rung & $I_{\rm bin}$ & $r$ & $N_{\rm eq}$ & $N_{111}$ & $d_{111}$ "
111
r"& $N_{100}$ & $d_{100}$ & $d_{\rm cav}$ \\"
,
112
r"\midrule"
]
113
if
ref:
114
for
nb
in
M.M2R_BINS:
115
rec = B.get(f
"M2R_B{nb}"
)
116
v =
cell
(rec)
117
if
v
is
None
:
118
L.append(f
"B{nb} & {nb} & --- & --- & "
119
+
" & "
.join([
r"\dnr"
] * 5) +
r" \\"
)
120
continue
121
d = [(v[k] - ref[k]) / ref[k] * 100
for
k
in
122
(
"N_loops_111"
,
"d_111_nm"
,
"N_loops_100"
,
"d_100_nm"
,
123
"d_cavity_nm"
)]
124
L.append(f
"B{nb} & {rec.get('bin_I_bin', nb)} "
125
f
"& {rec.get('bin_r', float('nan')):.2f} "
126
f
"& {rec.get('N_eq', '---')} & "
127
+
" & "
.join(f
"{x:+.1f}"
for
x
in
d) +
r" \\"
)
128
L += [
r"\bottomrule"
,
r"\end{tabular}"
]
129
w
(
"M2R_dev.tex"
, L)
130
131
# ── M2D: the 2-D sweep, d_111 deviation ─────────────────────────────────
132
for
key, fname
in
((
"d_111_nm"
,
"M2D_d111.tex"
),
133
(
"d_cavity_nm"
,
"M2D_dcav.tex"
)):
134
L = [
r"\begin{tabular}{l"
+
"r"
* len(M.M2D_BINS) +
"}"
,
r"\toprule"
,
135
r"$i_{\rm discrete}$ & "
136
+
" & "
.join(f
"$I_{{\\rm bin}}={nb}$"
for
nb
in
M.M2D_BINS)
137
+
r" \\"
,
r"\midrule"
]
138
for
i_d
in
M.M2D_IDISC:
139
row = [f
"{i_d}"
]
140
for
nb
in
M.M2D_BINS:
141
v =
cell
(B.get(f
"M2D_I{i_d}_B{nb}"
))
142
if
v
is
None
or
not
ref
or
not
ref[key]:
143
row.append(
why_blank
(B.get(f
"M2D_I{i_d}_B{nb}"
)))
144
else
:
145
row.append(f
"{(v[key]-ref[key])/ref[key]*100:+.1f}"
)
146
L.append(
" & "
.join(row) +
r" \\"
)
147
L += [
r"\bottomrule"
,
r"\end{tabular}"
]
148
w
(fname, L)
149
return
0
150
151
152
if
__name__ ==
"__main__"
:
153
sys.exit(
main
())
make_M_latex.main
main()
Definition
make_M_latex.py:73
make_M_latex.board
board()
Definition
make_M_latex.py:33
make_M_latex.cell
cell(rec)
Definition
make_M_latex.py:44
make_M_latex.w
w(name, lines)
Definition
make_M_latex.py:67
make_M_latex.why_blank
why_blank(rec)
Definition
make_M_latex.py:51
Generated by
1.9.8