trxrdpy 3.2.1__tar.gz → 3.2.2__tar.gz
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.
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/PKG-INFO +1 -1
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/pyproject.toml +1 -1
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/Spring8_SACLA/datared.py +79 -23
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/_shared_2d/azimint.py +24 -6
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/differential_analysis_utils.py +184 -29
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/plot_utils.py +60 -8
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/differential_analysis.py +49 -11
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/fitting.py +47 -9
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/main_window.py +271 -1
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/state.py +20 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/differential_tab.py +67 -10
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/fitting_tab.py +75 -15
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/viewer_tab.py +32 -2
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/multi_experiment_widgets.py +33 -2
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/cif.py +2 -1
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/PKG-INFO +1 -1
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/README.md +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/setup.cfg +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/ESRF_ID09/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/ESRF_ID09/azimint.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/ESRF_ID09/datared.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/MaxIV_FemtoMAX/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/MaxIV_FemtoMAX/azimint.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/MaxIV_FemtoMAX/datared.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/MaxIV_FemtoMAX/datared_utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/MaxIV_FemtoMAX/ping_references_default.csv +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/Spring8_SACLA/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/Spring8_SACLA/azimint.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/Spring8_SACLA/pbs/parallel_job_sender.sh +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/_shared_2d/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/calibration.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/azimint_utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/calibration_utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/fitting_utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/general_utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/common/paths.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/defaults.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/runtime_guard.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/calibration_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/differential_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/facility_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/fitting_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/integration_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/path_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/services/preparation_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/style.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/calibration_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/pattern_creation_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/preparation_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/tabs/session_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/experiment_widgets.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/facility_widgets.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/log_widget.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/parameter_widgets.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/path_widgets.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/polarization_widget.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/analysis/gui/widgets/task_output_dialog.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/detector.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/diffractometers.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/experiment.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/geometry.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/main_window.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/services/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/services/path_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/services/simulation_service.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/state.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/style.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/tabs/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/tabs/polycrystalline_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/tabs/single_crystal_tab.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/widgets/__init__.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/widgets/geometry_panel.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/gui/widgets/matrix_rotation_window.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/plot.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/polycrystalline.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/poni.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/sample.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/single_crystal.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy/simulation/utils.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/SOURCES.txt +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/dependency_links.txt +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/entry_points.txt +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/requires.txt +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/src/trxrdpy.egg-info/top_level.txt +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/tests/test_calibration_2d_cake.py +0 -0
- {trxrdpy-3.2.1 → trxrdpy-3.2.2}/tests/test_id09_datared_io.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "trxrdpy"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.2"
|
|
8
8
|
description = "Python toolkit for time-resolved X-ray diffraction simulation and beamline-dependent analysis workflows."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1123,14 +1123,16 @@ def create_dark_from_laser_off(
|
|
|
1123
1123
|
sample_name,
|
|
1124
1124
|
temperature_K,
|
|
1125
1125
|
excitation_wl_nm,
|
|
1126
|
-
fluence_mJ_cm2,
|
|
1127
|
-
time_window_fs,
|
|
1126
|
+
fluence_mJ_cm2=None,
|
|
1127
|
+
time_window_fs=None,
|
|
1128
1128
|
*,
|
|
1129
|
+
scan_type="delay",
|
|
1130
|
+
delay_for_fluence=None,
|
|
1129
1131
|
analysis_root=PATH_ANALYSIS_FOLDER,
|
|
1130
1132
|
overwrite=True,
|
|
1131
1133
|
min_files=1):
|
|
1132
1134
|
"""Build a representative DARK image by averaging all available *_laser_off.npy files
|
|
1133
|
-
for a
|
|
1135
|
+
for a delay- or fluence-scan experiment, and save it.
|
|
1134
1136
|
|
|
1135
1137
|
Inputs:
|
|
1136
1138
|
runs : int, list/tuple/np.ndarray, or "1466556,1466557" / "1466556 1466557"
|
|
@@ -1140,11 +1142,19 @@ def create_dark_from_laser_off(
|
|
|
1140
1142
|
fluence_mJ_cm2 : float/int, e.g. 25 (will be tagged like "25p0mJ")
|
|
1141
1143
|
time_window_fs : int, e.g. 250
|
|
1142
1144
|
|
|
1143
|
-
Behavior:
|
|
1145
|
+
Behavior for delay-family scans:
|
|
1144
1146
|
- Searches in:
|
|
1145
1147
|
<analysis_root>/<sample>/temperature_<T>K/excitation_wl_<wl>nm/delay/fluence_<flu>mJ/time_window_<tw>fs/2D_images
|
|
1146
1148
|
for files matching:
|
|
1147
1149
|
<sample>_<T>K_<wl>nm_<flu>mJ_<tw>fs_*_laser_off.npy
|
|
1150
|
+
|
|
1151
|
+
Behavior for fluence-family scans:
|
|
1152
|
+
- Searches in:
|
|
1153
|
+
<analysis_root>/<sample>/temperature_<T>K/excitation_wl_<wl>nm/fluence/delay_<delay>fs/time_window_<tw>fs/2D_images
|
|
1154
|
+
for files matching:
|
|
1155
|
+
<sample>_<T>K_<wl>nm_*mJ_<tw>fs_<delay>fs_laser_off.npy
|
|
1156
|
+
|
|
1157
|
+
In both cases:
|
|
1148
1158
|
- Averages all matching files (skips unreadable / NaN / shape-mismatch files).
|
|
1149
1159
|
- Saves to FemtoMAX-compatible dark path:
|
|
1150
1160
|
<analysis_root>/<sample>/temperature_<T>K/dark/<scan_tag>/2D_images/<sample>_<T>K_dark_<scan_tag_file>.npy
|
|
@@ -1156,9 +1166,13 @@ def create_dark_from_laser_off(
|
|
|
1156
1166
|
----------
|
|
1157
1167
|
runs
|
|
1158
1168
|
Run number or run collection defining the dark provenance tag.
|
|
1159
|
-
sample_name, temperature_K, excitation_wl_nm, fluence_mJ_cm2,
|
|
1160
|
-
|
|
1161
|
-
|
|
1169
|
+
sample_name, temperature_K, excitation_wl_nm, fluence_mJ_cm2, time_window_fs
|
|
1170
|
+
Experiment identity used to locate laser-off images. ``fluence_mJ_cm2``
|
|
1171
|
+
is required for delay-family inputs.
|
|
1172
|
+
scan_type : {"delay", "delay_off", "differentials", "fluence", "fluence_off"}
|
|
1173
|
+
Source image family to search.
|
|
1174
|
+
delay_for_fluence : int, optional
|
|
1175
|
+
Fixed delay folder used for fluence-family inputs.
|
|
1162
1176
|
analysis_root : path-like
|
|
1163
1177
|
Root of the standardized analysis tree.
|
|
1164
1178
|
overwrite : bool
|
|
@@ -1256,29 +1270,59 @@ def create_dark_from_laser_off(
|
|
|
1256
1270
|
sn = str(sample_name)
|
|
1257
1271
|
tK = int(temperature_K)
|
|
1258
1272
|
wl_tag = _wl_tag_nm_local(excitation_wl_nm)
|
|
1259
|
-
flu_tag = _fluence_tag_file_local(fluence_mJ_cm2)
|
|
1260
1273
|
tw = int(time_window_fs)
|
|
1261
1274
|
|
|
1262
1275
|
runs_list = _parse_runs(runs)
|
|
1263
1276
|
dark_folder_tag, dark_file_tag = _dark_tags_from_runs(runs_list)
|
|
1264
1277
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1278
|
+
st = str(scan_type).strip().lower()
|
|
1279
|
+
if st in ("delay", "delay_off", "differentials"):
|
|
1280
|
+
if fluence_mJ_cm2 is None:
|
|
1281
|
+
raise ValueError("fluence_mJ_cm2 must be provided for delay laser-off dark creation.")
|
|
1282
|
+
|
|
1283
|
+
flu_tag = _fluence_tag_file_local(fluence_mJ_cm2)
|
|
1284
|
+
source_root = os.path.join(
|
|
1285
|
+
str(analysis_root),
|
|
1286
|
+
sn,
|
|
1287
|
+
"temperature_{}K".format(int(tK)),
|
|
1288
|
+
"excitation_wl_{}nm".format(str(wl_tag)),
|
|
1289
|
+
"delay",
|
|
1290
|
+
"fluence_{}mJ".format(str(flu_tag)),
|
|
1291
|
+
"time_window_{}fs".format(int(tw)),
|
|
1292
|
+
)
|
|
1293
|
+
prefix = "{}_{}K_{}nm_{}mJ_{}fs_".format(
|
|
1294
|
+
sn, int(tK), str(wl_tag), str(flu_tag), int(tw)
|
|
1295
|
+
)
|
|
1296
|
+
suffix = "_laser_off.npy"
|
|
1297
|
+
|
|
1298
|
+
elif st in ("fluence", "fluence_off"):
|
|
1299
|
+
if delay_for_fluence is None:
|
|
1300
|
+
raise ValueError("delay_for_fluence must be provided for fluence laser-off dark creation.")
|
|
1301
|
+
|
|
1302
|
+
dly = int(delay_for_fluence)
|
|
1303
|
+
source_root = os.path.join(
|
|
1304
|
+
str(analysis_root),
|
|
1305
|
+
sn,
|
|
1306
|
+
"temperature_{}K".format(int(tK)),
|
|
1307
|
+
"excitation_wl_{}nm".format(str(wl_tag)),
|
|
1308
|
+
"fluence",
|
|
1309
|
+
"delay_{}fs".format(int(dly)),
|
|
1310
|
+
"time_window_{}fs".format(int(tw)),
|
|
1311
|
+
)
|
|
1312
|
+
prefix = "{}_{}K_{}nm_".format(sn, int(tK), str(wl_tag))
|
|
1313
|
+
suffix = "_{}fs_{}fs_laser_off.npy".format(int(tw), int(dly))
|
|
1314
|
+
|
|
1315
|
+
else:
|
|
1316
|
+
raise ValueError(
|
|
1317
|
+
"dark_from_laser_off only supports scan_type delay/delay_off/differentials "
|
|
1318
|
+
"or fluence/fluence_off."
|
|
1319
|
+
)
|
|
1320
|
+
|
|
1321
|
+
img_dir = os.path.join(source_root, "2D_images")
|
|
1275
1322
|
|
|
1276
1323
|
if not os.path.isdir(img_dir):
|
|
1277
1324
|
raise FileNotFoundError("2D_images folder not found: {}".format(img_dir))
|
|
1278
1325
|
|
|
1279
|
-
prefix = "{}_{}K_{}nm_{}mJ_{}fs_".format(sn, int(tK), str(wl_tag), str(flu_tag), int(tw))
|
|
1280
|
-
suffix = "_laser_off.npy"
|
|
1281
|
-
|
|
1282
1326
|
candidates = []
|
|
1283
1327
|
for fn in os.listdir(img_dir):
|
|
1284
1328
|
if not fn.endswith(suffix):
|
|
@@ -2735,8 +2779,18 @@ def main():
|
|
|
2735
2779
|
print("Error: --excitation_wl_nm is required for dark_from_laser_off mode.")
|
|
2736
2780
|
sys.exit(1)
|
|
2737
2781
|
|
|
2738
|
-
if
|
|
2739
|
-
|
|
2782
|
+
if st_images in ("delay", "delay_off", "differentials"):
|
|
2783
|
+
if args.fluence_mJ_cm2 is None:
|
|
2784
|
+
print("Error: --fluence_mJ_cm2 is required for dark_from_laser_off mode with delay scans.")
|
|
2785
|
+
sys.exit(1)
|
|
2786
|
+
|
|
2787
|
+
elif st_images in ("fluence", "fluence_off"):
|
|
2788
|
+
if args.delay_for_fluence is None:
|
|
2789
|
+
print("Error: --delay_for_fluence is required for dark_from_laser_off mode with fluence scans.")
|
|
2790
|
+
sys.exit(1)
|
|
2791
|
+
|
|
2792
|
+
else:
|
|
2793
|
+
print("Error: --mode dark_from_laser_off only supports delay-family or fluence-family scan types.")
|
|
2740
2794
|
sys.exit(1)
|
|
2741
2795
|
|
|
2742
2796
|
if args.time_window_fs is None:
|
|
@@ -2751,6 +2805,8 @@ def main():
|
|
|
2751
2805
|
excitation_wl_nm=args.excitation_wl_nm,
|
|
2752
2806
|
fluence_mJ_cm2=args.fluence_mJ_cm2,
|
|
2753
2807
|
time_window_fs=args.time_window_fs,
|
|
2808
|
+
scan_type=st_images,
|
|
2809
|
+
delay_for_fluence=args.delay_for_fluence,
|
|
2754
2810
|
analysis_root=PATH_ANALYSIS_FOLDER,
|
|
2755
2811
|
overwrite=bool(args.overwrite),
|
|
2756
2812
|
min_files=int(args.min_files),
|
|
@@ -614,6 +614,12 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
614
614
|
vlines_peak: Optional[Tuple[float, float]] = None,
|
|
615
615
|
vlines_bckg: Optional[Tuple[float, float]] = None,
|
|
616
616
|
title: Optional[str] = None,
|
|
617
|
+
fluence_scale: float = 1.0,
|
|
618
|
+
fluence_offset: float = 0.0,
|
|
619
|
+
delay_offset_fs: float = 0.0,
|
|
620
|
+
fs_or_ps: str = "fs",
|
|
621
|
+
digits: int = 2,
|
|
622
|
+
fluence_unit: str = "mJ/cm$^2$",
|
|
617
623
|
azim_offset_deg: float = -90.0,
|
|
618
624
|
polarization_factor: Optional[float] = None,
|
|
619
625
|
save_plots: bool = False,
|
|
@@ -648,8 +654,11 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
648
654
|
Intensity normalization and XY cache-generation controls.
|
|
649
655
|
xlim, ylim_top, ylim_diff, vlines_peak, vlines_bckg
|
|
650
656
|
Axis limits and optional marked q intervals.
|
|
651
|
-
title,
|
|
652
|
-
Figure title
|
|
657
|
+
title, fluence_scale, fluence_offset, delay_offset_fs, fs_or_ps, digits, fluence_unit
|
|
658
|
+
Figure title and display-only fluence/delay corrections. File lookup
|
|
659
|
+
still uses the stored ``delay_fs`` and ``fluences_mJ_cm2`` values.
|
|
660
|
+
azim_offset_deg, polarization_factor
|
|
661
|
+
Azimuth-coordinate offset and polarization correction.
|
|
653
662
|
save_plots, out_name, save_format, save_dpi, save_overwrite, save_base_dir
|
|
654
663
|
Figure-output controls.
|
|
655
664
|
paths, path_root, analysis_subdir
|
|
@@ -704,6 +713,7 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
704
713
|
if ref_value is None:
|
|
705
714
|
raise ValueError("ref_type='fluence' requires ref_value=<fluence_mJ_cm2>.")
|
|
706
715
|
ref_f = float(ref_value)
|
|
716
|
+
ref_f_display = ref_f * float(fluence_scale) + float(fluence_offset)
|
|
707
717
|
ds_ref = azimint_utils.FluenceDataset(
|
|
708
718
|
sample_name,
|
|
709
719
|
temperature_K,
|
|
@@ -713,7 +723,7 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
713
723
|
int(delay_fs),
|
|
714
724
|
**dataset_kwargs,
|
|
715
725
|
)
|
|
716
|
-
ref_label = f"ref: {
|
|
726
|
+
ref_label = f"ref: {ref_f_display:g} {fluence_unit}"
|
|
717
727
|
ref_tag_for_file = f"fluence_{general_utils.fluence_tag_file(ref_f)}mJ"
|
|
718
728
|
|
|
719
729
|
else:
|
|
@@ -757,12 +767,20 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
757
767
|
compute_if_missing=bool(compute_if_missing),
|
|
758
768
|
overwrite_xy=bool(overwrite_xy),
|
|
759
769
|
)
|
|
760
|
-
|
|
770
|
+
f_display = float(f) * float(fluence_scale) + float(fluence_offset)
|
|
771
|
+
patterns.append((f"{f_display:g}", q, I))
|
|
761
772
|
|
|
762
773
|
if title is None:
|
|
774
|
+
display_delay_fs = int(delay_fs) + float(delay_offset_fs)
|
|
775
|
+
display_delay = azimint_utils.delay_label_value(
|
|
776
|
+
display_delay_fs,
|
|
777
|
+
fs_or_ps=fs_or_ps,
|
|
778
|
+
digits=digits,
|
|
779
|
+
)
|
|
780
|
+
display_unit = general_utils.time_unit_label(fs_or_ps)
|
|
763
781
|
title = (
|
|
764
782
|
f"{sample_name}. {temperature_K}K.\n"
|
|
765
|
-
f"ex. wl={excitation_wl_nm}nm. delay={
|
|
783
|
+
f"ex. wl={excitation_wl_nm}nm. delay={display_delay:g} {display_unit}. tw={time_window_fs}fs.\n"
|
|
766
784
|
f"azim=({azim_window[0]},{azim_window[1]})"
|
|
767
785
|
)
|
|
768
786
|
|
|
@@ -808,7 +826,7 @@ def plot_1D_abs_and_diffs_fluence(
|
|
|
808
826
|
ylim_diff=ylim_diff,
|
|
809
827
|
vlines_peak=vlines_peak,
|
|
810
828
|
vlines_bckg=vlines_bckg,
|
|
811
|
-
legend_title="Fluence [
|
|
829
|
+
legend_title=f"Fluence [{fluence_unit}]",
|
|
812
830
|
legend_loc="upper left",
|
|
813
831
|
legend_outside=True,
|
|
814
832
|
**save_kwargs,
|
|
@@ -28,7 +28,7 @@ from .paths import AnalysisPaths
|
|
|
28
28
|
|
|
29
29
|
@dataclass(frozen=True)
|
|
30
30
|
class PeakSpec:
|
|
31
|
-
"""Define a q-space peak window and its
|
|
31
|
+
"""Define a q-space peak window and its background convention.
|
|
32
32
|
|
|
33
33
|
Attributes
|
|
34
34
|
----------
|
|
@@ -36,14 +36,33 @@ class PeakSpec:
|
|
|
36
36
|
Stable peak identifier written to result tables and figure labels.
|
|
37
37
|
q_range : tuple of float
|
|
38
38
|
Lower and upper q limits of the peak integration window in Å⁻¹.
|
|
39
|
-
bg_mode : {"before", "after", "avg"}
|
|
39
|
+
bg_mode : {"before", "after", "avg", "custom"}
|
|
40
40
|
Location of the equal-width background window. ``"avg"`` averages
|
|
41
|
-
windows immediately below and above the peak interval.
|
|
41
|
+
windows immediately below and above the peak interval. ``"custom"``
|
|
42
|
+
uses the explicit ``bg_range`` interval.
|
|
43
|
+
bg_range : tuple of float, optional
|
|
44
|
+
Explicit q limits for a custom background integration window.
|
|
42
45
|
"""
|
|
43
46
|
|
|
44
47
|
name: str
|
|
45
48
|
q_range: Tuple[float, float]
|
|
46
49
|
bg_mode: str = "after"
|
|
50
|
+
bg_range: Optional[Tuple[float, float]] = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _normalize_q_range(value: Sequence[Any], *, name: str) -> Tuple[float, float]:
|
|
54
|
+
"""Return sorted q limits and validate positive width."""
|
|
55
|
+
try:
|
|
56
|
+
q0, q1 = value
|
|
57
|
+
except Exception:
|
|
58
|
+
raise ValueError(f"{name} must be a two-value q range.")
|
|
59
|
+
|
|
60
|
+
lo, hi = float(q0), float(q1)
|
|
61
|
+
if hi < lo:
|
|
62
|
+
lo, hi = hi, lo
|
|
63
|
+
if hi <= lo:
|
|
64
|
+
raise ValueError(f"{name} must have positive width.")
|
|
65
|
+
return lo, hi
|
|
47
66
|
|
|
48
67
|
|
|
49
68
|
def resolve_bg_mode(spec: Dict[str, Any], bg_mode: Optional[str]) -> str:
|
|
@@ -52,25 +71,31 @@ def resolve_bg_mode(spec: Dict[str, Any], bg_mode: Optional[str]) -> str:
|
|
|
52
71
|
An explicit mode takes precedence over the legacy ``bg_side`` entry.
|
|
53
72
|
``left`` and ``right`` are accepted as aliases for ``before`` and ``after``.
|
|
54
73
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
source = bg_mode
|
|
75
|
+
if source is None and spec.get("bg_mode", None) is not None:
|
|
76
|
+
source = spec.get("bg_mode")
|
|
77
|
+
if source is None and spec.get("bg_side", None) is not None:
|
|
78
|
+
source = spec.get("bg_side")
|
|
79
|
+
|
|
80
|
+
if source is not None:
|
|
81
|
+
m = str(source).strip().lower()
|
|
82
|
+
if m in ("before", "after", "avg", "custom"):
|
|
58
83
|
return m
|
|
59
|
-
if m
|
|
84
|
+
if m in ("left", "before_peak"):
|
|
60
85
|
return "before"
|
|
61
|
-
if m
|
|
86
|
+
if m in ("right", "rigth", "after_peak"):
|
|
62
87
|
return "after"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if "bg_side" in spec and spec["bg_side"] is not None:
|
|
66
|
-
s = str(spec["bg_side"]).strip().lower()
|
|
67
|
-
if s == "left":
|
|
68
|
-
return "before"
|
|
69
|
-
if s == "right":
|
|
70
|
-
return "after"
|
|
71
|
-
if s == "avg":
|
|
88
|
+
if m in ("both", "average"):
|
|
72
89
|
return "avg"
|
|
73
|
-
|
|
90
|
+
if m in ("manual", "range", "explicit"):
|
|
91
|
+
return "custom"
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"bg_mode must be 'before'/'after'/'avg'/'custom' "
|
|
94
|
+
"(or 'left'/'right')."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if spec.get("bg_range", None) is not None:
|
|
98
|
+
return "custom"
|
|
74
99
|
|
|
75
100
|
return "after"
|
|
76
101
|
|
|
@@ -92,7 +117,8 @@ def get_peak_spec(
|
|
|
92
117
|
peak_specs : Optional[Dict[str, Dict[str, Any]]]
|
|
93
118
|
Mapping from peak labels to q ranges and optional background or fit settings.
|
|
94
119
|
bg_mode : Optional[str]
|
|
95
|
-
|
|
120
|
+
Background choice: ``"before"``, ``"after"``, ``"avg"``, or
|
|
121
|
+
``"custom"``.
|
|
96
122
|
|
|
97
123
|
Returns
|
|
98
124
|
-------
|
|
@@ -115,9 +141,35 @@ def get_peak_spec(
|
|
|
115
141
|
if "q_range" not in s:
|
|
116
142
|
raise ValueError(f"Peak '{peak}' spec must include q_range=(q0,q1).")
|
|
117
143
|
|
|
118
|
-
q0, q1 = s["q_range"]
|
|
144
|
+
q0, q1 = _normalize_q_range(s["q_range"], name=f"Peak '{peak}' q_range")
|
|
119
145
|
bm = resolve_bg_mode(s, bg_mode)
|
|
120
|
-
|
|
146
|
+
bg_range = None
|
|
147
|
+
if s.get("bg_range", None) is not None:
|
|
148
|
+
bg_range = _normalize_q_range(
|
|
149
|
+
s["bg_range"],
|
|
150
|
+
name=f"Peak '{peak}' bg_range",
|
|
151
|
+
)
|
|
152
|
+
if bm == "custom" and bg_range is None:
|
|
153
|
+
raise ValueError(
|
|
154
|
+
f"Peak '{peak}' uses bg_mode='custom' and must include bg_range=(q0,q1)."
|
|
155
|
+
)
|
|
156
|
+
return PeakSpec(
|
|
157
|
+
name=str(peak),
|
|
158
|
+
q_range=(float(q0), float(q1)),
|
|
159
|
+
bg_mode=str(bm),
|
|
160
|
+
bg_range=bg_range,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def background_description(peak_spec: PeakSpec) -> str:
|
|
165
|
+
"""Return a compact background description for figure titles and logs."""
|
|
166
|
+
mode = str(peak_spec.bg_mode).strip().lower()
|
|
167
|
+
if mode == "custom" and peak_spec.bg_range is not None:
|
|
168
|
+
q0, q1 = peak_spec.q_range
|
|
169
|
+
b0, b1 = peak_spec.bg_range
|
|
170
|
+
scale = abs(float(q1) - float(q0)) / abs(float(b1) - float(b0))
|
|
171
|
+
return f"custom bg=({b0:.3g}, {b1:.3g}), scale={scale:g}"
|
|
172
|
+
return mode
|
|
121
173
|
|
|
122
174
|
|
|
123
175
|
def select_series_for_fft(
|
|
@@ -410,7 +462,7 @@ class DelayDifferentialAnalyzer:
|
|
|
410
462
|
|
|
411
463
|
Output columns (long-form):
|
|
412
464
|
delay_fs, delay_ps, region, int_delta, int_abs_delta,
|
|
413
|
-
q0, q1, bg0, bg1, bg_mode, peak_name,
|
|
465
|
+
q0, q1, bg0, bg1, bg_mode, bg_scale, peak_width, bg_width, peak_name,
|
|
414
466
|
ref_type, ref_value, azim0, azim1
|
|
415
467
|
|
|
416
468
|
``region`` is either ``"peak"`` or ``"background"``.
|
|
@@ -533,8 +585,9 @@ class DelayDifferentialAnalyzer:
|
|
|
533
585
|
def _bg_ranges(
|
|
534
586
|
q_range: Tuple[float, float],
|
|
535
587
|
bg_mode: str,
|
|
588
|
+
bg_range: Optional[Tuple[float, float]] = None,
|
|
536
589
|
) -> Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]]:
|
|
537
|
-
"""Construct
|
|
590
|
+
"""Construct background interval(s) for a peak range."""
|
|
538
591
|
q0, q1 = float(q_range[0]), float(q_range[1])
|
|
539
592
|
if q1 < q0:
|
|
540
593
|
q0, q1 = q1, q0
|
|
@@ -549,7 +602,37 @@ class DelayDifferentialAnalyzer:
|
|
|
549
602
|
return (q1, q1 + w)
|
|
550
603
|
if m == "avg":
|
|
551
604
|
return ((q0 - w, q0), (q1, q1 + w))
|
|
552
|
-
|
|
605
|
+
if m == "custom":
|
|
606
|
+
if bg_range is None:
|
|
607
|
+
raise ValueError("bg_mode='custom' requires bg_range=(q0,q1).")
|
|
608
|
+
return _normalize_q_range(bg_range, name="bg_range")
|
|
609
|
+
raise ValueError("bg_mode must be 'before', 'after', 'avg', or 'custom'.")
|
|
610
|
+
|
|
611
|
+
@staticmethod
|
|
612
|
+
def _background_width(
|
|
613
|
+
bg_ranges: Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]],
|
|
614
|
+
bg_mode: str,
|
|
615
|
+
) -> float:
|
|
616
|
+
"""Return the effective background q width."""
|
|
617
|
+
if str(bg_mode).strip().lower() == "avg":
|
|
618
|
+
return float(abs(float(bg_ranges[0][1]) - float(bg_ranges[0][0]))) # type: ignore[index]
|
|
619
|
+
return float(abs(float(bg_ranges[1]) - float(bg_ranges[0]))) # type: ignore[arg-type]
|
|
620
|
+
|
|
621
|
+
@classmethod
|
|
622
|
+
def _background_scale(
|
|
623
|
+
cls,
|
|
624
|
+
q_range: Tuple[float, float],
|
|
625
|
+
bg_ranges: Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]],
|
|
626
|
+
bg_mode: str,
|
|
627
|
+
) -> float:
|
|
628
|
+
"""Scale custom-background integrals to the peak q width."""
|
|
629
|
+
if str(bg_mode).strip().lower() != "custom":
|
|
630
|
+
return 1.0
|
|
631
|
+
peak_width = abs(float(q_range[1]) - float(q_range[0]))
|
|
632
|
+
bg_width = cls._background_width(bg_ranges, bg_mode)
|
|
633
|
+
if bg_width <= 0:
|
|
634
|
+
raise ValueError("bg_range must have positive width.")
|
|
635
|
+
return float(peak_width / bg_width)
|
|
553
636
|
|
|
554
637
|
@staticmethod
|
|
555
638
|
def _interp_to_ref_grid(q_ref: np.ndarray, q: np.ndarray, I: np.ndarray) -> np.ndarray:
|
|
@@ -699,7 +782,10 @@ class DelayDifferentialAnalyzer:
|
|
|
699
782
|
|
|
700
783
|
q_range = (float(pk.q_range[0]), float(pk.q_range[1]))
|
|
701
784
|
bg_mode = str(pk.bg_mode).strip().lower()
|
|
702
|
-
bg_ranges = self._bg_ranges(q_range, bg_mode)
|
|
785
|
+
bg_ranges = self._bg_ranges(q_range, bg_mode, pk.bg_range)
|
|
786
|
+
peak_width = abs(float(q_range[1]) - float(q_range[0]))
|
|
787
|
+
bg_width = self._background_width(bg_ranges, bg_mode)
|
|
788
|
+
bg_scale = self._background_scale(q_range, bg_ranges, bg_mode)
|
|
703
789
|
|
|
704
790
|
rows: List[Dict[str, Any]] = []
|
|
705
791
|
|
|
@@ -716,6 +802,9 @@ class DelayDifferentialAnalyzer:
|
|
|
716
802
|
bg0=np.nan,
|
|
717
803
|
bg1=np.nan,
|
|
718
804
|
bg_mode=bg_mode,
|
|
805
|
+
bg_scale=np.nan,
|
|
806
|
+
peak_width=peak_width,
|
|
807
|
+
bg_width=np.nan,
|
|
719
808
|
peak_name=pk.name,
|
|
720
809
|
ref_type=str(ref_type),
|
|
721
810
|
ref_value=str(ref_value),
|
|
@@ -762,6 +851,9 @@ class DelayDifferentialAnalyzer:
|
|
|
762
851
|
bg0=np.nan,
|
|
763
852
|
bg1=np.nan,
|
|
764
853
|
bg_mode=bg_mode,
|
|
854
|
+
bg_scale=np.nan,
|
|
855
|
+
peak_width=peak_width,
|
|
856
|
+
bg_width=np.nan,
|
|
765
857
|
peak_name=pk.name,
|
|
766
858
|
ref_type=str(ref_type),
|
|
767
859
|
ref_value=str(ref_value),
|
|
@@ -783,6 +875,9 @@ class DelayDifferentialAnalyzer:
|
|
|
783
875
|
b_int, b_abs = self._integrals_from_diff(q_ref, diff, (b0, b1))
|
|
784
876
|
bg0, bg1 = float(b0), float(b1)
|
|
785
877
|
|
|
878
|
+
b_int = float(b_int) * float(bg_scale)
|
|
879
|
+
b_abs = float(b_abs) * float(bg_scale)
|
|
880
|
+
|
|
786
881
|
rows.append(
|
|
787
882
|
dict(
|
|
788
883
|
delay_fs=int(d),
|
|
@@ -795,6 +890,9 @@ class DelayDifferentialAnalyzer:
|
|
|
795
890
|
bg0=bg0,
|
|
796
891
|
bg1=bg1,
|
|
797
892
|
bg_mode=bg_mode,
|
|
893
|
+
bg_scale=float(bg_scale),
|
|
894
|
+
peak_width=float(peak_width),
|
|
895
|
+
bg_width=float(bg_width),
|
|
798
896
|
peak_name=pk.name,
|
|
799
897
|
ref_type=str(ref_type),
|
|
800
898
|
ref_value=str(ref_value),
|
|
@@ -985,7 +1083,7 @@ class FluenceDifferentialAnalyzer:
|
|
|
985
1083
|
|
|
986
1084
|
Output columns (long-form):
|
|
987
1085
|
fluence_mJ_cm2, region, int_delta, int_abs_delta,
|
|
988
|
-
q0, q1, bg0, bg1, bg_mode, peak_name,
|
|
1086
|
+
q0, q1, bg0, bg1, bg_mode, bg_scale, peak_width, bg_width, peak_name,
|
|
989
1087
|
ref_type, ref_value, azim0, azim1, delay_fs
|
|
990
1088
|
|
|
991
1089
|
``region`` is either ``"peak"`` or ``"background"``.
|
|
@@ -1108,8 +1206,9 @@ class FluenceDifferentialAnalyzer:
|
|
|
1108
1206
|
def _bg_ranges(
|
|
1109
1207
|
q_range: Tuple[float, float],
|
|
1110
1208
|
bg_mode: str,
|
|
1209
|
+
bg_range: Optional[Tuple[float, float]] = None,
|
|
1111
1210
|
) -> Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]]:
|
|
1112
|
-
"""Construct
|
|
1211
|
+
"""Construct background interval(s) for a peak range."""
|
|
1113
1212
|
q0, q1 = float(q_range[0]), float(q_range[1])
|
|
1114
1213
|
if q1 < q0:
|
|
1115
1214
|
q0, q1 = q1, q0
|
|
@@ -1124,7 +1223,37 @@ class FluenceDifferentialAnalyzer:
|
|
|
1124
1223
|
return (q1, q1 + w)
|
|
1125
1224
|
if m == "avg":
|
|
1126
1225
|
return ((q0 - w, q0), (q1, q1 + w))
|
|
1127
|
-
|
|
1226
|
+
if m == "custom":
|
|
1227
|
+
if bg_range is None:
|
|
1228
|
+
raise ValueError("bg_mode='custom' requires bg_range=(q0,q1).")
|
|
1229
|
+
return _normalize_q_range(bg_range, name="bg_range")
|
|
1230
|
+
raise ValueError("bg_mode must be 'before', 'after', 'avg', or 'custom'.")
|
|
1231
|
+
|
|
1232
|
+
@staticmethod
|
|
1233
|
+
def _background_width(
|
|
1234
|
+
bg_ranges: Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]],
|
|
1235
|
+
bg_mode: str,
|
|
1236
|
+
) -> float:
|
|
1237
|
+
"""Return the effective background q width."""
|
|
1238
|
+
if str(bg_mode).strip().lower() == "avg":
|
|
1239
|
+
return float(abs(float(bg_ranges[0][1]) - float(bg_ranges[0][0]))) # type: ignore[index]
|
|
1240
|
+
return float(abs(float(bg_ranges[1]) - float(bg_ranges[0]))) # type: ignore[arg-type]
|
|
1241
|
+
|
|
1242
|
+
@classmethod
|
|
1243
|
+
def _background_scale(
|
|
1244
|
+
cls,
|
|
1245
|
+
q_range: Tuple[float, float],
|
|
1246
|
+
bg_ranges: Union[Tuple[float, float], Tuple[Tuple[float, float], Tuple[float, float]]],
|
|
1247
|
+
bg_mode: str,
|
|
1248
|
+
) -> float:
|
|
1249
|
+
"""Scale custom-background integrals to the peak q width."""
|
|
1250
|
+
if str(bg_mode).strip().lower() != "custom":
|
|
1251
|
+
return 1.0
|
|
1252
|
+
peak_width = abs(float(q_range[1]) - float(q_range[0]))
|
|
1253
|
+
bg_width = cls._background_width(bg_ranges, bg_mode)
|
|
1254
|
+
if bg_width <= 0:
|
|
1255
|
+
raise ValueError("bg_range must have positive width.")
|
|
1256
|
+
return float(peak_width / bg_width)
|
|
1128
1257
|
|
|
1129
1258
|
@staticmethod
|
|
1130
1259
|
def _interp_to_ref_grid(q_ref: np.ndarray, q: np.ndarray, I: np.ndarray) -> np.ndarray:
|
|
@@ -1271,7 +1400,10 @@ class FluenceDifferentialAnalyzer:
|
|
|
1271
1400
|
|
|
1272
1401
|
q_range = (float(pk.q_range[0]), float(pk.q_range[1]))
|
|
1273
1402
|
bg_mode = str(pk.bg_mode).strip().lower()
|
|
1274
|
-
bg_ranges = self._bg_ranges(q_range, bg_mode)
|
|
1403
|
+
bg_ranges = self._bg_ranges(q_range, bg_mode, pk.bg_range)
|
|
1404
|
+
peak_width = abs(float(q_range[1]) - float(q_range[0]))
|
|
1405
|
+
bg_width = self._background_width(bg_ranges, bg_mode)
|
|
1406
|
+
bg_scale = self._background_scale(q_range, bg_ranges, bg_mode)
|
|
1275
1407
|
|
|
1276
1408
|
rows: List[Dict[str, Any]] = []
|
|
1277
1409
|
|
|
@@ -1287,6 +1419,9 @@ class FluenceDifferentialAnalyzer:
|
|
|
1287
1419
|
bg0=np.nan,
|
|
1288
1420
|
bg1=np.nan,
|
|
1289
1421
|
bg_mode=bg_mode,
|
|
1422
|
+
bg_scale=np.nan,
|
|
1423
|
+
peak_width=peak_width,
|
|
1424
|
+
bg_width=np.nan,
|
|
1290
1425
|
peak_name=pk.name,
|
|
1291
1426
|
ref_type=str(ref_type),
|
|
1292
1427
|
ref_value=str(ref_value),
|
|
@@ -1332,6 +1467,9 @@ class FluenceDifferentialAnalyzer:
|
|
|
1332
1467
|
bg0=np.nan,
|
|
1333
1468
|
bg1=np.nan,
|
|
1334
1469
|
bg_mode=bg_mode,
|
|
1470
|
+
bg_scale=np.nan,
|
|
1471
|
+
peak_width=peak_width,
|
|
1472
|
+
bg_width=np.nan,
|
|
1335
1473
|
peak_name=pk.name,
|
|
1336
1474
|
ref_type=str(ref_type),
|
|
1337
1475
|
ref_value=str(ref_value),
|
|
@@ -1353,6 +1491,9 @@ class FluenceDifferentialAnalyzer:
|
|
|
1353
1491
|
b_int, b_abs = self._integrals_from_diff(q_ref, diff, (b0, b1))
|
|
1354
1492
|
bg0, bg1 = float(b0), float(b1)
|
|
1355
1493
|
|
|
1494
|
+
b_int = float(b_int) * float(bg_scale)
|
|
1495
|
+
b_abs = float(b_abs) * float(bg_scale)
|
|
1496
|
+
|
|
1356
1497
|
rows.append(
|
|
1357
1498
|
dict(
|
|
1358
1499
|
fluence_mJ_cm2=float(f),
|
|
@@ -1364,6 +1505,9 @@ class FluenceDifferentialAnalyzer:
|
|
|
1364
1505
|
bg0=bg0,
|
|
1365
1506
|
bg1=bg1,
|
|
1366
1507
|
bg_mode=bg_mode,
|
|
1508
|
+
bg_scale=float(bg_scale),
|
|
1509
|
+
peak_width=float(peak_width),
|
|
1510
|
+
bg_width=float(bg_width),
|
|
1367
1511
|
peak_name=pk.name,
|
|
1368
1512
|
ref_type=str(ref_type),
|
|
1369
1513
|
ref_value=str(ref_value),
|
|
@@ -1824,7 +1968,9 @@ def build_multi_fluence_integral_series(
|
|
|
1824
1968
|
|
|
1825
1969
|
Each returned dict has keys:
|
|
1826
1970
|
- "experiment": original experiment dict
|
|
1971
|
+
- "fluence_scale": float (optional x-scale for plotting)
|
|
1827
1972
|
- "fluence_offset": float (optional x-shift for plotting)
|
|
1973
|
+
- "delay_offset_fs": float (optional display offset for fixed-delay labels)
|
|
1828
1974
|
- "fluence_mJ_cm2": 1D np.ndarray (x axis, no offset)
|
|
1829
1975
|
- "int_delta": 1D np.ndarray (peak ΔI)
|
|
1830
1976
|
- "int_abs_delta": 1D np.ndarray (peak |ΔI|)
|
|
@@ -1865,7 +2011,14 @@ def build_multi_fluence_integral_series(
|
|
|
1865
2011
|
if ref_value is None:
|
|
1866
2012
|
raise ValueError("Each experiment must provide ref_value (e.g. dark scans or fluence).")
|
|
1867
2013
|
|
|
2014
|
+
fluence_scale = float(exp.get("fluence_scale", 1.0))
|
|
1868
2015
|
fluence_offset = float(exp.get("fluence_offset", 0.0))
|
|
2016
|
+
delay_offset_fs = float(
|
|
2017
|
+
exp.get(
|
|
2018
|
+
"delay_offset_fs",
|
|
2019
|
+
float(exp.get("delay_offset_ps", 0.0)) * 1000.0,
|
|
2020
|
+
)
|
|
2021
|
+
)
|
|
1869
2022
|
|
|
1870
2023
|
analyzer = FluenceDifferentialAnalyzer(
|
|
1871
2024
|
sample_name=sample_name,
|
|
@@ -1929,7 +2082,9 @@ def build_multi_fluence_integral_series(
|
|
|
1929
2082
|
series_list.append(
|
|
1930
2083
|
dict(
|
|
1931
2084
|
experiment=exp,
|
|
2085
|
+
fluence_scale=float(fluence_scale),
|
|
1932
2086
|
fluence_offset=float(fluence_offset),
|
|
2087
|
+
delay_offset_fs=float(delay_offset_fs),
|
|
1933
2088
|
fluence_mJ_cm2=np.asarray(x_flu, float),
|
|
1934
2089
|
int_delta=np.asarray(y_delta, float),
|
|
1935
2090
|
int_abs_delta=np.asarray(y_abs, float),
|