module-qc-database-tools 0.0.1__py3-none-any.whl
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.
- module_qc_database_tools/__init__.py +13 -0
- module_qc_database_tools/_version.py +4 -0
- module_qc_database_tools/cli/__init__.py +8 -0
- module_qc_database_tools/cli/__main__.py +5 -0
- module_qc_database_tools/cli/generate_yarr_config.py +450 -0
- module_qc_database_tools/cli/main.py +40 -0
- module_qc_database_tools/cli/register_component.py +61 -0
- module_qc_database_tools/data/YARR/chip_template.json +19 -0
- module_qc_database_tools/data/componentConfigs.json +34 -0
- module_qc_database_tools-0.0.1.dist-info/METADATA +120 -0
- module_qc_database_tools-0.0.1.dist-info/RECORD +13 -0
- module_qc_database_tools-0.0.1.dist-info/WHEEL +4 -0
- module_qc_database_tools-0.0.1.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from module_qc_database_tools._version import __version__
|
|
6
|
+
|
|
7
|
+
if sys.version_info >= (3, 9):
|
|
8
|
+
from importlib import resources
|
|
9
|
+
else:
|
|
10
|
+
import importlib_resources as resources
|
|
11
|
+
data = resources.files("module_qc_database_tools") / "data"
|
|
12
|
+
|
|
13
|
+
__all__ = ("__version__", "data")
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import math
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import itkdb
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
import module_qc_database_tools
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command()
|
|
17
|
+
def main(
|
|
18
|
+
serial_number: str = typer.Option(..., "-sn", "--sn", help="ATLAS serialNumber"),
|
|
19
|
+
chip_template: Path = typer.Option(
|
|
20
|
+
(module_qc_database_tools.data / "YARR" / "chip_template.json").resolve(),
|
|
21
|
+
"-ch",
|
|
22
|
+
"--chipTemplate",
|
|
23
|
+
help="Default chip template from which the chip configs are generated.",
|
|
24
|
+
exists=True,
|
|
25
|
+
file_okay=True,
|
|
26
|
+
readable=True,
|
|
27
|
+
resolve_path=True,
|
|
28
|
+
),
|
|
29
|
+
output_dir: Path = typer.Option(
|
|
30
|
+
...,
|
|
31
|
+
"-o",
|
|
32
|
+
"--outdir",
|
|
33
|
+
help="Path to output directory. Configs must be in Yarr/configs.",
|
|
34
|
+
exists=False,
|
|
35
|
+
writable=True,
|
|
36
|
+
),
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Main executable for generating yarr config.
|
|
40
|
+
"""
|
|
41
|
+
client = itkdb.Client()
|
|
42
|
+
module = Module(client, serial_number)
|
|
43
|
+
typer.echo("INFO: Getting layer-dependent config from module SN...")
|
|
44
|
+
layer_config = get_layer_from_serial_number(serial_number)
|
|
45
|
+
|
|
46
|
+
module.generate_config(chip_template, output_dir, layer_config, suffix="warm")
|
|
47
|
+
module.generate_config(chip_template, output_dir, layer_config, suffix="cold")
|
|
48
|
+
module.generate_config(chip_template, output_dir, layer_config, suffix="LP")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Module:
|
|
52
|
+
"""
|
|
53
|
+
Module class.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, client, serial_number, name=None):
|
|
57
|
+
self.client = client
|
|
58
|
+
self.serial_number = serial_number
|
|
59
|
+
self.name = name if name else self.serial_number
|
|
60
|
+
self.module = client.get("getComponent", json={"component": serial_number})
|
|
61
|
+
self.bare_modules = []
|
|
62
|
+
for child in self.module["children"]:
|
|
63
|
+
if child["componentType"]["code"] == "BARE_MODULE":
|
|
64
|
+
self.bare_modules.append(
|
|
65
|
+
client.get(
|
|
66
|
+
"getComponent",
|
|
67
|
+
json={"component": child["component"]["serialNumber"]},
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
self.chips = []
|
|
71
|
+
for bare_module in self.bare_modules:
|
|
72
|
+
for child in bare_module["children"]:
|
|
73
|
+
if child["componentType"]["code"] == "FE_CHIP":
|
|
74
|
+
self.chips.append(
|
|
75
|
+
Chip(
|
|
76
|
+
client,
|
|
77
|
+
child["component"]["serialNumber"],
|
|
78
|
+
module_name=self.name,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
if len(self.bare_modules) == 3 and len(self.chips) == 3:
|
|
82
|
+
self.module_type = "triplet"
|
|
83
|
+
typer.echo(f"INFO: triplet {self.serial_number} initiated.")
|
|
84
|
+
else:
|
|
85
|
+
self.module_type = "quad"
|
|
86
|
+
typer.echo(f"INFO: quad module {self.serial_number} initiated.")
|
|
87
|
+
|
|
88
|
+
def generate_config(self, chip_template, outdir, layer_config, suffix):
|
|
89
|
+
"""
|
|
90
|
+
Generate module config.
|
|
91
|
+
"""
|
|
92
|
+
typer.echo(
|
|
93
|
+
f"INFO: generating module config for module {self.serial_number} with {layer_config}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
spec = {"chipType": "RD53B", "chips": []}
|
|
97
|
+
|
|
98
|
+
for chip_index, chip in enumerate(self.chips):
|
|
99
|
+
if self.module_type == "triplet":
|
|
100
|
+
spec["chips"].append(
|
|
101
|
+
{
|
|
102
|
+
"config": chip.generate_config(
|
|
103
|
+
chip_template,
|
|
104
|
+
outdir,
|
|
105
|
+
chip_index,
|
|
106
|
+
layer_config,
|
|
107
|
+
self.module_type,
|
|
108
|
+
suffix=suffix,
|
|
109
|
+
),
|
|
110
|
+
"path": "relToCon",
|
|
111
|
+
"tx": 0,
|
|
112
|
+
"rx": [0, 1, 2][chip_index],
|
|
113
|
+
"enable": 1,
|
|
114
|
+
"locked": 0,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
spec["chips"].append(
|
|
119
|
+
{
|
|
120
|
+
"config": chip.generate_config(
|
|
121
|
+
chip_template,
|
|
122
|
+
outdir,
|
|
123
|
+
chip_index,
|
|
124
|
+
layer_config,
|
|
125
|
+
self.module_type,
|
|
126
|
+
suffix=suffix,
|
|
127
|
+
),
|
|
128
|
+
"path": "relToCon",
|
|
129
|
+
"tx": 0,
|
|
130
|
+
"rx": [2, 1, 0, 3][chip_index],
|
|
131
|
+
"enable": 1,
|
|
132
|
+
"locked": 0,
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
output_path = Path(outdir, self.name)
|
|
137
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
138
|
+
with output_path.joinpath(
|
|
139
|
+
f"{self.name}_{layer_config}{'_'+suffix if suffix else ''}.json"
|
|
140
|
+
).open("w", encoding="utf-8") as serialized:
|
|
141
|
+
json.dump(spec, serialized, indent=4)
|
|
142
|
+
typer.echo(
|
|
143
|
+
f"INFO: connectivity file saved to {outdir}/{self.name}/{self.name}_{layer_config}{'_'+suffix if suffix else ''}.json"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class Chip:
|
|
148
|
+
"""
|
|
149
|
+
Chip class.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def __init__(self, client, serial_number, module_name=None):
|
|
153
|
+
self.client = client
|
|
154
|
+
self.serial_number = serial_number
|
|
155
|
+
self.chip_uid = hex(int(serial_number[-7:]))
|
|
156
|
+
self.module_name = module_name or self.serial_number
|
|
157
|
+
self.chip = client.get("getComponent", json={"component": serial_number})
|
|
158
|
+
self.test_run = None
|
|
159
|
+
|
|
160
|
+
typer.echo(f"INFO: chip {self.chip_uid} initiated.")
|
|
161
|
+
|
|
162
|
+
def load_wafer_probing_data(self):
|
|
163
|
+
"""
|
|
164
|
+
Load chip wafer probing data.
|
|
165
|
+
"""
|
|
166
|
+
test_id = None
|
|
167
|
+
tests = pd.DataFrame(self.chip["tests"])
|
|
168
|
+
if len(tests[tests["code"] == "FECHIP_TEST"]) > 0:
|
|
169
|
+
test_id = tests[tests["code"] == "FECHIP_TEST"]["testRuns"].iloc[-1][-1][
|
|
170
|
+
"id"
|
|
171
|
+
]
|
|
172
|
+
if not test_id:
|
|
173
|
+
msg = (
|
|
174
|
+
f"No wafer probing data in production DB for chip {self.serial_number}!"
|
|
175
|
+
)
|
|
176
|
+
raise RuntimeError(msg)
|
|
177
|
+
self.test_run = TestRun(self.client, test_id)
|
|
178
|
+
|
|
179
|
+
def generate_config(
|
|
180
|
+
self, chip_template, outdir, chip_index, layer_config, module_type, suffix=""
|
|
181
|
+
):
|
|
182
|
+
"""
|
|
183
|
+
Generate chip config.
|
|
184
|
+
"""
|
|
185
|
+
typer.echo(
|
|
186
|
+
f"INFO: generating chip config for chip {self.chip_uid} with {layer_config}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
with Path(chip_template).open(encoding="utf-8") as serialized:
|
|
190
|
+
spec = json.load(serialized)
|
|
191
|
+
|
|
192
|
+
power_config = "LP" if suffix == "LP" else layer_config
|
|
193
|
+
|
|
194
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreComp"] = {
|
|
195
|
+
"R0": 350,
|
|
196
|
+
"R0.5": 350,
|
|
197
|
+
"L0": 350,
|
|
198
|
+
"L1": 350,
|
|
199
|
+
"L2": 350,
|
|
200
|
+
"LP": 0,
|
|
201
|
+
}[power_config]
|
|
202
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampL"] = {
|
|
203
|
+
"R0": 900,
|
|
204
|
+
"R0.5": 900,
|
|
205
|
+
"L0": 900,
|
|
206
|
+
"L1": 730,
|
|
207
|
+
"L2": 550,
|
|
208
|
+
"LP": 0,
|
|
209
|
+
}[power_config]
|
|
210
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampM"] = {
|
|
211
|
+
"R0": 900,
|
|
212
|
+
"R0.5": 900,
|
|
213
|
+
"L0": 900,
|
|
214
|
+
"L1": 730,
|
|
215
|
+
"L2": 550,
|
|
216
|
+
"LP": 0,
|
|
217
|
+
}[power_config]
|
|
218
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampR"] = {
|
|
219
|
+
"R0": 900,
|
|
220
|
+
"R0.5": 900,
|
|
221
|
+
"L0": 900,
|
|
222
|
+
"L1": 730,
|
|
223
|
+
"L2": 550,
|
|
224
|
+
"LP": 0,
|
|
225
|
+
}[power_config]
|
|
226
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampT"] = {
|
|
227
|
+
"R0": 900,
|
|
228
|
+
"R0.5": 900,
|
|
229
|
+
"L0": 900,
|
|
230
|
+
"L1": 730,
|
|
231
|
+
"L2": 550,
|
|
232
|
+
"LP": 0,
|
|
233
|
+
}[power_config]
|
|
234
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampTL"] = {
|
|
235
|
+
"R0": 900,
|
|
236
|
+
"R0.5": 900,
|
|
237
|
+
"L0": 900,
|
|
238
|
+
"L1": 730,
|
|
239
|
+
"L2": 550,
|
|
240
|
+
"LP": 0,
|
|
241
|
+
}[power_config]
|
|
242
|
+
spec["RD53B"]["GlobalConfig"]["DiffPreampTR"] = {
|
|
243
|
+
"R0": 900,
|
|
244
|
+
"R0.5": 900,
|
|
245
|
+
"L0": 900,
|
|
246
|
+
"L1": 730,
|
|
247
|
+
"L2": 550,
|
|
248
|
+
"LP": 0,
|
|
249
|
+
}[power_config]
|
|
250
|
+
spec["RD53B"]["GlobalConfig"]["DiffVff"] = {
|
|
251
|
+
"R0": 150,
|
|
252
|
+
"R0.5": 150,
|
|
253
|
+
"L0": 150,
|
|
254
|
+
"L1": 150,
|
|
255
|
+
"L2": 60,
|
|
256
|
+
"LP": 0,
|
|
257
|
+
}[power_config]
|
|
258
|
+
spec["RD53B"]["GlobalConfig"]["EnCoreCol0"] = {
|
|
259
|
+
"R0": 65535,
|
|
260
|
+
"R0.5": 65535,
|
|
261
|
+
"L0": 65535,
|
|
262
|
+
"L1": 65535,
|
|
263
|
+
"L2": 65535,
|
|
264
|
+
"LP": 0,
|
|
265
|
+
}[power_config]
|
|
266
|
+
spec["RD53B"]["GlobalConfig"]["EnCoreCol1"] = {
|
|
267
|
+
"R0": 65535,
|
|
268
|
+
"R0.5": 65535,
|
|
269
|
+
"L0": 65535,
|
|
270
|
+
"L1": 65535,
|
|
271
|
+
"L2": 65535,
|
|
272
|
+
"LP": 0,
|
|
273
|
+
}[power_config]
|
|
274
|
+
spec["RD53B"]["GlobalConfig"]["EnCoreCol2"] = {
|
|
275
|
+
"R0": 65535,
|
|
276
|
+
"R0.5": 65535,
|
|
277
|
+
"L0": 65535,
|
|
278
|
+
"L1": 65535,
|
|
279
|
+
"L2": 65535,
|
|
280
|
+
"LP": 0,
|
|
281
|
+
}[power_config]
|
|
282
|
+
spec["RD53B"]["GlobalConfig"]["EnCoreCol3"] = {
|
|
283
|
+
"R0": 63,
|
|
284
|
+
"R0.5": 63,
|
|
285
|
+
"L0": 63,
|
|
286
|
+
"L1": 63,
|
|
287
|
+
"L2": 63,
|
|
288
|
+
"LP": 0,
|
|
289
|
+
}[power_config]
|
|
290
|
+
spec["RD53B"]["Parameter"]["Name"] = self.chip_uid
|
|
291
|
+
|
|
292
|
+
if module_type == "triplet":
|
|
293
|
+
spec["RD53B"]["Parameter"]["ChipId"] = chip_index + 1
|
|
294
|
+
spec["RD53B"]["GlobalConfig"]["AuroraActiveLanes"] = {
|
|
295
|
+
"R0": 7,
|
|
296
|
+
"R0.5": 3,
|
|
297
|
+
"L0": 15,
|
|
298
|
+
}[
|
|
299
|
+
layer_config
|
|
300
|
+
] ## TODO: add source for R0 and R0.5
|
|
301
|
+
# spec["RD53B"]["GlobalConfig"]["MonitorEnable"] = 0
|
|
302
|
+
# spec["RD53B"]["GlobalConfig"]["MonitorV"] = 63
|
|
303
|
+
for index in range(4):
|
|
304
|
+
spec["RD53B"]["GlobalConfig"][f"DataMergeOutMux{index}"] = (
|
|
305
|
+
0 + index
|
|
306
|
+
) % 4
|
|
307
|
+
spec["RD53B"]["GlobalConfig"]["SerEnLane"] = {"R0": 7, "R0.5": 3, "L0": 15}[
|
|
308
|
+
layer_config
|
|
309
|
+
]
|
|
310
|
+
else:
|
|
311
|
+
spec["RD53B"]["Parameter"]["ChipId"] = 12 + chip_index
|
|
312
|
+
spec["RD53B"]["GlobalConfig"]["AuroraActiveLanes"] = 1
|
|
313
|
+
for index in range(4):
|
|
314
|
+
spec["RD53B"]["GlobalConfig"][f"DataMergeOutMux{index}"] = (
|
|
315
|
+
[2, 0, 1, 0][chip_index] + index
|
|
316
|
+
) % 4
|
|
317
|
+
spec["RD53B"]["GlobalConfig"]["SerEnLane"] = [4, 1, 8, 1][chip_index]
|
|
318
|
+
|
|
319
|
+
if not self.test_run:
|
|
320
|
+
self.load_wafer_probing_data()
|
|
321
|
+
if self.test_run:
|
|
322
|
+
spec["RD53B"]["GlobalConfig"]["SldoTrimA"] = self.test_run.get_result(
|
|
323
|
+
"VDDA_TRIM"
|
|
324
|
+
)
|
|
325
|
+
spec["RD53B"]["GlobalConfig"]["SldoTrimD"] = self.test_run.get_result(
|
|
326
|
+
"VDDD_TRIM"
|
|
327
|
+
)
|
|
328
|
+
spec["RD53B"]["Parameter"]["ADCcalPar"][0] = (
|
|
329
|
+
self.test_run.get_result("ADC_OFFSET") * 1000
|
|
330
|
+
)
|
|
331
|
+
spec["RD53B"]["Parameter"]["ADCcalPar"][1] = (
|
|
332
|
+
self.test_run.get_result("ADC_SLOPE") * 1000
|
|
333
|
+
)
|
|
334
|
+
spec["RD53B"]["Parameter"]["InjCap"] = self.test_run.get_result(
|
|
335
|
+
"InjectionCapacitance"
|
|
336
|
+
) * (10**15)
|
|
337
|
+
|
|
338
|
+
# For transistor sensors calibration, the ideality factor is calculated following the presentation:
|
|
339
|
+
# https://indico.cern.ch/event/1011941/contributions/4278988/attachments/2210633/3741190/RD53B_calibatrion_sensor_temperature.pdf
|
|
340
|
+
e_charge = 1.602e-19
|
|
341
|
+
kB = 1.38064852e-23
|
|
342
|
+
PC_NTC = self.test_run.get_result("PC_NTC") + 273
|
|
343
|
+
DeltaT = 2 # 2 degree difference between PC NTC and transistor sensors
|
|
344
|
+
spec["RD53B"]["Parameter"]["NfDSLDO"] = (
|
|
345
|
+
self.test_run.get_result("TEMPERATURE_D")
|
|
346
|
+
* e_charge
|
|
347
|
+
/ (kB * math.log(15) * (PC_NTC + DeltaT))
|
|
348
|
+
)
|
|
349
|
+
spec["RD53B"]["Parameter"]["NfASLDO"] = (
|
|
350
|
+
self.test_run.get_result("TEMPERATURE_A")
|
|
351
|
+
* e_charge
|
|
352
|
+
/ (kB * math.log(15) * (PC_NTC + DeltaT))
|
|
353
|
+
)
|
|
354
|
+
spec["RD53B"]["Parameter"]["NfACB"] = (
|
|
355
|
+
self.test_run.get_result("TEMPERATURE_C")
|
|
356
|
+
* e_charge
|
|
357
|
+
/ (kB * math.log(15) * (PC_NTC + DeltaT))
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
spec["RD53B"]["Parameter"]["VcalPar"] = [
|
|
361
|
+
abs(self.test_run.get_result("VCAL_HIGH_LARGE_RANGE_OFFSET") * 1000),
|
|
362
|
+
self.test_run.get_result("VCAL_HIGH_LARGE_RANGE_SLOPE") * 1000,
|
|
363
|
+
]
|
|
364
|
+
spec["RD53B"]["Parameter"]["IrefTrim"] = self.test_run.get_result(
|
|
365
|
+
"IREF_TRIM"
|
|
366
|
+
)
|
|
367
|
+
spec["RD53B"]["Parameter"]["KSenseInA"] = self.test_run.get_result(
|
|
368
|
+
"CURR_MULT_FAC_A"
|
|
369
|
+
)
|
|
370
|
+
spec["RD53B"]["Parameter"]["KSenseInD"] = self.test_run.get_result(
|
|
371
|
+
"CURR_MULT_FAC_D"
|
|
372
|
+
)
|
|
373
|
+
spec["RD53B"]["Parameter"]["KSenseShuntA"] = (
|
|
374
|
+
self.test_run.get_result("CURR_MULT_FAC_A") * 26000.0 / 21000.0
|
|
375
|
+
)
|
|
376
|
+
spec["RD53B"]["Parameter"]["KSenseShuntD"] = (
|
|
377
|
+
self.test_run.get_result("CURR_MULT_FAC_D") * 26000.0 / 21000.0
|
|
378
|
+
)
|
|
379
|
+
spec["RD53B"]["Parameter"]["KShuntA"] = self.test_run.get_result(
|
|
380
|
+
"VINA_SHUNT_KFACTOR"
|
|
381
|
+
)
|
|
382
|
+
spec["RD53B"]["Parameter"]["KShuntD"] = self.test_run.get_result(
|
|
383
|
+
"VIND_SHUNT_KFACTOR"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
output_path = Path(
|
|
387
|
+
outdir, self.module_name, f"{layer_config}{'_'+suffix if suffix else ''}"
|
|
388
|
+
)
|
|
389
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
390
|
+
output_file = output_path.joinpath(
|
|
391
|
+
f"{self.chip_uid}_{layer_config}{'_'+suffix if suffix else ''}.json"
|
|
392
|
+
)
|
|
393
|
+
with output_file.open("w", encoding="utf-8") as serialized:
|
|
394
|
+
json.dump(spec, serialized, indent=4)
|
|
395
|
+
|
|
396
|
+
typer.echo(f"INFO: chip config saved to {output_file}")
|
|
397
|
+
return str(Path(*output_file.parts[-2:]))
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class TestRun:
|
|
401
|
+
"""
|
|
402
|
+
TestRun class.
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
def __init__(self, client, test_run_id):
|
|
406
|
+
self.client = client
|
|
407
|
+
self.identifier = test_run_id
|
|
408
|
+
self.test_run = client.get("getTestRun", json={"testRun": test_run_id})
|
|
409
|
+
self.results = pd.DataFrame(self.test_run["results"])
|
|
410
|
+
|
|
411
|
+
typer.echo(f"INFO: test run {self.identifier} initiated.")
|
|
412
|
+
|
|
413
|
+
def get_result(self, code):
|
|
414
|
+
"""
|
|
415
|
+
Get test run result.
|
|
416
|
+
"""
|
|
417
|
+
if len(self.results[self.results["code"] == code]) > 0:
|
|
418
|
+
return self.results[self.results["code"] == code]["value"].iloc[-1]
|
|
419
|
+
return None
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def get_layer_from_serial_number(serial_number):
|
|
423
|
+
"""
|
|
424
|
+
Get the layer from the serial number.
|
|
425
|
+
"""
|
|
426
|
+
if len(serial_number) != 14 or not serial_number.startswith("20U"):
|
|
427
|
+
typer.echo("Error: Please enter a valid ATLAS SN.")
|
|
428
|
+
raise typer.Exit(code=1)
|
|
429
|
+
|
|
430
|
+
if "PIMS" in serial_number or "PIR6" in serial_number:
|
|
431
|
+
return "L0"
|
|
432
|
+
|
|
433
|
+
if "PIM0" in serial_number or "PIR7" in serial_number:
|
|
434
|
+
return "R0"
|
|
435
|
+
|
|
436
|
+
if "PIM5" in serial_number or "PIR8" in serial_number:
|
|
437
|
+
return "R0.5"
|
|
438
|
+
|
|
439
|
+
if "PIM1" in serial_number or "PIRB" in serial_number:
|
|
440
|
+
return "L1"
|
|
441
|
+
|
|
442
|
+
if "PG" in serial_number:
|
|
443
|
+
return "L2"
|
|
444
|
+
|
|
445
|
+
typer.echo("Error: invalid module SN.")
|
|
446
|
+
raise typer.Exit(code=1)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
if __name__ == "__main__":
|
|
450
|
+
typer.run(main)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Top-level entrypoint for the command line interface.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
import module_qc_database_tools
|
|
9
|
+
from module_qc_database_tools.cli.generate_yarr_config import (
|
|
10
|
+
main as generate_yarr_config,
|
|
11
|
+
)
|
|
12
|
+
from module_qc_database_tools.cli.register_component import main as register_component
|
|
13
|
+
|
|
14
|
+
# subcommands
|
|
15
|
+
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.callback(invoke_without_command=True)
|
|
19
|
+
def main(
|
|
20
|
+
version: bool = typer.Option(False, "--version", help="Print the current version."),
|
|
21
|
+
prefix: bool = typer.Option(
|
|
22
|
+
False, "--prefix", help="Print the path prefix for data files."
|
|
23
|
+
),
|
|
24
|
+
) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Manage top-level options
|
|
27
|
+
"""
|
|
28
|
+
if version:
|
|
29
|
+
typer.echo(f"module-qc-database-tools v{module_qc_database_tools.__version__}")
|
|
30
|
+
raise typer.Exit()
|
|
31
|
+
if prefix:
|
|
32
|
+
typer.echo(module_qc_database_tools.data.resolve())
|
|
33
|
+
raise typer.Exit()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
app.command("generate-yarr-config")(generate_yarr_config)
|
|
37
|
+
app.command("register-component")(register_component)
|
|
38
|
+
|
|
39
|
+
# for generating documentation using mkdocs-click
|
|
40
|
+
typer_click_object = typer.main.get_command(app)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import itkdb
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
import module_qc_database_tools
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command()
|
|
16
|
+
def main(
|
|
17
|
+
serial_number: str = typer.Option(..., "-sn", "--sn", help="ATLAS serialNumber"),
|
|
18
|
+
config_path: Path = typer.Option(
|
|
19
|
+
(module_qc_database_tools.data / "componentConfigs.json").resolve(),
|
|
20
|
+
"-c",
|
|
21
|
+
"--config",
|
|
22
|
+
help="component configs for registering components",
|
|
23
|
+
exists=True,
|
|
24
|
+
file_okay=True,
|
|
25
|
+
readable=True,
|
|
26
|
+
resolve_path=True,
|
|
27
|
+
),
|
|
28
|
+
component: str = typer.Option(
|
|
29
|
+
...,
|
|
30
|
+
"-cp",
|
|
31
|
+
"--component",
|
|
32
|
+
help="Component to register",
|
|
33
|
+
),
|
|
34
|
+
institution: str = typer.Option("", "-i", "--institution", help="Institution"),
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Main executable for registering components.
|
|
38
|
+
"""
|
|
39
|
+
with config_path.open() as fpointer:
|
|
40
|
+
config = json.load(fpointer)[component]
|
|
41
|
+
|
|
42
|
+
institution = institution or os.environ.get("INSTITUTION")
|
|
43
|
+
if not institution:
|
|
44
|
+
msg = "Must specify institution from commandline or set the appropriate environment variable."
|
|
45
|
+
raise ValueError(msg)
|
|
46
|
+
|
|
47
|
+
client = itkdb.Client()
|
|
48
|
+
register_component(client, institution, config, serial_number)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def register_component(client, institution, config, serial_number):
|
|
52
|
+
"""
|
|
53
|
+
Register a component in production database using institution, config, and serial number
|
|
54
|
+
"""
|
|
55
|
+
config["institution"] = institution
|
|
56
|
+
config["serialNumber"] = serial_number
|
|
57
|
+
client.post("registerComponent", json=config)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
typer.run(main)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"RD53B": {
|
|
3
|
+
"GlobalConfig": {
|
|
4
|
+
"AuroraActiveLanes": 1,
|
|
5
|
+
"CmlBias0": 800,
|
|
6
|
+
"CmlBias1": 200,
|
|
7
|
+
"CmlBias2": 0,
|
|
8
|
+
"SerEnTap": 1,
|
|
9
|
+
"SerInvTap": 1,
|
|
10
|
+
"MonitorEnable": 1,
|
|
11
|
+
"MonitorI": 63,
|
|
12
|
+
"MonitorV": 1,
|
|
13
|
+
"ServiceBlockEn": 1
|
|
14
|
+
},
|
|
15
|
+
"Parameter": {
|
|
16
|
+
"ADCcalPar": [5.894350051879883, 0.1920430064201355, 10000.0]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"quad_PCB_2_4": {
|
|
3
|
+
"project": "P",
|
|
4
|
+
"subproject": "PG",
|
|
5
|
+
"componentType": "PCB",
|
|
6
|
+
"type": "QUAD_PCB",
|
|
7
|
+
"properties": {
|
|
8
|
+
"VERSION_OF_HYBRID_DESIGN": 2.4,
|
|
9
|
+
"FECHIP_VERSION": "2"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"quad_PCB_1_5": {
|
|
13
|
+
"project": "P",
|
|
14
|
+
"subproject": "PG",
|
|
15
|
+
"componentType": "PCB",
|
|
16
|
+
"type": "QUAD_PCB",
|
|
17
|
+
"properties": {
|
|
18
|
+
"VERSION_OF_HYBRID_DESIGN": 1.5,
|
|
19
|
+
"FECHIP_VERSION": "2"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"digital_quad_bare_module": {
|
|
23
|
+
"project": "P",
|
|
24
|
+
"subproject": "PG",
|
|
25
|
+
"componentType": "BARE_MODULE",
|
|
26
|
+
"type": "DIGITAL_QUAD_BARE_MODULE",
|
|
27
|
+
"properties": {
|
|
28
|
+
"FECHIP_VERSION": "2",
|
|
29
|
+
"SENSOR_TYPE": "0",
|
|
30
|
+
"THICKNESS": "1",
|
|
31
|
+
"VENDOR": "1"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: module-qc-database-tools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python wrapper to interface with LocalDB and Production DB for common tasks for pixel modules.
|
|
5
|
+
Project-URL: Homepage, https://gitlab.cern.ch/atlas-itk/pixel/module/module-qc-database-tools
|
|
6
|
+
Project-URL: Bug Tracker, https://gitlab.cern.ch/atlas-itk/pixel/module/module-qc-database-tools/issues
|
|
7
|
+
Project-URL: Source, https://gitlab.cern.ch/atlas-itk/pixel/module/module-qc-database-tools
|
|
8
|
+
Author-email: Jay Chan <jay.chan@cern.ch>
|
|
9
|
+
Maintainer-email: Giordon Stark <kratsg@gmail.com>, Elisabetta Pianori <elisabetta.pianori@cern.ch>, Lingxin Meng <lingxin.meng@cern.ch>
|
|
10
|
+
Classifier: Development Status :: 1 - Planning
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Requires-Python: >=3.7
|
|
26
|
+
Requires-Dist: importlib-resources>=1.4.0; python_version < '3.9'
|
|
27
|
+
Requires-Dist: itkdb>=0.4.0
|
|
28
|
+
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: typer
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Module QC Database Tools v0.0.1
|
|
33
|
+
|
|
34
|
+
The package to regisiter ITkPixV1.1 modules, and generate YARR configs from ITk
|
|
35
|
+
production database using `itkdb` API.
|
|
36
|
+
|
|
37
|
+
## Set-Up and First-time Installation
|
|
38
|
+
|
|
39
|
+
A minimum of python version 3.7+ is required.
|
|
40
|
+
|
|
41
|
+
### Virtual Python Environment
|
|
42
|
+
|
|
43
|
+
Creating the python virtual environment the standard way used the python version
|
|
44
|
+
available on the operating system. For CentOS 7 this is version 3.6. If using
|
|
45
|
+
CentOS 7, you can install python 3.8 following these
|
|
46
|
+
[instructions](https://tecadmin.net/install-python-3-8-centos/).
|
|
47
|
+
|
|
48
|
+
After installing python 3.8, create the virtual environment:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
$ python3 -m venv venv
|
|
52
|
+
$ source venv/bin/activate
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For future use:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
$ source venv/bin/activate
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Alternatively, [anaconda](https://docs.anaconda.com/anaconda/install/index.html)
|
|
62
|
+
or [miniconda](https://docs.conda.io/en/latest/miniconda.html) can also be
|
|
63
|
+
installed.
|
|
64
|
+
|
|
65
|
+
For future use:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
$ conda activate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Install
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
$ python -m pip install module-qc-database-tools
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Environment Variables
|
|
78
|
+
|
|
79
|
+
If not already set elsewhere (e.g. `~/.bashrc`), copy `.env.template` to `.env`
|
|
80
|
+
and update the values of the shell variables. Essentially, the following
|
|
81
|
+
variables regarding the production database should be available, shown below as
|
|
82
|
+
an example of environmental variables in `~/.bashrc`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export INSTITUTION="LBNL_PIXEL_MODULES"
|
|
86
|
+
export ITKDB_ACCESS_CODE1="accesscode1"
|
|
87
|
+
export ITKDB_ACCESS_CODE2="accesscode2"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Module registration
|
|
91
|
+
|
|
92
|
+
Under construction...
|
|
93
|
+
|
|
94
|
+
## Generate YARR configuration
|
|
95
|
+
|
|
96
|
+
This script has been tested on python 3.7+.
|
|
97
|
+
|
|
98
|
+
To generate YARR configuration for a given module, run `generateYARRConfig` or
|
|
99
|
+
`mqdbt generate-yarr-config`:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
$ generateYARRConfig -sn [ATLAS SN] -o [outdir]
|
|
103
|
+
$ mqdbt generate-yarr-config -sn [ATLAS SN] -o [outdir]
|
|
104
|
+
|
|
105
|
+
Parameters:
|
|
106
|
+
-sn, --sn, required=True: ATLAS serial number of the module
|
|
107
|
+
-ch, --chipTemplate, default="configs/YARR/chip_template.json": provide the path of a chip config template to generate the new chip configs from
|
|
108
|
+
-o, --outdir, required=True: Path to output directory config folder will be placed in
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For example, to generate the YARR configs for the module `20UPGR91301046` with
|
|
112
|
+
all power configs:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
$ generateYARRConfig -sn 20UPGR91301046 -o ~/module_data/.
|
|
116
|
+
$ mqdbt generate-yarr-config -sn 20UPGR91301046 -o ~/module_data/.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The time needed to generate warm and cold L2 configs for a quad module is about
|
|
120
|
+
4 seconds.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module_qc_database_tools/__init__.py,sha256=Ug7pRtlzlDzQFNexHqET-BnZxOh4qbIHdOoDE0CvjqA,319
|
|
2
|
+
module_qc_database_tools/_version.py,sha256=0E2q3bbXSYHV7qQaXVj0To-J2XdlP2Roo0TsM8oQtR4,160
|
|
3
|
+
module_qc_database_tools/cli/__init__.py,sha256=1W6Ei7U8ZHuCUe_qNkzr7-no5RJHBhVGGr0XzMCNkEs,144
|
|
4
|
+
module_qc_database_tools/cli/__main__.py,sha256=FMkuLcxfugwbVz6KD0R0DEUamhpgCJCAsD_CbadKltI,93
|
|
5
|
+
module_qc_database_tools/cli/generate_yarr_config.py,sha256=bQjkAozFNIOXL4dRyfcqD9drSkeV7Iv0CQt71rrBADI,15476
|
|
6
|
+
module_qc_database_tools/cli/main.py,sha256=aZR8tHF7qZk6T5oaX6bTlRm6rCn0wrPLCm_to3JTQW0,1180
|
|
7
|
+
module_qc_database_tools/cli/register_component.py,sha256=QDROimbJafcIAFRcjTkYZzU8_qZA1ynPuUqFfiNDVx4,1694
|
|
8
|
+
module_qc_database_tools/data/componentConfigs.json,sha256=6XWYBjrT5It7CM0IA_6tYTB4mDXFSph5ncwgsvvYYlE,713
|
|
9
|
+
module_qc_database_tools/data/YARR/chip_template.json,sha256=CysMQxQYav_kKzhDt--tbpuWW5JKNLCK-WRhXuZDPv4,378
|
|
10
|
+
module_qc_database_tools-0.0.1.dist-info/METADATA,sha256=XpqqEtQXDiEhslCE4rckFV1td5RgB_GYPrwcLV5UXfI,4032
|
|
11
|
+
module_qc_database_tools-0.0.1.dist-info/WHEEL,sha256=EI2JsGydwUL5GP9t6kzZv7G3HDPi7FuZDDf9In6amRM,87
|
|
12
|
+
module_qc_database_tools-0.0.1.dist-info/entry_points.txt,sha256=VYCepcugT-1ZbTRVDGXyF59PTTGKYEIYLpSv6rmlw6Q,266
|
|
13
|
+
module_qc_database_tools-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
generateYARRConfig = module_qc_database_tools.cli.generate_yarr_config:app
|
|
3
|
+
module-qc-database-tools = module_qc_database_tools.cli:app
|
|
4
|
+
mqdbt = module_qc_database_tools.cli:app
|
|
5
|
+
registerComponent = module_qc_database_tools.cli.register_component:app
|