Skip to content

Commit e0ec72b

Browse files
committed
Merge remote-tracking branch 'origin/main' into mpes_elab_metadata
2 parents 037dfb3 + f1bb527 commit e0ec72b

11 files changed

+214
-94
lines changed

.cspell/custom-dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ coldist
5353
colgrid
5454
COLLECTIONCOLUMN
5555
colname
56+
colorbar
5657
colsize
5758
COMPES
5859
coordmat

src/sed/binning/binning.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ def normalization_histogram_from_timed_dataframe(
465465
axis: str,
466466
bin_centers: np.ndarray,
467467
time_unit: float,
468+
**kwds,
468469
) -> xr.DataArray:
469470
"""Get a normalization histogram from a timed dataframe.
470471
@@ -475,17 +476,12 @@ def normalization_histogram_from_timed_dataframe(
475476
histogram.
476477
bin_centers (np.ndarray): Bin centers used for binning of the axis.
477478
time_unit (float): Time unit the data frame entries are based on.
479+
**kwds: Additional keyword arguments passed to the bin_dataframe function.
478480
479481
Returns:
480482
xr.DataArray: Calculated normalization histogram.
481483
"""
482-
bins = df[axis].map_partitions(
483-
pd.cut,
484-
bins=bin_centers_to_bin_edges(bin_centers),
485-
)
486-
487-
histogram = df[axis].groupby([bins]).count().compute().values * time_unit
488-
# histogram = bin_dataframe(df, axes=[axis], bins=[bin_centers]) * time_unit
484+
histogram = bin_dataframe(df, axes=[axis], bins=[bin_centers], **kwds) * time_unit
489485

490486
data_array = xr.DataArray(
491487
data=histogram,

src/sed/core/logging.py

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import sys
1212
from datetime import datetime
1313
from functools import wraps
14+
from inspect import signature
1415
from typing import Callable
1516

1617
# Default log directory
@@ -43,28 +44,33 @@ def setup_logging(
4344
# Create base logger
4445
base_logger = logging.getLogger("sed")
4546
base_logger.setLevel(logging.DEBUG) # Set the minimum log level for the logger
46-
if set_base_handler or not base_logger.hasHandlers():
47-
if base_logger.hasHandlers():
47+
if set_base_handler or len(base_logger.handlers) == 0:
48+
if len(base_logger.handlers):
4849
base_logger.handlers.clear()
4950

5051
# Determine log file path
5152
if user_log_path is None:
5253
user_log_path = DEFAULT_LOG_DIR
53-
os.makedirs(user_log_path, exist_ok=True)
54-
log_file = os.path.join(user_log_path, f"sed_{datetime.now().strftime('%Y-%m-%d')}.log")
55-
56-
# Create file handler and set level to debug
57-
file_handler = logging.FileHandler(log_file)
58-
file_handler.setLevel(FILE_VERBOSITY)
59-
60-
# Create formatter for file
61-
file_formatter = logging.Formatter(
62-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s in %(filename)s:%(lineno)d",
63-
)
64-
file_handler.setFormatter(file_formatter)
65-
66-
# Add file handler to logger
67-
base_logger.addHandler(file_handler)
54+
try:
55+
os.makedirs(user_log_path, exist_ok=True)
56+
log_file = os.path.join(user_log_path, f"sed_{datetime.now().strftime('%Y-%m-%d')}.log")
57+
58+
# Create file handler and set level to debug
59+
file_handler = logging.FileHandler(log_file)
60+
file_handler.setLevel(FILE_VERBOSITY)
61+
62+
# Create formatter for file
63+
file_formatter = logging.Formatter(
64+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s in %(filename)s:%(lineno)d",
65+
)
66+
file_handler.setFormatter(file_formatter)
67+
68+
# Add file handler to logger
69+
base_logger.addHandler(file_handler)
70+
except PermissionError:
71+
logging.warning(f"Cannot create logfile in Folder {user_log_path}, disabling logfile.")
72+
base_logger.addHandler(logging.NullHandler())
73+
base_logger.propagate = False
6874

6975
# create named logger
7076
logger = base_logger.getChild(name)
@@ -109,7 +115,11 @@ def log_call(func: Callable):
109115
def new_func(*args, **kwargs):
110116
saved_args = locals()
111117
args_str = ""
112-
for arg in saved_args["args"][1:]:
118+
for arg in (
119+
saved_args["args"][1:]
120+
if "self" in signature(func).parameters
121+
else saved_args["args"]
122+
):
113123
args_str += f"{arg}, "
114124
for name, arg in saved_args["kwargs"].items():
115125
args_str += f"{name}={arg}, "

src/sed/core/processor.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,8 @@ def compute(
22832283
)
22842284
# if the axes are named correctly, xarray figures out the normalization correctly
22852285
self._normalized = self._binned / self._normalization_histogram
2286+
# Set datatype of binned data
2287+
self._normalized.data = self._normalized.data.astype(self._binned.data.dtype)
22862288
self._attributes.add(
22872289
self._normalization_histogram.values,
22882290
name="normalization_histogram",
@@ -2353,36 +2355,35 @@ def get_normalization_histogram(
23532355

23542356
if isinstance(df_partitions, int):
23552357
df_partitions = list(range(0, min(df_partitions, self._dataframe.npartitions)))
2358+
23562359
if use_time_stamps or self._timed_dataframe is None:
23572360
if df_partitions is not None:
2358-
self._normalization_histogram = normalization_histogram_from_timestamps(
2359-
self._dataframe.partitions[df_partitions],
2360-
axis,
2361-
self._binned.coords[axis].values,
2362-
self._config["dataframe"]["columns"]["timestamp"],
2363-
)
2361+
dataframe = self._dataframe.partitions[df_partitions]
23642362
else:
2365-
self._normalization_histogram = normalization_histogram_from_timestamps(
2366-
self._dataframe,
2367-
axis,
2368-
self._binned.coords[axis].values,
2369-
self._config["dataframe"]["columns"]["timestamp"],
2370-
)
2363+
dataframe = self._dataframe
2364+
self._normalization_histogram = normalization_histogram_from_timestamps(
2365+
df=dataframe,
2366+
axis=axis,
2367+
bin_centers=self._binned.coords[axis].values,
2368+
time_stamp_column=self._config["dataframe"]["columns"]["timestamp"],
2369+
)
23712370
else:
23722371
if df_partitions is not None:
2373-
self._normalization_histogram = normalization_histogram_from_timed_dataframe(
2374-
self._timed_dataframe.partitions[df_partitions],
2375-
axis,
2376-
self._binned.coords[axis].values,
2377-
self._config["dataframe"]["timed_dataframe_unit_time"],
2378-
)
2372+
timed_dataframe = self._timed_dataframe.partitions[df_partitions]
23792373
else:
2380-
self._normalization_histogram = normalization_histogram_from_timed_dataframe(
2381-
self._timed_dataframe,
2382-
axis,
2383-
self._binned.coords[axis].values,
2384-
self._config["dataframe"]["timed_dataframe_unit_time"],
2385-
)
2374+
timed_dataframe = self._timed_dataframe
2375+
self._normalization_histogram = normalization_histogram_from_timed_dataframe(
2376+
df=timed_dataframe,
2377+
axis=axis,
2378+
bin_centers=self._binned.coords[axis].values,
2379+
time_unit=self._config["dataframe"]["timed_dataframe_unit_time"],
2380+
hist_mode=self.config["binning"]["hist_mode"],
2381+
mode=self.config["binning"]["mode"],
2382+
pbar=self.config["binning"]["pbar"],
2383+
n_cores=self.config["core"]["num_cores"],
2384+
threads_per_worker=self.config["binning"]["threads_per_worker"],
2385+
threadpool_api=self.config["binning"]["threadpool_API"],
2386+
)
23862387

23872388
return self._normalization_histogram
23882389

tests/test_logging.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import io
2+
import logging
3+
import os
4+
from datetime import datetime
5+
6+
import pytest
7+
8+
from sed.core.logging import call_logger
9+
from sed.core.logging import set_verbosity
10+
from sed.core.logging import setup_logging
11+
12+
13+
@pytest.fixture
14+
def logger_():
15+
logger = setup_logging("test_logger")
16+
log_capture_string = io.StringIO()
17+
ch = logging.StreamHandler(log_capture_string)
18+
ch.setLevel(logging.DEBUG)
19+
logger.addHandler(ch)
20+
yield logger, log_capture_string
21+
22+
23+
def test_debug_logging(logger_):
24+
logger, log_capture_string = logger_
25+
logger.debug("This is a debug message")
26+
assert "This is a debug message" in log_capture_string.getvalue()
27+
28+
29+
def test_info_logging(logger_):
30+
logger, log_capture_string = logger_
31+
logger.info("This is an info message")
32+
assert "This is an info message" in log_capture_string.getvalue()
33+
34+
35+
def test_warning_logging(logger_):
36+
logger, log_capture_string = logger_
37+
logger.warning("This is a warning message")
38+
assert "This is a warning message" in log_capture_string.getvalue()
39+
40+
41+
def test_error_logging(logger_):
42+
logger, log_capture_string = logger_
43+
logger.error("This is an error message")
44+
assert "This is an error message" in log_capture_string.getvalue()
45+
46+
47+
def test_critical_logging(logger_):
48+
logger, log_capture_string = logger_
49+
logger.critical("This is a critical message")
50+
assert "This is a critical message" in log_capture_string.getvalue()
51+
52+
53+
def test_set_verbosity(logger_):
54+
logger, log_capture_string = logger_
55+
set_verbosity(logger, verbose=True)
56+
assert logger.handlers[0].level == logging.INFO
57+
set_verbosity(logger, verbose=False)
58+
assert logger.handlers[0].level == logging.WARNING
59+
60+
61+
def test_logger_has_base_logger(logger_):
62+
logger, log_capture_string = logger_
63+
assert logger.name == "sed.test_logger"
64+
assert logger.parent.name == "sed"
65+
assert logger.parent.parent.name == "root"
66+
assert logger.parent.level == logging.DEBUG
67+
assert isinstance(logger.parent.handlers[0], logging.FileHandler)
68+
69+
70+
def test_logger_creates_logfile(tmp_path):
71+
logger = setup_logging("test_logger", set_base_handler=True, user_log_path=tmp_path)
72+
log_file = os.path.join(tmp_path, f"sed_{datetime.now().strftime('%Y-%m-%d')}.log")
73+
assert os.path.exists(log_file)
74+
with open(log_file) as f:
75+
assert f.read() == ""
76+
logger.debug("This is a debug message")
77+
with open(log_file) as f:
78+
assert "This is a debug message" in f.read()
79+
80+
81+
def test_readonly_path(tmp_path, caplog):
82+
os.chmod(tmp_path, 0o444)
83+
with caplog.at_level(logging.WARNING):
84+
setup_logging("test_logger", set_base_handler=True, user_log_path=tmp_path)
85+
assert f"Cannot create logfile in Folder {tmp_path}, disabling logfile." in caplog.messages[0]
86+
log_file = os.path.join(tmp_path, f"sed_{datetime.now().strftime('%Y-%m-%d')}.log")
87+
assert not os.path.exists(log_file)
88+
89+
90+
def test_call_logger(logger_):
91+
logger, log_capture_string = logger_
92+
93+
@call_logger(logger)
94+
def test_function(test_param=None): # noqa: ARG001
95+
return
96+
97+
test_function(test_param=[1, 3, 5])
98+
assert "test_function(test_param=[1, 3, 5])" in log_capture_string.getvalue()
99+
100+
test_function([1, 3, 5])
101+
assert "test_function([1, 3, 5])" in log_capture_string.getvalue()
102+
103+
test_function()
104+
assert "test_function()" in log_capture_string.getvalue()
105+
106+
class TestClass:
107+
@call_logger(logger)
108+
def test_method(self, test_param=None): # noqa: ARG002
109+
return
110+
111+
test_instance = TestClass()
112+
test_instance.test_method(test_param=[1, 3, 5])
113+
assert "test_method(test_param=[1, 3, 5])" in log_capture_string.getvalue()

tests/test_processor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,8 @@ def test_compute_with_normalization() -> None:
10081008
processor.binned.data,
10091009
(processor.normalized * processor.normalization_histogram).data,
10101010
)
1011+
# check dtype
1012+
assert processor.normalized.dtype == processor.binned.dtype
10111013
# bin only second dataframe partition
10121014
result2 = processor.compute(
10131015
bins=bins,

tutorial/10_hextof_workflow_trXPS_bam_correction.ipynb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@
238238
"metadata": {},
239239
"outputs": [],
240240
"source": [
241-
"fig,ax = plt.subplots(1,2,figsize=(8,3), layout='constrained')\n",
241+
"fig,ax = plt.subplots(1,2,figsize=(6,2.25), layout='constrained')\n",
242242
"res.plot(robust=True, ax=ax[0], cmap='terrain')\n",
243243
"fig.suptitle(f\"Run {run_number}: W 4f, side bands\")\n",
244244
"ax[0].set_title('raw')\n",
@@ -338,7 +338,7 @@
338338
"outputs": [],
339339
"source": [
340340
"\n",
341-
"fig,ax = plt.subplots(1,2,figsize=(8,3), layout='constrained')\n",
341+
"fig,ax = plt.subplots(1,2,figsize=(6,2.25), layout='constrained')\n",
342342
"res_bam.sel(bam=slice(-6400,-5100)).sum('trainId').plot(ax=ax[0],robust=True, cmap='terrain')\n",
343343
"res_bam.sel(bam=slice(-6400,-5100)).sum('pulseId').plot(ax=ax[1],robust=True, cmap='terrain')\n",
344344
"plt.show()"
@@ -404,7 +404,7 @@
404404
"metadata": {},
405405
"outputs": [],
406406
"source": [
407-
"fig,ax = plt.subplots(1,2,figsize=(8,3), layout='constrained')\n",
407+
"fig,ax = plt.subplots(1,2,figsize=(6,2.25), layout='constrained')\n",
408408
"fig.suptitle(f\"Run {run_number}: W 4f, side bands\")\n",
409409
"res_corr.plot(robust=True, ax=ax[0], cmap='terrain')\n",
410410
"ax[0].set_title('raw')\n",
@@ -497,7 +497,7 @@
497497
"metadata": {},
498498
"outputs": [],
499499
"source": [
500-
"fig,ax=plt.subplots(2,2,figsize=(9,7),layout=\"constrained\")\n",
500+
"fig,ax=plt.subplots(2,2,figsize=(6,6),layout=\"constrained\")\n",
501501
"\n",
502502
"plt.axes(ax[0,0])\n",
503503
"res.plot(cmap='terrain', robust=True)\n",
@@ -523,7 +523,7 @@
523523
"plt.legend(loc=1)\n",
524524
"plt.title(\"Sidebands with bam correction\")\n",
525525
"\n",
526-
"fig.suptitle(f'Run {run_number}: Effect of BAM correction',fontsize='22')"
526+
"fig.suptitle(f'Run {run_number}: Effect of BAM correction',fontsize='14')"
527527
]
528528
},
529529
{
@@ -545,7 +545,7 @@
545545
],
546546
"metadata": {
547547
"kernelspec": {
548-
"display_name": "sed-processor-7Jy-bAA8-py3.9",
548+
"display_name": ".venv",
549549
"language": "python",
550550
"name": "python3"
551551
},
@@ -559,7 +559,7 @@
559559
"name": "python",
560560
"nbconvert_exporter": "python",
561561
"pygments_lexer": "ipython3",
562-
"version": "3.9.19"
562+
"version": "3.12.8"
563563
}
564564
},
565565
"nbformat": 4,

tutorial/11_hextof_workflow_trXPS_energy_calibration_using_SB.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@
327327
"bins = [100,60]\n",
328328
"res_corr = sp_44498.compute(bins=bins, axes=axes, ranges=ranges, normalize_to_acquisition_time=\"delayStage\")\n",
329329
"\n",
330-
"fig,ax = plt.subplots(1,2,figsize=(8,3), layout='constrained')\n",
330+
"fig,ax = plt.subplots(1,2,figsize=(6,2.25), layout='constrained')\n",
331331
"fig.suptitle(f\"Run {run_number}: W 4f, side bands\")\n",
332332
"res_corr.plot(ax=ax[0], cmap='terrain')\n",
333333
"ax[0].set_title('raw')\n",
@@ -443,7 +443,7 @@
443443
"bins = [200,60]\n",
444444
"res_corr = sp_44498.compute(bins=bins, axes=axes, ranges=ranges, normalize_to_acquisition_time=\"delayStage\")\n",
445445
"\n",
446-
"fig,ax = plt.subplots(1,2,figsize=(8,3), layout='constrained')\n",
446+
"fig,ax = plt.subplots(1,2,figsize=(6,2.25), layout='constrained')\n",
447447
"fig.suptitle(f\"Run {run_number}: W 4f, side bands\")\n",
448448
"res_corr.plot(ax=ax[0], cmap='terrain')\n",
449449
"ax[0].set_title('raw')\n",

0 commit comments

Comments
 (0)