nds-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -0
- package/bin/nds-mcp.js +33 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/constants.js.map +1 -0
- package/dist/db/chargeRadii.d.ts +22 -0
- package/dist/db/chargeRadii.d.ts.map +1 -0
- package/dist/db/chargeRadii.js +75 -0
- package/dist/db/chargeRadii.js.map +1 -0
- package/dist/db/decayFeedings.d.ts +31 -0
- package/dist/db/decayFeedings.d.ts.map +1 -0
- package/dist/db/decayFeedings.js +41 -0
- package/dist/db/decayFeedings.js.map +1 -0
- package/dist/db/ensureDb.d.ts +20 -0
- package/dist/db/ensureDb.d.ts.map +1 -0
- package/dist/db/ensureDb.js +148 -0
- package/dist/db/ensureDb.js.map +1 -0
- package/dist/db/gammas.d.ts +39 -0
- package/dist/db/gammas.d.ts.map +1 -0
- package/dist/db/gammas.js +53 -0
- package/dist/db/gammas.js.map +1 -0
- package/dist/db/levels.d.ts +68 -0
- package/dist/db/levels.d.ts.map +1 -0
- package/dist/db/levels.js +121 -0
- package/dist/db/levels.js.map +1 -0
- package/dist/db/masses.d.ts +16 -0
- package/dist/db/masses.d.ts.map +1 -0
- package/dist/db/masses.js +22 -0
- package/dist/db/masses.js.map +1 -0
- package/dist/db/ndsDb.d.ts +10 -0
- package/dist/db/ndsDb.d.ts.map +1 -0
- package/dist/db/ndsDb.js +61 -0
- package/dist/db/ndsDb.js.map +1 -0
- package/dist/db/nubase.d.ts +21 -0
- package/dist/db/nubase.d.ts.map +1 -0
- package/dist/db/nubase.js +61 -0
- package/dist/db/nubase.js.map +1 -0
- package/dist/db/reactions.d.ts +11 -0
- package/dist/db/reactions.d.ts.map +1 -0
- package/dist/db/reactions.js +54 -0
- package/dist/db/reactions.js.map +1 -0
- package/dist/db/references.d.ts +11 -0
- package/dist/db/references.d.ts.map +1 -0
- package/dist/db/references.js +24 -0
- package/dist/db/references.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/buildDb.d.ts +36 -0
- package/dist/ingest/buildDb.d.ts.map +1 -0
- package/dist/ingest/buildDb.js +933 -0
- package/dist/ingest/buildDb.js.map +1 -0
- package/dist/ingest/parseAme.d.ts +79 -0
- package/dist/ingest/parseAme.d.ts.map +1 -0
- package/dist/ingest/parseAme.js +186 -0
- package/dist/ingest/parseAme.js.map +1 -0
- package/dist/ingest/parseEnsdf.d.ts +210 -0
- package/dist/ingest/parseEnsdf.d.ts.map +1 -0
- package/dist/ingest/parseEnsdf.js +469 -0
- package/dist/ingest/parseEnsdf.js.map +1 -0
- package/dist/ingest/parseLaserRadii.d.ts +33 -0
- package/dist/ingest/parseLaserRadii.d.ts.map +1 -0
- package/dist/ingest/parseLaserRadii.js +210 -0
- package/dist/ingest/parseLaserRadii.js.map +1 -0
- package/dist/ingest/parseNubase.d.ts +40 -0
- package/dist/ingest/parseNubase.d.ts.map +1 -0
- package/dist/ingest/parseNubase.js +146 -0
- package/dist/ingest/parseNubase.js.map +1 -0
- package/dist/ingest/parseRadii.d.ts +17 -0
- package/dist/ingest/parseRadii.d.ts.map +1 -0
- package/dist/ingest/parseRadii.js +42 -0
- package/dist/ingest/parseRadii.js.map +1 -0
- package/dist/ingest/parseTunl.d.ts +48 -0
- package/dist/ingest/parseTunl.d.ts.map +1 -0
- package/dist/ingest/parseTunl.js +773 -0
- package/dist/ingest/parseTunl.js.map +1 -0
- package/dist/shared/errors.d.ts +20 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +45 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/sqlite3Cli.d.ts +3 -0
- package/dist/shared/sqlite3Cli.d.ts.map +1 -0
- package/dist/shared/sqlite3Cli.js +106 -0
- package/dist/shared/sqlite3Cli.js.map +1 -0
- package/dist/tooling.d.ts +2 -0
- package/dist/tooling.d.ts.map +1 -0
- package/dist/tooling.js +2 -0
- package/dist/tooling.js.map +1 -0
- package/dist/tools/dispatcher.d.ts +11 -0
- package/dist/tools/dispatcher.d.ts.map +1 -0
- package/dist/tools/dispatcher.js +64 -0
- package/dist/tools/dispatcher.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/mcpSchema.d.ts +3 -0
- package/dist/tools/mcpSchema.d.ts.map +1 -0
- package/dist/tools/mcpSchema.js +21 -0
- package/dist/tools/mcpSchema.js.map +1 -0
- package/dist/tools/registry.d.ts +22 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +309 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/utils/stdioHygiene.d.ts +7 -0
- package/dist/utils/stdioHygiene.d.ts.map +1 -0
- package/dist/utils/stdioHygiene.js +16 -0
- package/dist/utils/stdioHygiene.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build NDS SQLite database from raw data files.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* Full build: tsx src/ingest/buildDb.ts --data-dir /path/to/raw/ --output /path/to/nds.sqlite
|
|
6
|
+
* ENSDF only: tsx src/ingest/buildDb.ts --ensdf-only --db /path/to/nds.sqlite --ensdf-dir /path/to/ensdf/
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { execFileSync } from 'child_process';
|
|
11
|
+
import { parseAmeMasses, parseAmeRct1, parseAmeRct2 } from './parseAme.js';
|
|
12
|
+
import { parseNubase } from './parseNubase.js';
|
|
13
|
+
import { parseChargeRadii } from './parseRadii.js';
|
|
14
|
+
import { parseLaserRadii } from './parseLaserRadii.js';
|
|
15
|
+
import { parseTunlLevels } from './parseTunl.js';
|
|
16
|
+
import { splitIntoDatasets, classifyRecord, parseReferenceRecord, identifyDataset, parseLevelRecord, parseGammaRecord, parseBetaRecord, parseECRecord, parseParentRecord, extractQrefKeynumbers, extractTitleHalfLife, parseBTypeContinuation, parseSTypeContinuation, preprocessLine, } from './parseEnsdf.js';
|
|
17
|
+
function sqlEscape(value) {
|
|
18
|
+
return value.replace(/'/g, "''");
|
|
19
|
+
}
|
|
20
|
+
function sqlVal(v) {
|
|
21
|
+
if (v === null || v === undefined)
|
|
22
|
+
return 'NULL';
|
|
23
|
+
if (typeof v === 'boolean')
|
|
24
|
+
return v ? '1' : '0';
|
|
25
|
+
return String(v);
|
|
26
|
+
}
|
|
27
|
+
// ── Base schema (AME + NUBASE + charge radii) ──────────────────────────────
|
|
28
|
+
const BASE_SCHEMA_SQL = `
|
|
29
|
+
CREATE TABLE IF NOT EXISTS nds_meta (
|
|
30
|
+
key TEXT PRIMARY KEY,
|
|
31
|
+
value TEXT NOT NULL
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS ame_masses (
|
|
35
|
+
Z INTEGER NOT NULL,
|
|
36
|
+
A INTEGER NOT NULL,
|
|
37
|
+
element TEXT NOT NULL,
|
|
38
|
+
mass_excess_keV REAL,
|
|
39
|
+
mass_excess_unc_keV REAL,
|
|
40
|
+
binding_energy_per_A_keV REAL,
|
|
41
|
+
binding_energy_per_A_unc_keV REAL,
|
|
42
|
+
beta_decay_energy_keV REAL,
|
|
43
|
+
beta_decay_energy_unc_keV REAL,
|
|
44
|
+
atomic_mass_micro_u REAL,
|
|
45
|
+
atomic_mass_unc_micro_u REAL,
|
|
46
|
+
is_estimated INTEGER DEFAULT 0,
|
|
47
|
+
PRIMARY KEY (Z, A)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
CREATE TABLE IF NOT EXISTS ame_reactions (
|
|
51
|
+
Z INTEGER NOT NULL,
|
|
52
|
+
A INTEGER NOT NULL,
|
|
53
|
+
element TEXT NOT NULL,
|
|
54
|
+
S2n_keV REAL, S2n_unc_keV REAL,
|
|
55
|
+
S2p_keV REAL, S2p_unc_keV REAL,
|
|
56
|
+
Qa_keV REAL, Qa_unc_keV REAL,
|
|
57
|
+
Q2bm_keV REAL, Q2bm_unc_keV REAL,
|
|
58
|
+
Qep_keV REAL, Qep_unc_keV REAL,
|
|
59
|
+
Qbn_keV REAL, Qbn_unc_keV REAL,
|
|
60
|
+
Sn_keV REAL, Sn_unc_keV REAL,
|
|
61
|
+
Sp_keV REAL, Sp_unc_keV REAL,
|
|
62
|
+
Q4bm_keV REAL, Q4bm_unc_keV REAL,
|
|
63
|
+
Qda_keV REAL, Qda_unc_keV REAL,
|
|
64
|
+
Qpa_keV REAL, Qpa_unc_keV REAL,
|
|
65
|
+
Qna_keV REAL, Qna_unc_keV REAL,
|
|
66
|
+
PRIMARY KEY (Z, A)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
CREATE TABLE IF NOT EXISTS nubase (
|
|
70
|
+
Z INTEGER NOT NULL,
|
|
71
|
+
A INTEGER NOT NULL,
|
|
72
|
+
element TEXT NOT NULL,
|
|
73
|
+
isomer_index INTEGER DEFAULT 0,
|
|
74
|
+
mass_excess_keV REAL,
|
|
75
|
+
mass_excess_unc_keV REAL,
|
|
76
|
+
excitation_energy_keV REAL,
|
|
77
|
+
half_life TEXT,
|
|
78
|
+
half_life_seconds REAL,
|
|
79
|
+
half_life_unc_seconds REAL,
|
|
80
|
+
spin_parity TEXT,
|
|
81
|
+
decay_modes TEXT,
|
|
82
|
+
is_estimated INTEGER DEFAULT 0,
|
|
83
|
+
PRIMARY KEY (Z, A, isomer_index)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS charge_radii (
|
|
87
|
+
Z INTEGER NOT NULL,
|
|
88
|
+
A INTEGER NOT NULL,
|
|
89
|
+
element TEXT NOT NULL,
|
|
90
|
+
r_charge_fm REAL,
|
|
91
|
+
r_charge_unc_fm REAL,
|
|
92
|
+
r_charge_preliminary_fm REAL,
|
|
93
|
+
r_charge_preliminary_unc_fm REAL,
|
|
94
|
+
PRIMARY KEY (Z, A)
|
|
95
|
+
);
|
|
96
|
+
`;
|
|
97
|
+
// ── ENSDF schema (5 tables) ────────────────────────────────────────────────
|
|
98
|
+
const ENSDF_SCHEMA_SQL = `
|
|
99
|
+
CREATE TABLE IF NOT EXISTS nds_meta (
|
|
100
|
+
key TEXT PRIMARY KEY,
|
|
101
|
+
value TEXT NOT NULL
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
CREATE TABLE IF NOT EXISTS ensdf_references (
|
|
105
|
+
A INTEGER NOT NULL,
|
|
106
|
+
keynumber TEXT NOT NULL,
|
|
107
|
+
type TEXT,
|
|
108
|
+
reference TEXT,
|
|
109
|
+
PRIMARY KEY (A, keynumber)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS ensdf_datasets (
|
|
113
|
+
dataset_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
114
|
+
Z INTEGER NOT NULL,
|
|
115
|
+
A INTEGER NOT NULL,
|
|
116
|
+
element TEXT NOT NULL,
|
|
117
|
+
dataset_type TEXT NOT NULL,
|
|
118
|
+
dsid TEXT NOT NULL,
|
|
119
|
+
parent_z INTEGER,
|
|
120
|
+
parent_a INTEGER,
|
|
121
|
+
parent_element TEXT,
|
|
122
|
+
parent_half_life TEXT,
|
|
123
|
+
qref_keynumbers TEXT,
|
|
124
|
+
qref_raw TEXT
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE TABLE IF NOT EXISTS ensdf_levels (
|
|
128
|
+
level_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
|
+
dataset_id INTEGER NOT NULL REFERENCES ensdf_datasets(dataset_id),
|
|
130
|
+
Z INTEGER NOT NULL,
|
|
131
|
+
A INTEGER NOT NULL,
|
|
132
|
+
element TEXT NOT NULL,
|
|
133
|
+
energy_keV REAL NOT NULL,
|
|
134
|
+
energy_raw TEXT NOT NULL,
|
|
135
|
+
energy_unc_keV REAL,
|
|
136
|
+
spin_parity TEXT,
|
|
137
|
+
half_life TEXT,
|
|
138
|
+
half_life_seconds REAL,
|
|
139
|
+
half_life_unc_seconds REAL,
|
|
140
|
+
isomer_flag TEXT,
|
|
141
|
+
questionable INTEGER DEFAULT 0,
|
|
142
|
+
comment_flag TEXT
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
CREATE TABLE IF NOT EXISTS ensdf_gammas (
|
|
146
|
+
gamma_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
147
|
+
dataset_id INTEGER NOT NULL REFERENCES ensdf_datasets(dataset_id),
|
|
148
|
+
level_id INTEGER NOT NULL REFERENCES ensdf_levels(level_id),
|
|
149
|
+
Z INTEGER NOT NULL,
|
|
150
|
+
A INTEGER NOT NULL,
|
|
151
|
+
element TEXT NOT NULL,
|
|
152
|
+
level_energy_keV REAL NOT NULL,
|
|
153
|
+
gamma_energy_keV REAL NOT NULL,
|
|
154
|
+
gamma_energy_raw TEXT NOT NULL,
|
|
155
|
+
gamma_energy_unc_keV REAL,
|
|
156
|
+
rel_intensity REAL,
|
|
157
|
+
rel_intensity_unc REAL,
|
|
158
|
+
total_intensity REAL,
|
|
159
|
+
total_intensity_unc REAL,
|
|
160
|
+
multipolarity TEXT,
|
|
161
|
+
mixing_ratio REAL,
|
|
162
|
+
mixing_ratio_unc REAL,
|
|
163
|
+
total_conv_coeff REAL,
|
|
164
|
+
total_conv_coeff_unc REAL,
|
|
165
|
+
comment_flag TEXT,
|
|
166
|
+
coin_flag TEXT,
|
|
167
|
+
questionable INTEGER DEFAULT 0,
|
|
168
|
+
be2w REAL,
|
|
169
|
+
be2w_unc REAL,
|
|
170
|
+
bm1w REAL,
|
|
171
|
+
bm1w_unc REAL
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
CREATE TABLE IF NOT EXISTS ensdf_decay_feedings (
|
|
175
|
+
feeding_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
176
|
+
dataset_id INTEGER NOT NULL REFERENCES ensdf_datasets(dataset_id),
|
|
177
|
+
parent_Z INTEGER NOT NULL,
|
|
178
|
+
parent_A INTEGER NOT NULL,
|
|
179
|
+
parent_element TEXT NOT NULL,
|
|
180
|
+
decay_mode TEXT NOT NULL,
|
|
181
|
+
daughter_level_keV REAL,
|
|
182
|
+
daughter_level_id INTEGER REFERENCES ensdf_levels(level_id),
|
|
183
|
+
ib_percent REAL,
|
|
184
|
+
ib_percent_unc REAL,
|
|
185
|
+
ie_percent REAL,
|
|
186
|
+
ie_percent_unc REAL,
|
|
187
|
+
ti_percent REAL,
|
|
188
|
+
ti_percent_unc REAL,
|
|
189
|
+
log_ft REAL,
|
|
190
|
+
log_ft_unc REAL,
|
|
191
|
+
endpoint_keV REAL,
|
|
192
|
+
endpoint_unc_keV REAL,
|
|
193
|
+
forbiddenness TEXT,
|
|
194
|
+
comment_flag TEXT
|
|
195
|
+
);
|
|
196
|
+
`;
|
|
197
|
+
// ── Laser spectroscopy radii schema (Li et al. 2021) ────────────────────────
|
|
198
|
+
const LASER_RADII_SCHEMA_SQL = `
|
|
199
|
+
CREATE TABLE IF NOT EXISTS laser_radii (
|
|
200
|
+
Z INTEGER NOT NULL,
|
|
201
|
+
A INTEGER NOT NULL,
|
|
202
|
+
N INTEGER NOT NULL,
|
|
203
|
+
element TEXT NOT NULL,
|
|
204
|
+
delta_r2_fm2 REAL NOT NULL,
|
|
205
|
+
delta_r2_unc_fm2 REAL,
|
|
206
|
+
r_charge_fm REAL NOT NULL,
|
|
207
|
+
r_charge_unc_fm REAL NOT NULL,
|
|
208
|
+
is_reference INTEGER DEFAULT 0,
|
|
209
|
+
in_angeli_2013 INTEGER DEFAULT 0,
|
|
210
|
+
ref_A INTEGER NOT NULL,
|
|
211
|
+
PRIMARY KEY (Z, A)
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
CREATE TABLE IF NOT EXISTS laser_radii_refs (
|
|
215
|
+
Z INTEGER NOT NULL,
|
|
216
|
+
A INTEGER NOT NULL,
|
|
217
|
+
citekey TEXT NOT NULL,
|
|
218
|
+
reference TEXT NOT NULL,
|
|
219
|
+
PRIMARY KEY (Z, A, citekey),
|
|
220
|
+
FOREIGN KEY (Z, A) REFERENCES laser_radii(Z, A)
|
|
221
|
+
);
|
|
222
|
+
`;
|
|
223
|
+
const LASER_RADII_INDEX_SQL = `
|
|
224
|
+
CREATE INDEX IF NOT EXISTS idx_laser_radii_element ON laser_radii(element);
|
|
225
|
+
CREATE INDEX IF NOT EXISTS idx_laser_radii_refs_za ON laser_radii_refs(Z, A);
|
|
226
|
+
`;
|
|
227
|
+
// ── TUNL energy levels schema (light nuclei A ≤ 20) ─────────────────────────
|
|
228
|
+
const TUNL_SCHEMA_SQL = `
|
|
229
|
+
CREATE TABLE IF NOT EXISTS tunl_levels (
|
|
230
|
+
tunl_level_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
231
|
+
Z INTEGER NOT NULL,
|
|
232
|
+
A INTEGER NOT NULL,
|
|
233
|
+
element TEXT NOT NULL,
|
|
234
|
+
energy_keV REAL NOT NULL,
|
|
235
|
+
energy_unc_keV REAL,
|
|
236
|
+
energy_raw TEXT,
|
|
237
|
+
spin_parity TEXT,
|
|
238
|
+
isospin TEXT,
|
|
239
|
+
width_keV REAL,
|
|
240
|
+
width_unc_keV REAL,
|
|
241
|
+
width_raw TEXT,
|
|
242
|
+
width_relation TEXT,
|
|
243
|
+
half_life TEXT,
|
|
244
|
+
decay_modes TEXT,
|
|
245
|
+
evaluation TEXT NOT NULL,
|
|
246
|
+
table_label TEXT
|
|
247
|
+
);
|
|
248
|
+
`;
|
|
249
|
+
const TUNL_INDEX_SQL = `
|
|
250
|
+
CREATE INDEX IF NOT EXISTS idx_tunl_levels_za_energy ON tunl_levels(Z, A, energy_keV);
|
|
251
|
+
CREATE INDEX IF NOT EXISTS idx_tunl_levels_element ON tunl_levels(element);
|
|
252
|
+
CREATE INDEX IF NOT EXISTS idx_tunl_levels_jpi ON tunl_levels(spin_parity);
|
|
253
|
+
CREATE INDEX IF NOT EXISTS idx_tunl_levels_isospin ON tunl_levels(isospin);
|
|
254
|
+
`;
|
|
255
|
+
// ── Combined constants for full rebuild ─────────────────────────────────────
|
|
256
|
+
const SCHEMA_SQL = BASE_SCHEMA_SQL + ENSDF_SCHEMA_SQL + LASER_RADII_SCHEMA_SQL + TUNL_SCHEMA_SQL;
|
|
257
|
+
// ── Indexes ─────────────────────────────────────────────────────────────────
|
|
258
|
+
const BASE_INDEX_SQL = `
|
|
259
|
+
CREATE INDEX IF NOT EXISTS idx_ame_masses_element ON ame_masses(element);
|
|
260
|
+
CREATE INDEX IF NOT EXISTS idx_ame_reactions_element ON ame_reactions(element);
|
|
261
|
+
CREATE INDEX IF NOT EXISTS idx_nubase_element ON nubase(element);
|
|
262
|
+
CREATE INDEX IF NOT EXISTS idx_nubase_half_life ON nubase(half_life_seconds);
|
|
263
|
+
CREATE INDEX IF NOT EXISTS idx_nubase_mass_excess ON nubase(mass_excess_keV);
|
|
264
|
+
CREATE INDEX IF NOT EXISTS idx_charge_radii_element ON charge_radii(element);
|
|
265
|
+
`;
|
|
266
|
+
const ENSDF_INDEX_SQL = `
|
|
267
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_references_keynumber ON ensdf_references(keynumber);
|
|
268
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_datasets_za ON ensdf_datasets(Z, A);
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_datasets_type ON ensdf_datasets(dataset_type);
|
|
270
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_levels_za ON ensdf_levels(Z, A);
|
|
271
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_levels_dataset ON ensdf_levels(dataset_id);
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_levels_element ON ensdf_levels(element);
|
|
273
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_levels_energy ON ensdf_levels(energy_keV);
|
|
274
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_gammas_za ON ensdf_gammas(Z, A);
|
|
275
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_gammas_level ON ensdf_gammas(level_id);
|
|
276
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_gammas_level_energy ON ensdf_gammas(Z, A, level_energy_keV);
|
|
277
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_gammas_energy ON ensdf_gammas(gamma_energy_keV);
|
|
278
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_feedings_parent ON ensdf_decay_feedings(parent_Z, parent_A);
|
|
279
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_feedings_dataset ON ensdf_decay_feedings(dataset_id);
|
|
280
|
+
CREATE INDEX IF NOT EXISTS idx_ensdf_feedings_mode ON ensdf_decay_feedings(decay_mode);
|
|
281
|
+
`;
|
|
282
|
+
const INDEX_SQL = BASE_INDEX_SQL + ENSDF_INDEX_SQL + LASER_RADII_INDEX_SQL + TUNL_INDEX_SQL;
|
|
283
|
+
function executeSql(dbPath, sql) {
|
|
284
|
+
execFileSync('sqlite3', [dbPath], {
|
|
285
|
+
input: sql,
|
|
286
|
+
stdio: ['pipe', 'ignore', 'pipe'],
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// ── ENSDF ingestion ────────────────────────────────────────────────────────
|
|
290
|
+
const ENSDF_BATCH_SIZE = 5000;
|
|
291
|
+
function sqlStr(v) {
|
|
292
|
+
if (v === null)
|
|
293
|
+
return 'NULL';
|
|
294
|
+
return `'${sqlEscape(v)}'`;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Execute an INSERT and return the AUTOINCREMENT rowid in a single sqlite3 session.
|
|
298
|
+
* (last_insert_rowid() is connection-scoped; separate processes always return 0.)
|
|
299
|
+
*/
|
|
300
|
+
function insertAndGetRowid(dbPath, insertSql) {
|
|
301
|
+
const combined = `${insertSql}\nSELECT last_insert_rowid();`;
|
|
302
|
+
const result = execFileSync('sqlite3', [dbPath], {
|
|
303
|
+
input: combined,
|
|
304
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
305
|
+
encoding: 'utf-8',
|
|
306
|
+
});
|
|
307
|
+
return parseInt(result.trim(), 10);
|
|
308
|
+
}
|
|
309
|
+
function ingestEnsdfFiles(dbPath, ensdfDir) {
|
|
310
|
+
if (!fs.existsSync(ensdfDir)) {
|
|
311
|
+
console.error('ENSDF directory not found, skipping ENSDF ingestion');
|
|
312
|
+
return { references: 0, datasets: 0, levels: 0, gammas: 0, feedings: 0 };
|
|
313
|
+
}
|
|
314
|
+
// Idempotent: clear any previous ENSDF data (order respects FK constraints)
|
|
315
|
+
executeSql(dbPath, `
|
|
316
|
+
DELETE FROM ensdf_decay_feedings;
|
|
317
|
+
DELETE FROM ensdf_gammas;
|
|
318
|
+
DELETE FROM ensdf_levels;
|
|
319
|
+
DELETE FROM ensdf_datasets;
|
|
320
|
+
DELETE FROM ensdf_references;
|
|
321
|
+
`);
|
|
322
|
+
const files = fs.readdirSync(ensdfDir)
|
|
323
|
+
.filter(f => /^ensdf\.\d{3}$/.test(f))
|
|
324
|
+
.sort();
|
|
325
|
+
const counts = { references: 0, datasets: 0, levels: 0, gammas: 0, feedings: 0 };
|
|
326
|
+
for (const file of files) {
|
|
327
|
+
const filePath = path.join(ensdfDir, file);
|
|
328
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
329
|
+
const blocks = splitIntoDatasets(content);
|
|
330
|
+
// Extract mass number from filename
|
|
331
|
+
const massMatch = file.match(/ensdf\.(\d{3})/);
|
|
332
|
+
const massNumber = massMatch ? parseInt(massMatch[1], 10) : 0;
|
|
333
|
+
for (const block of blocks) {
|
|
334
|
+
const headerLine = block.headerLine;
|
|
335
|
+
// Check if this is a REFERENCES dataset
|
|
336
|
+
const dsidField = headerLine.substring(9, 39).trim();
|
|
337
|
+
if (dsidField === 'REFERENCES') {
|
|
338
|
+
const refStmts = [];
|
|
339
|
+
for (const line of block.lines) {
|
|
340
|
+
const processed = preprocessLine(line);
|
|
341
|
+
const rc = classifyRecord(processed);
|
|
342
|
+
if (rc === 'reference') {
|
|
343
|
+
const ref = parseReferenceRecord(processed, massNumber);
|
|
344
|
+
if (ref) {
|
|
345
|
+
refStmts.push(`INSERT OR IGNORE INTO ensdf_references VALUES(${ref.A},${sqlStr(ref.keynumber)},${sqlStr(ref.type)},${sqlStr(ref.reference)});`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (refStmts.length > 0) {
|
|
350
|
+
for (let i = 0; i < refStmts.length; i += ENSDF_BATCH_SIZE) {
|
|
351
|
+
const batch = refStmts.slice(i, i + ENSDF_BATCH_SIZE);
|
|
352
|
+
executeSql(dbPath, `BEGIN;\n${batch.join('\n')}\nCOMMIT;`);
|
|
353
|
+
}
|
|
354
|
+
counts.references += refStmts.length;
|
|
355
|
+
}
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
// Check if this is a COMMENTS dataset (col 6-9 area)
|
|
359
|
+
if (dsidField === 'COMMENTS' || dsidField === '')
|
|
360
|
+
continue;
|
|
361
|
+
// Identify dataset type
|
|
362
|
+
const dsInfo = identifyDataset(headerLine);
|
|
363
|
+
if (!dsInfo)
|
|
364
|
+
continue;
|
|
365
|
+
// Insert dataset record
|
|
366
|
+
const parentNucid = dsInfo.parentNucid;
|
|
367
|
+
const datasetId = insertAndGetRowid(dbPath, `INSERT INTO ensdf_datasets(Z,A,element,dataset_type,dsid,parent_z,parent_a,parent_element) VALUES(${dsInfo.nucid.Z},${dsInfo.nucid.A},${sqlStr(dsInfo.nucid.element)},${sqlStr(dsInfo.datasetType)},${sqlStr(dsInfo.dsid)},${sqlVal(parentNucid?.Z ?? null)},${sqlVal(parentNucid?.A ?? null)},${sqlStr(parentNucid?.element ?? null)});`);
|
|
368
|
+
counts.datasets++;
|
|
369
|
+
// Per-dataset state
|
|
370
|
+
let currentLevelId = null;
|
|
371
|
+
let currentLevelEnergy = null;
|
|
372
|
+
let currentGamma = null;
|
|
373
|
+
let currentGammaRecordType = '';
|
|
374
|
+
let parentHalfLife = extractTitleHalfLife(dsInfo.dsid);
|
|
375
|
+
const qrefKeynumbers = [];
|
|
376
|
+
let qrefRaw = '';
|
|
377
|
+
const gammaBuffer = [];
|
|
378
|
+
function flushGamma() {
|
|
379
|
+
if (currentGamma && currentGamma.gamma_energy_keV !== undefined) {
|
|
380
|
+
gammaBuffer.push(`INSERT INTO ensdf_gammas(dataset_id,level_id,Z,A,element,level_energy_keV,gamma_energy_keV,gamma_energy_raw,gamma_energy_unc_keV,rel_intensity,rel_intensity_unc,total_intensity,total_intensity_unc,multipolarity,mixing_ratio,mixing_ratio_unc,total_conv_coeff,total_conv_coeff_unc,comment_flag,coin_flag,questionable,be2w,be2w_unc,bm1w,bm1w_unc) VALUES(${datasetId},${sqlVal(currentGamma.level_id ?? null)},${currentGamma.Z},${currentGamma.A},${sqlStr(currentGamma.element ?? null)},${sqlVal(currentGamma.level_energy_keV ?? null)},${sqlVal(currentGamma.gamma_energy_keV ?? null)},${sqlStr(currentGamma.gamma_energy_raw ?? null)},${sqlVal(currentGamma.gamma_energy_unc_keV ?? null)},${sqlVal(currentGamma.rel_intensity ?? null)},${sqlVal(currentGamma.rel_intensity_unc ?? null)},${sqlVal(currentGamma.total_intensity ?? null)},${sqlVal(currentGamma.total_intensity_unc ?? null)},${sqlStr(currentGamma.multipolarity ?? null)},${sqlVal(currentGamma.mixing_ratio ?? null)},${sqlVal(currentGamma.mixing_ratio_unc ?? null)},${sqlVal(currentGamma.total_conv_coeff ?? null)},${sqlVal(currentGamma.total_conv_coeff_unc ?? null)},${sqlStr(currentGamma.comment_flag ?? null)},${sqlStr(currentGamma.coin_flag ?? null)},${sqlVal(currentGamma.questionable ?? 0)},${sqlVal(currentGamma.be2w ?? null)},${sqlVal(currentGamma.be2w_unc ?? null)},${sqlVal(currentGamma.bm1w ?? null)},${sqlVal(currentGamma.bm1w_unc ?? null)});`);
|
|
381
|
+
}
|
|
382
|
+
currentGamma = null;
|
|
383
|
+
}
|
|
384
|
+
// Process lines
|
|
385
|
+
for (let i = 1; i < block.lines.length; i++) {
|
|
386
|
+
const line = block.lines[i];
|
|
387
|
+
const col6 = line[5];
|
|
388
|
+
// Continuation records (col 6 !== ' ')
|
|
389
|
+
if (col6 !== ' ') {
|
|
390
|
+
if (col6 === 'B' && currentGammaRecordType === 'G' && currentGamma) {
|
|
391
|
+
const bData = parseBTypeContinuation(line);
|
|
392
|
+
if (bData.be2w !== null) {
|
|
393
|
+
currentGamma.be2w = bData.be2w;
|
|
394
|
+
currentGamma.be2w_unc = bData.be2w_unc;
|
|
395
|
+
}
|
|
396
|
+
if (bData.bm1w !== null) {
|
|
397
|
+
currentGamma.bm1w = bData.bm1w;
|
|
398
|
+
currentGamma.bm1w_unc = bData.bm1w_unc;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else if (col6 === 'S' && currentGammaRecordType === 'G' && currentGamma) {
|
|
402
|
+
const sData = parseSTypeContinuation(line);
|
|
403
|
+
if (sData.cc !== null && currentGamma.total_conv_coeff === null) {
|
|
404
|
+
currentGamma.total_conv_coeff = sData.cc;
|
|
405
|
+
currentGamma.total_conv_coeff_unc = sData.cc_unc;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// col 6 = '2'-'9' or other → skip
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
// Primary records (col 6 = ' ')
|
|
412
|
+
const rc = classifyRecord(line);
|
|
413
|
+
switch (rc) {
|
|
414
|
+
case 'comment':
|
|
415
|
+
case 'text':
|
|
416
|
+
case 'xref':
|
|
417
|
+
case 'history':
|
|
418
|
+
case 'normalization':
|
|
419
|
+
case 'unknown':
|
|
420
|
+
case 'delayed':
|
|
421
|
+
case 'alpha':
|
|
422
|
+
case 'header':
|
|
423
|
+
break;
|
|
424
|
+
case 'parent': {
|
|
425
|
+
const pData = parseParentRecord(line);
|
|
426
|
+
if (pData.halfLife) {
|
|
427
|
+
parentHalfLife = pData.halfLife;
|
|
428
|
+
}
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
case 'qvalue': {
|
|
432
|
+
const qData = extractQrefKeynumbers(line);
|
|
433
|
+
if (qData.keynumbers.length > 0) {
|
|
434
|
+
qrefKeynumbers.push(...qData.keynumbers);
|
|
435
|
+
}
|
|
436
|
+
if (qData.raw)
|
|
437
|
+
qrefRaw = qData.raw;
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case 'level': {
|
|
441
|
+
flushGamma();
|
|
442
|
+
const lData = parseLevelRecord(line);
|
|
443
|
+
if (lData) {
|
|
444
|
+
currentLevelId = insertAndGetRowid(dbPath, `INSERT INTO ensdf_levels(dataset_id,Z,A,element,energy_keV,energy_raw,energy_unc_keV,spin_parity,half_life,half_life_seconds,half_life_unc_seconds,isomer_flag,questionable,comment_flag) VALUES(${datasetId},${dsInfo.nucid.Z},${dsInfo.nucid.A},${sqlStr(dsInfo.nucid.element)},${sqlVal(lData.energy_keV)},${sqlStr(lData.energy_raw)},${sqlVal(lData.energy_unc_keV)},${sqlStr(lData.spin_parity)},${sqlStr(lData.half_life)},${sqlVal(lData.half_life_seconds)},${sqlVal(lData.half_life_unc_seconds)},${sqlStr(lData.isomer_flag)},${sqlVal(lData.questionable)},${sqlStr(lData.comment_flag)});`);
|
|
445
|
+
currentLevelEnergy = lData.energy_keV;
|
|
446
|
+
currentGammaRecordType = '';
|
|
447
|
+
counts.levels++;
|
|
448
|
+
}
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case 'gamma': {
|
|
452
|
+
flushGamma();
|
|
453
|
+
const gData = parseGammaRecord(line);
|
|
454
|
+
if (gData && currentLevelId !== null) {
|
|
455
|
+
currentGamma = {
|
|
456
|
+
dataset_id: datasetId,
|
|
457
|
+
level_id: currentLevelId,
|
|
458
|
+
Z: dsInfo.nucid.Z,
|
|
459
|
+
A: dsInfo.nucid.A,
|
|
460
|
+
element: dsInfo.nucid.element,
|
|
461
|
+
level_energy_keV: currentLevelEnergy ?? 0,
|
|
462
|
+
gamma_energy_keV: gData.gamma_energy_keV,
|
|
463
|
+
gamma_energy_raw: gData.gamma_energy_raw,
|
|
464
|
+
gamma_energy_unc_keV: gData.gamma_energy_unc_keV,
|
|
465
|
+
rel_intensity: gData.rel_intensity,
|
|
466
|
+
rel_intensity_unc: gData.rel_intensity_unc,
|
|
467
|
+
total_intensity: gData.total_intensity,
|
|
468
|
+
total_intensity_unc: gData.total_intensity_unc,
|
|
469
|
+
multipolarity: gData.multipolarity,
|
|
470
|
+
mixing_ratio: gData.mixing_ratio,
|
|
471
|
+
mixing_ratio_unc: gData.mixing_ratio_unc,
|
|
472
|
+
total_conv_coeff: gData.total_conv_coeff,
|
|
473
|
+
total_conv_coeff_unc: gData.total_conv_coeff_unc,
|
|
474
|
+
comment_flag: gData.comment_flag,
|
|
475
|
+
coin_flag: gData.coin_flag,
|
|
476
|
+
questionable: gData.questionable,
|
|
477
|
+
be2w: null,
|
|
478
|
+
be2w_unc: null,
|
|
479
|
+
bm1w: null,
|
|
480
|
+
bm1w_unc: null,
|
|
481
|
+
};
|
|
482
|
+
currentGammaRecordType = 'G';
|
|
483
|
+
}
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
case 'beta': {
|
|
487
|
+
flushGamma();
|
|
488
|
+
const bData = parseBetaRecord(line);
|
|
489
|
+
const parentZ = parentNucid?.Z ?? dsInfo.nucid.Z;
|
|
490
|
+
const parentA = parentNucid?.A ?? dsInfo.nucid.A;
|
|
491
|
+
const parentEl = parentNucid?.element ?? dsInfo.nucid.element;
|
|
492
|
+
executeSql(dbPath, `INSERT INTO ensdf_decay_feedings(dataset_id,parent_Z,parent_A,parent_element,decay_mode,daughter_level_keV,daughter_level_id,ib_percent,ib_percent_unc,ie_percent,ie_percent_unc,ti_percent,ti_percent_unc,log_ft,log_ft_unc,endpoint_keV,endpoint_unc_keV,forbiddenness,comment_flag) VALUES(${datasetId},${parentZ},${parentA},${sqlStr(parentEl)},${sqlStr('B-')},${sqlVal(currentLevelEnergy)},${sqlVal(currentLevelId)},${sqlVal(bData.ib_percent)},${sqlVal(bData.ib_percent_unc)},NULL,NULL,NULL,NULL,${sqlVal(bData.log_ft)},${sqlVal(bData.log_ft_unc)},${sqlVal(bData.endpoint_keV)},${sqlVal(bData.endpoint_unc_keV)},${sqlStr(bData.forbiddenness)},${sqlStr(bData.comment_flag)});`);
|
|
493
|
+
currentGammaRecordType = 'B';
|
|
494
|
+
counts.feedings++;
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
case 'ec': {
|
|
498
|
+
flushGamma();
|
|
499
|
+
const eData = parseECRecord(line);
|
|
500
|
+
const parentZ = parentNucid?.Z ?? dsInfo.nucid.Z;
|
|
501
|
+
const parentA = parentNucid?.A ?? dsInfo.nucid.A;
|
|
502
|
+
const parentEl = parentNucid?.element ?? dsInfo.nucid.element;
|
|
503
|
+
const mode = dsInfo.datasetType.includes('EC+B+') ? 'EC+B+' : 'EC';
|
|
504
|
+
executeSql(dbPath, `INSERT INTO ensdf_decay_feedings(dataset_id,parent_Z,parent_A,parent_element,decay_mode,daughter_level_keV,daughter_level_id,ib_percent,ib_percent_unc,ie_percent,ie_percent_unc,ti_percent,ti_percent_unc,log_ft,log_ft_unc,endpoint_keV,endpoint_unc_keV,forbiddenness,comment_flag) VALUES(${datasetId},${parentZ},${parentA},${sqlStr(parentEl)},${sqlStr(mode)},${sqlVal(currentLevelEnergy)},${sqlVal(currentLevelId)},${sqlVal(eData.ib_percent)},${sqlVal(eData.ib_percent_unc)},${sqlVal(eData.ie_percent)},${sqlVal(eData.ie_percent_unc)},${sqlVal(eData.ti_percent)},${sqlVal(eData.ti_percent_unc)},${sqlVal(eData.log_ft)},${sqlVal(eData.log_ft_unc)},${sqlVal(eData.endpoint_keV)},${sqlVal(eData.endpoint_unc_keV)},${sqlStr(eData.forbiddenness)},${sqlStr(eData.comment_flag)});`);
|
|
505
|
+
currentGammaRecordType = 'E';
|
|
506
|
+
counts.feedings++;
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
case 'reference':
|
|
510
|
+
// References in non-REFERENCES datasets → handled at dataset level
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// Flush last gamma
|
|
515
|
+
flushGamma();
|
|
516
|
+
// Batch insert gammas
|
|
517
|
+
if (gammaBuffer.length > 0) {
|
|
518
|
+
for (let i = 0; i < gammaBuffer.length; i += ENSDF_BATCH_SIZE) {
|
|
519
|
+
const batch = gammaBuffer.slice(i, i + ENSDF_BATCH_SIZE);
|
|
520
|
+
executeSql(dbPath, `BEGIN;\n${batch.join('\n')}\nCOMMIT;`);
|
|
521
|
+
}
|
|
522
|
+
counts.gammas += gammaBuffer.length;
|
|
523
|
+
}
|
|
524
|
+
// Update dataset with QREF and parent half-life
|
|
525
|
+
if (qrefKeynumbers.length > 0 || parentHalfLife) {
|
|
526
|
+
const uniqueKeys = [...new Set(qrefKeynumbers)];
|
|
527
|
+
const qrefJson = uniqueKeys.length > 0 ? JSON.stringify(uniqueKeys) : null;
|
|
528
|
+
executeSql(dbPath, `UPDATE ensdf_datasets SET qref_keynumbers=${sqlStr(qrefJson)}, qref_raw=${sqlStr(qrefRaw || null)}, parent_half_life=${sqlStr(parentHalfLife)} WHERE dataset_id=${datasetId};`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
console.error(` ${file}: refs=${counts.references}, ds=${counts.datasets}, lvl=${counts.levels}, g=${counts.gammas}, feed=${counts.feedings}`);
|
|
532
|
+
}
|
|
533
|
+
return counts;
|
|
534
|
+
}
|
|
535
|
+
// ── Laser radii ingestion ───────────────────────────────────────────────────
|
|
536
|
+
function ingestLaserRadii(dbPath, texPath) {
|
|
537
|
+
if (!fs.existsSync(texPath)) {
|
|
538
|
+
console.error('Laser radii file not found, skipping laser radii ingestion');
|
|
539
|
+
return { rows: 0, refs: 0 };
|
|
540
|
+
}
|
|
541
|
+
const content = fs.readFileSync(texPath, 'utf-8');
|
|
542
|
+
const { rows, refs, refIsotopes } = parseLaserRadii(content);
|
|
543
|
+
// Idempotent: clear previous data (refs first for FK constraint)
|
|
544
|
+
executeSql(dbPath, `
|
|
545
|
+
DELETE FROM laser_radii_refs;
|
|
546
|
+
DELETE FROM laser_radii;
|
|
547
|
+
`);
|
|
548
|
+
const BATCH_SIZE = 200;
|
|
549
|
+
// Insert laser_radii rows
|
|
550
|
+
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
|
551
|
+
const batch = rows.slice(i, i + BATCH_SIZE);
|
|
552
|
+
const stmts = batch.map(r => {
|
|
553
|
+
const refA = refIsotopes.get(r.Z) ?? 0;
|
|
554
|
+
return `INSERT OR REPLACE INTO laser_radii VALUES(${r.Z},${r.A},${r.N},'${sqlEscape(r.element)}',${sqlVal(r.delta_r2_fm2)},${sqlVal(r.delta_r2_unc_fm2)},${sqlVal(r.r_charge_fm)},${sqlVal(r.r_charge_unc_fm)},${sqlVal(r.is_reference)},${sqlVal(r.in_angeli_2013)},${refA});`;
|
|
555
|
+
});
|
|
556
|
+
executeSql(dbPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
557
|
+
}
|
|
558
|
+
// Insert laser_radii_refs
|
|
559
|
+
for (let i = 0; i < refs.length; i += BATCH_SIZE) {
|
|
560
|
+
const batch = refs.slice(i, i + BATCH_SIZE);
|
|
561
|
+
const stmts = batch.map(r => `INSERT OR REPLACE INTO laser_radii_refs VALUES(${r.Z},${r.A},${sqlStr(r.citekey)},${sqlStr(r.reference)});`);
|
|
562
|
+
executeSql(dbPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
563
|
+
}
|
|
564
|
+
return { rows: rows.length, refs: refs.length };
|
|
565
|
+
}
|
|
566
|
+
// ── TUNL levels ingestion ─────────────────────────────────────────────────────
|
|
567
|
+
function ingestTunlLevels(dbPath, tunlDir) {
|
|
568
|
+
if (!fs.existsSync(tunlDir)) {
|
|
569
|
+
console.error('TUNL directory not found, skipping TUNL ingestion');
|
|
570
|
+
return 0;
|
|
571
|
+
}
|
|
572
|
+
const files = fs.readdirSync(tunlDir)
|
|
573
|
+
.filter(f => f.endsWith('.txt'))
|
|
574
|
+
.sort();
|
|
575
|
+
if (files.length === 0) {
|
|
576
|
+
console.error('No TUNL .txt files found, skipping TUNL ingestion');
|
|
577
|
+
return 0;
|
|
578
|
+
}
|
|
579
|
+
// Idempotent: clear previous TUNL data
|
|
580
|
+
executeSql(dbPath, `DELETE FROM tunl_levels;`);
|
|
581
|
+
const BATCH_SIZE = 200;
|
|
582
|
+
let totalRows = 0;
|
|
583
|
+
for (const file of files) {
|
|
584
|
+
const filePath = path.join(tunlDir, file);
|
|
585
|
+
const text = fs.readFileSync(filePath, 'utf-8');
|
|
586
|
+
const rows = parseTunlLevels(text);
|
|
587
|
+
if (rows.length === 0) {
|
|
588
|
+
console.error(` TUNL ${file}: 0 levels (parse failed or empty)`);
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
|
592
|
+
const batch = rows.slice(i, i + BATCH_SIZE);
|
|
593
|
+
const stmts = batch.map(r => `INSERT INTO tunl_levels(Z,A,element,energy_keV,energy_unc_keV,energy_raw,spin_parity,isospin,width_keV,width_unc_keV,width_raw,width_relation,half_life,decay_modes,evaluation,table_label) VALUES(${r.Z},${r.A},${sqlStr(r.element)},${sqlVal(r.energy_keV)},${sqlVal(r.energy_unc_keV)},${sqlStr(r.energy_raw)},${sqlStr(r.spin_parity)},${sqlStr(r.isospin)},${sqlVal(r.width_keV)},${sqlVal(r.width_unc_keV)},${sqlStr(r.width_raw)},${sqlStr(r.width_relation)},${sqlStr(r.half_life)},${sqlStr(r.decay_modes)},${sqlStr(r.evaluation)},${sqlStr(r.table_label)});`);
|
|
594
|
+
executeSql(dbPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
595
|
+
}
|
|
596
|
+
totalRows += rows.length;
|
|
597
|
+
console.error(` TUNL ${file}: ${rows.length} levels`);
|
|
598
|
+
}
|
|
599
|
+
return totalRows;
|
|
600
|
+
}
|
|
601
|
+
export function buildDatabase(dataDir, outputPath) {
|
|
602
|
+
// Ensure output directory exists
|
|
603
|
+
const outDir = path.dirname(outputPath);
|
|
604
|
+
if (!fs.existsSync(outDir)) {
|
|
605
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
606
|
+
}
|
|
607
|
+
// Build into temp file, then atomic rename on success
|
|
608
|
+
const tmpPath = `${outputPath}.tmp.${Date.now()}`;
|
|
609
|
+
// Remove stale temp file if any
|
|
610
|
+
if (fs.existsSync(tmpPath)) {
|
|
611
|
+
fs.unlinkSync(tmpPath);
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
// Create schema
|
|
615
|
+
executeSql(tmpPath, SCHEMA_SQL);
|
|
616
|
+
// Parse all data files
|
|
617
|
+
const massContent = fs.readFileSync(path.join(dataDir, 'mass_1.mas20'), 'utf-8');
|
|
618
|
+
const masses = parseAmeMasses(massContent);
|
|
619
|
+
const rct1Content = fs.readFileSync(path.join(dataDir, 'rct1.mas20'), 'utf-8');
|
|
620
|
+
const rct1Rows = parseAmeRct1(rct1Content);
|
|
621
|
+
const rct2Content = fs.readFileSync(path.join(dataDir, 'rct2_1.mas20'), 'utf-8');
|
|
622
|
+
const rct2Rows = parseAmeRct2(rct2Content);
|
|
623
|
+
const nubaseContent = fs.readFileSync(path.join(dataDir, 'nubase_4.mas20'), 'utf-8');
|
|
624
|
+
const nubaseRows = parseNubase(nubaseContent);
|
|
625
|
+
let radiiCount = 0;
|
|
626
|
+
const radiiPath = path.join(dataDir, 'charge_radii.csv');
|
|
627
|
+
let radiiRows = [];
|
|
628
|
+
if (fs.existsSync(radiiPath)) {
|
|
629
|
+
const radiiContent = fs.readFileSync(radiiPath, 'utf-8');
|
|
630
|
+
radiiRows = parseChargeRadii(radiiContent);
|
|
631
|
+
}
|
|
632
|
+
// Insert data in batches
|
|
633
|
+
const BATCH_SIZE = 200;
|
|
634
|
+
// -- ame_masses --
|
|
635
|
+
for (let i = 0; i < masses.length; i += BATCH_SIZE) {
|
|
636
|
+
const batch = masses.slice(i, i + BATCH_SIZE);
|
|
637
|
+
const stmts = batch.map(r => `INSERT OR REPLACE INTO ame_masses VALUES(${r.Z},${r.A},'${sqlEscape(r.element)}',${sqlVal(r.mass_excess_keV)},${sqlVal(r.mass_excess_unc_keV)},${sqlVal(r.binding_energy_per_A_keV)},${sqlVal(r.binding_energy_per_A_unc_keV)},${sqlVal(r.beta_decay_energy_keV)},${sqlVal(r.beta_decay_energy_unc_keV)},${sqlVal(r.atomic_mass_micro_u)},${sqlVal(r.atomic_mass_unc_micro_u)},${sqlVal(r.is_estimated)});`);
|
|
638
|
+
executeSql(tmpPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
639
|
+
}
|
|
640
|
+
// -- ame_reactions (merge rct1 + rct2) --
|
|
641
|
+
// Build rct2 lookup
|
|
642
|
+
const rct2Map = new Map();
|
|
643
|
+
for (const r of rct2Rows) {
|
|
644
|
+
rct2Map.set(`${r.Z}_${r.A}`, r);
|
|
645
|
+
}
|
|
646
|
+
// Union of all (Z,A) from rct1 and rct2
|
|
647
|
+
const reactionKeys = new Set();
|
|
648
|
+
for (const r of rct1Rows)
|
|
649
|
+
reactionKeys.add(`${r.Z}_${r.A}`);
|
|
650
|
+
for (const r of rct2Rows)
|
|
651
|
+
reactionKeys.add(`${r.Z}_${r.A}`);
|
|
652
|
+
const rct1Map = new Map();
|
|
653
|
+
for (const r of rct1Rows)
|
|
654
|
+
rct1Map.set(`${r.Z}_${r.A}`, r);
|
|
655
|
+
const reactionInserts = [];
|
|
656
|
+
for (const key of reactionKeys) {
|
|
657
|
+
const r1 = rct1Map.get(key);
|
|
658
|
+
const r2 = rct2Map.get(key);
|
|
659
|
+
const [zStr, aStr] = key.split('_');
|
|
660
|
+
const Z = parseInt(zStr, 10);
|
|
661
|
+
const A = parseInt(aStr, 10);
|
|
662
|
+
const element = r1?.element ?? r2?.element ?? '';
|
|
663
|
+
reactionInserts.push(`INSERT OR REPLACE INTO ame_reactions VALUES(${Z},${A},'${sqlEscape(element)}',${sqlVal(r1?.S2n_keV ?? null)},${sqlVal(r1?.S2n_unc_keV ?? null)},${sqlVal(r1?.S2p_keV ?? null)},${sqlVal(r1?.S2p_unc_keV ?? null)},${sqlVal(r1?.Qa_keV ?? null)},${sqlVal(r1?.Qa_unc_keV ?? null)},${sqlVal(r1?.Q2bm_keV ?? null)},${sqlVal(r1?.Q2bm_unc_keV ?? null)},${sqlVal(r1?.Qep_keV ?? null)},${sqlVal(r1?.Qep_unc_keV ?? null)},${sqlVal(r1?.Qbn_keV ?? null)},${sqlVal(r1?.Qbn_unc_keV ?? null)},${sqlVal(r2?.Sn_keV ?? null)},${sqlVal(r2?.Sn_unc_keV ?? null)},${sqlVal(r2?.Sp_keV ?? null)},${sqlVal(r2?.Sp_unc_keV ?? null)},${sqlVal(r2?.Q4bm_keV ?? null)},${sqlVal(r2?.Q4bm_unc_keV ?? null)},${sqlVal(r2?.Qda_keV ?? null)},${sqlVal(r2?.Qda_unc_keV ?? null)},${sqlVal(r2?.Qpa_keV ?? null)},${sqlVal(r2?.Qpa_unc_keV ?? null)},${sqlVal(r2?.Qna_keV ?? null)},${sqlVal(r2?.Qna_unc_keV ?? null)});`);
|
|
664
|
+
}
|
|
665
|
+
for (let i = 0; i < reactionInserts.length; i += BATCH_SIZE) {
|
|
666
|
+
const batch = reactionInserts.slice(i, i + BATCH_SIZE);
|
|
667
|
+
executeSql(tmpPath, `BEGIN;\n${batch.join('\n')}\nCOMMIT;`);
|
|
668
|
+
}
|
|
669
|
+
// -- nubase --
|
|
670
|
+
for (let i = 0; i < nubaseRows.length; i += BATCH_SIZE) {
|
|
671
|
+
const batch = nubaseRows.slice(i, i + BATCH_SIZE);
|
|
672
|
+
const stmts = batch.map(r => `INSERT OR REPLACE INTO nubase VALUES(${r.Z},${r.A},'${sqlEscape(r.element)}',${r.isomer_index},${sqlVal(r.mass_excess_keV)},${sqlVal(r.mass_excess_unc_keV)},${sqlVal(r.excitation_energy_keV)},'${sqlEscape(r.half_life)}',${sqlVal(r.half_life_seconds)},${sqlVal(r.half_life_unc_seconds)},'${sqlEscape(r.spin_parity)}','${sqlEscape(r.decay_modes)}',${sqlVal(r.is_estimated)});`);
|
|
673
|
+
executeSql(tmpPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
674
|
+
}
|
|
675
|
+
// -- charge_radii --
|
|
676
|
+
for (let i = 0; i < radiiRows.length; i += BATCH_SIZE) {
|
|
677
|
+
const batch = radiiRows.slice(i, i + BATCH_SIZE);
|
|
678
|
+
const stmts = batch.map(r => `INSERT OR REPLACE INTO charge_radii VALUES(${r.Z},${r.A},'${sqlEscape(r.element)}',${sqlVal(r.r_charge_fm)},${sqlVal(r.r_charge_unc_fm)},${sqlVal(r.r_charge_preliminary_fm)},${sqlVal(r.r_charge_preliminary_unc_fm)});`);
|
|
679
|
+
executeSql(tmpPath, `BEGIN;\n${stmts.join('\n')}\nCOMMIT;`);
|
|
680
|
+
}
|
|
681
|
+
radiiCount = radiiRows.length;
|
|
682
|
+
// -- ENSDF --
|
|
683
|
+
const ensdfCounts = ingestEnsdfFiles(tmpPath, path.join(dataDir, 'ensdf'));
|
|
684
|
+
// -- Laser radii (Li et al. 2021) --
|
|
685
|
+
const laserRadiiPath = path.join(dataDir, 'laser_radii', 'Radii.tex');
|
|
686
|
+
const laserCounts = ingestLaserRadii(tmpPath, laserRadiiPath);
|
|
687
|
+
// -- TUNL energy levels (A ≤ 20) --
|
|
688
|
+
const tunlCount = ingestTunlLevels(tmpPath, path.join(dataDir, 'tunl'));
|
|
689
|
+
// Indexes
|
|
690
|
+
executeSql(tmpPath, INDEX_SQL);
|
|
691
|
+
// Metadata
|
|
692
|
+
const now = new Date().toISOString();
|
|
693
|
+
executeSql(tmpPath, `
|
|
694
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ame_version','AME2020');
|
|
695
|
+
INSERT OR REPLACE INTO nds_meta VALUES('nubase_version','NUBASE2020');
|
|
696
|
+
INSERT OR REPLACE INTO nds_meta VALUES('radii_version','IAEA-2024');
|
|
697
|
+
INSERT OR REPLACE INTO nds_meta VALUES('build_date','${now}');
|
|
698
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ame_masses_count','${masses.length}');
|
|
699
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ame_reactions_count','${reactionKeys.size}');
|
|
700
|
+
INSERT OR REPLACE INTO nds_meta VALUES('nubase_count','${nubaseRows.length}');
|
|
701
|
+
INSERT OR REPLACE INTO nds_meta VALUES('radii_count','${radiiCount}');
|
|
702
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_version','ENSDF-2024');
|
|
703
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_references_count','${ensdfCounts.references}');
|
|
704
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_datasets_count','${ensdfCounts.datasets}');
|
|
705
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_levels_count','${ensdfCounts.levels}');
|
|
706
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_gammas_count','${ensdfCounts.gammas}');
|
|
707
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_feedings_count','${ensdfCounts.feedings}');
|
|
708
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_version','Li2021');
|
|
709
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_count','${laserCounts.rows}');
|
|
710
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_refs_count','${laserCounts.refs}');
|
|
711
|
+
INSERT OR REPLACE INTO nds_meta VALUES('tunl_version','TUNL-2024');
|
|
712
|
+
INSERT OR REPLACE INTO nds_meta VALUES('tunl_levels_count','${tunlCount}');
|
|
713
|
+
`);
|
|
714
|
+
// Atomic rename: replace old DB only on complete success
|
|
715
|
+
fs.renameSync(tmpPath, outputPath);
|
|
716
|
+
return {
|
|
717
|
+
masses: masses.length,
|
|
718
|
+
rct1: rct1Rows.length,
|
|
719
|
+
rct2: rct2Rows.length,
|
|
720
|
+
nubase: nubaseRows.length,
|
|
721
|
+
radii: radiiCount,
|
|
722
|
+
laserRadii: laserCounts.rows,
|
|
723
|
+
ensdf: ensdfCounts,
|
|
724
|
+
tunl: tunlCount,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
// Clean up partial temp file on failure
|
|
729
|
+
try {
|
|
730
|
+
fs.unlinkSync(tmpPath);
|
|
731
|
+
}
|
|
732
|
+
catch { /* ignore */ }
|
|
733
|
+
throw err;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
// ── ENSDF-only mode: add ENSDF data to an existing database ─────────────────
|
|
737
|
+
export function addEnsdfToDatabase(dbPath, ensdfDir) {
|
|
738
|
+
if (!fs.existsSync(dbPath)) {
|
|
739
|
+
throw new Error(`Database not found: ${dbPath}`);
|
|
740
|
+
}
|
|
741
|
+
if (!fs.existsSync(ensdfDir)) {
|
|
742
|
+
throw new Error(`ENSDF directory not found: ${ensdfDir}`);
|
|
743
|
+
}
|
|
744
|
+
// Atomic: work on a temp copy, rename on success
|
|
745
|
+
const tmpPath = `${dbPath}.tmp.${Date.now()}`;
|
|
746
|
+
fs.copyFileSync(dbPath, tmpPath);
|
|
747
|
+
try {
|
|
748
|
+
// Create ENSDF tables (IF NOT EXISTS — safe for existing DB)
|
|
749
|
+
executeSql(tmpPath, ENSDF_SCHEMA_SQL);
|
|
750
|
+
// Ingest (clears previous ENSDF data first for idempotency)
|
|
751
|
+
const counts = ingestEnsdfFiles(tmpPath, ensdfDir);
|
|
752
|
+
// Indexes AFTER bulk insert (performance: no B-tree rebalance during inserts)
|
|
753
|
+
executeSql(tmpPath, ENSDF_INDEX_SQL);
|
|
754
|
+
// Update metadata
|
|
755
|
+
const now = new Date().toISOString();
|
|
756
|
+
executeSql(tmpPath, `
|
|
757
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_version','ENSDF-2024');
|
|
758
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_build_date','${now}');
|
|
759
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_references_count','${counts.references}');
|
|
760
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_datasets_count','${counts.datasets}');
|
|
761
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_levels_count','${counts.levels}');
|
|
762
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_gammas_count','${counts.gammas}');
|
|
763
|
+
INSERT OR REPLACE INTO nds_meta VALUES('ensdf_feedings_count','${counts.feedings}');
|
|
764
|
+
`);
|
|
765
|
+
// Atomic replace
|
|
766
|
+
fs.renameSync(tmpPath, dbPath);
|
|
767
|
+
return counts;
|
|
768
|
+
}
|
|
769
|
+
catch (err) {
|
|
770
|
+
try {
|
|
771
|
+
fs.unlinkSync(tmpPath);
|
|
772
|
+
}
|
|
773
|
+
catch { /* ignore */ }
|
|
774
|
+
throw err;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// ── Laser-radii-only mode: add laser radii to an existing database ──────────
|
|
778
|
+
export function addLaserRadiiToDatabase(dbPath, texPath) {
|
|
779
|
+
if (!fs.existsSync(dbPath)) {
|
|
780
|
+
throw new Error(`Database not found: ${dbPath}`);
|
|
781
|
+
}
|
|
782
|
+
if (!fs.existsSync(texPath)) {
|
|
783
|
+
throw new Error(`Laser radii file not found: ${texPath}`);
|
|
784
|
+
}
|
|
785
|
+
// Atomic: work on a temp copy, rename on success
|
|
786
|
+
const tmpPath = `${dbPath}.tmp.${Date.now()}`;
|
|
787
|
+
fs.copyFileSync(dbPath, tmpPath);
|
|
788
|
+
try {
|
|
789
|
+
executeSql(tmpPath, LASER_RADII_SCHEMA_SQL);
|
|
790
|
+
const counts = ingestLaserRadii(tmpPath, texPath);
|
|
791
|
+
executeSql(tmpPath, LASER_RADII_INDEX_SQL);
|
|
792
|
+
const now = new Date().toISOString();
|
|
793
|
+
executeSql(tmpPath, `
|
|
794
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_version','Li2021');
|
|
795
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_build_date','${now}');
|
|
796
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_count','${counts.rows}');
|
|
797
|
+
INSERT OR REPLACE INTO nds_meta VALUES('laser_radii_refs_count','${counts.refs}');
|
|
798
|
+
`);
|
|
799
|
+
fs.renameSync(tmpPath, dbPath);
|
|
800
|
+
return counts;
|
|
801
|
+
}
|
|
802
|
+
catch (err) {
|
|
803
|
+
try {
|
|
804
|
+
fs.unlinkSync(tmpPath);
|
|
805
|
+
}
|
|
806
|
+
catch { /* ignore */ }
|
|
807
|
+
throw err;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
// ── TUNL-only mode: add TUNL levels to an existing database ─────────────────
|
|
811
|
+
export function addTunlToDatabase(dbPath, tunlDir) {
|
|
812
|
+
if (!fs.existsSync(dbPath)) {
|
|
813
|
+
throw new Error(`Database not found: ${dbPath}`);
|
|
814
|
+
}
|
|
815
|
+
if (!fs.existsSync(tunlDir)) {
|
|
816
|
+
throw new Error(`TUNL directory not found: ${tunlDir}`);
|
|
817
|
+
}
|
|
818
|
+
const tmpPath = `${dbPath}.tmp.${Date.now()}`;
|
|
819
|
+
fs.copyFileSync(dbPath, tmpPath);
|
|
820
|
+
try {
|
|
821
|
+
// Drop old tunl_levels table (schema may have changed) and recreate
|
|
822
|
+
executeSql(tmpPath, `DROP TABLE IF EXISTS tunl_levels;`);
|
|
823
|
+
executeSql(tmpPath, TUNL_SCHEMA_SQL);
|
|
824
|
+
const count = ingestTunlLevels(tmpPath, tunlDir);
|
|
825
|
+
executeSql(tmpPath, TUNL_INDEX_SQL);
|
|
826
|
+
const now = new Date().toISOString();
|
|
827
|
+
executeSql(tmpPath, `
|
|
828
|
+
INSERT OR REPLACE INTO nds_meta VALUES('tunl_version','TUNL-2024');
|
|
829
|
+
INSERT OR REPLACE INTO nds_meta VALUES('tunl_build_date','${now}');
|
|
830
|
+
INSERT OR REPLACE INTO nds_meta VALUES('tunl_levels_count','${count}');
|
|
831
|
+
`);
|
|
832
|
+
fs.renameSync(tmpPath, dbPath);
|
|
833
|
+
return count;
|
|
834
|
+
}
|
|
835
|
+
catch (err) {
|
|
836
|
+
try {
|
|
837
|
+
fs.unlinkSync(tmpPath);
|
|
838
|
+
}
|
|
839
|
+
catch { /* ignore */ }
|
|
840
|
+
throw err;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (process.argv[1] && (process.argv[1].endsWith('buildDb.ts') || process.argv[1].endsWith('buildDb.js'))) {
|
|
844
|
+
const args = process.argv.slice(2);
|
|
845
|
+
let dataDir = '';
|
|
846
|
+
let output = '';
|
|
847
|
+
let ensdfOnly = false;
|
|
848
|
+
let laserRadiiOnly = false;
|
|
849
|
+
let tunlOnly = false;
|
|
850
|
+
let db = '';
|
|
851
|
+
let ensdfDir = '';
|
|
852
|
+
let texFile = '';
|
|
853
|
+
let tunlDir = '';
|
|
854
|
+
for (let i = 0; i < args.length; i++) {
|
|
855
|
+
if (args[i] === '--data-dir' && args[i + 1]) {
|
|
856
|
+
dataDir = args[i + 1];
|
|
857
|
+
i++;
|
|
858
|
+
}
|
|
859
|
+
else if (args[i] === '--output' && args[i + 1]) {
|
|
860
|
+
output = args[i + 1];
|
|
861
|
+
i++;
|
|
862
|
+
}
|
|
863
|
+
else if (args[i] === '--ensdf-only') {
|
|
864
|
+
ensdfOnly = true;
|
|
865
|
+
}
|
|
866
|
+
else if (args[i] === '--laser-radii-only') {
|
|
867
|
+
laserRadiiOnly = true;
|
|
868
|
+
}
|
|
869
|
+
else if (args[i] === '--tunl-only') {
|
|
870
|
+
tunlOnly = true;
|
|
871
|
+
}
|
|
872
|
+
else if (args[i] === '--db' && args[i + 1]) {
|
|
873
|
+
db = args[i + 1];
|
|
874
|
+
i++;
|
|
875
|
+
}
|
|
876
|
+
else if (args[i] === '--ensdf-dir' && args[i + 1]) {
|
|
877
|
+
ensdfDir = args[i + 1];
|
|
878
|
+
i++;
|
|
879
|
+
}
|
|
880
|
+
else if (args[i] === '--tex-file' && args[i + 1]) {
|
|
881
|
+
texFile = args[i + 1];
|
|
882
|
+
i++;
|
|
883
|
+
}
|
|
884
|
+
else if (args[i] === '--tunl-dir' && args[i + 1]) {
|
|
885
|
+
tunlDir = args[i + 1];
|
|
886
|
+
i++;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (tunlOnly) {
|
|
890
|
+
if (!db || !tunlDir) {
|
|
891
|
+
console.error('Usage: buildDb --tunl-only --db <path> --tunl-dir <dir>');
|
|
892
|
+
process.exit(1);
|
|
893
|
+
}
|
|
894
|
+
console.error(`Adding TUNL levels to ${db} from ${tunlDir}`);
|
|
895
|
+
const count = addTunlToDatabase(db, tunlDir);
|
|
896
|
+
console.error(`TUNL done: ${count} levels`);
|
|
897
|
+
const size = fs.statSync(db).size;
|
|
898
|
+
console.error(`Database size: ${(size / 1024 / 1024).toFixed(1)} MB`);
|
|
899
|
+
}
|
|
900
|
+
else if (laserRadiiOnly) {
|
|
901
|
+
if (!db || !texFile) {
|
|
902
|
+
console.error('Usage: buildDb --laser-radii-only --db <path> --tex-file <path>');
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
console.error(`Adding laser radii to ${db} from ${texFile}`);
|
|
906
|
+
const counts = addLaserRadiiToDatabase(db, texFile);
|
|
907
|
+
console.error(`Laser radii done: rows=${counts.rows}, refs=${counts.refs}`);
|
|
908
|
+
const size = fs.statSync(db).size;
|
|
909
|
+
console.error(`Database size: ${(size / 1024 / 1024).toFixed(1)} MB`);
|
|
910
|
+
}
|
|
911
|
+
else if (ensdfOnly) {
|
|
912
|
+
if (!db || !ensdfDir) {
|
|
913
|
+
console.error('Usage: buildDb --ensdf-only --db <path> --ensdf-dir <dir>');
|
|
914
|
+
process.exit(1);
|
|
915
|
+
}
|
|
916
|
+
console.error(`Adding ENSDF data to ${db} from ${ensdfDir}`);
|
|
917
|
+
const counts = addEnsdfToDatabase(db, ensdfDir);
|
|
918
|
+
console.error(`ENSDF done: refs=${counts.references}, datasets=${counts.datasets}, levels=${counts.levels}, gammas=${counts.gammas}, feedings=${counts.feedings}`);
|
|
919
|
+
const size = fs.statSync(db).size;
|
|
920
|
+
console.error(`Database size: ${(size / 1024 / 1024).toFixed(1)} MB`);
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
if (!dataDir || !output) {
|
|
924
|
+
console.error('Usage: buildDb --data-dir <dir> --output <path>');
|
|
925
|
+
process.exit(1);
|
|
926
|
+
}
|
|
927
|
+
console.error(`Building NDS database from ${dataDir} → ${output}`);
|
|
928
|
+
const counts = buildDatabase(dataDir, output);
|
|
929
|
+
console.error(`Done: masses=${counts.masses}, rct1=${counts.rct1}, rct2=${counts.rct2}, nubase=${counts.nubase}, radii=${counts.radii}, laserRadii=${counts.laserRadii}, tunl=${counts.tunl}`);
|
|
930
|
+
console.error(`ENSDF: refs=${counts.ensdf.references}, datasets=${counts.ensdf.datasets}, levels=${counts.ensdf.levels}, gammas=${counts.ensdf.gammas}, feedings=${counts.ensdf.feedings}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
//# sourceMappingURL=buildDb.js.map
|