Skip to content

Commit

Permalink
requested changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jackbdoughty committed Dec 4, 2024
1 parent 580c9f1 commit d864579
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
2 changes: 1 addition & 1 deletion doc/fitting/livefit_logger.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Fitting Files Callback
## Fitting Files

The callback (`LiveFitLogger`) exists to write all fitting metrics from `LiveFit` to file. These are designed to be human readable files rather than machine readable.
The callback ({py:obj}`ibex_bluesky_core.fitting.livefit_logger.LiveFitLogger`) exists to write all fitting metrics from `LiveFit` to file. These are designed to be human readable files rather than machine readable.

This callback provides you with useful metrics such as `R-squared` and `chi-square`, then providing you with a table of the raw collected data included modelled `y` data and `y` uncertainty.

Expand Down
18 changes: 12 additions & 6 deletions src/ibex_bluesky_core/callbacks/fitting/livefit_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,18 @@ def event(self, doc: Event) -> Event:
"""
event_data = doc[DATA]

assert self.x in event_data, f"{self.x} is not in event document."
assert self.y in event_data, f"{self.y} is not in event document."
if self.x not in event_data:
raise OSError(f"{self.x} is not in event document.")

if self.y not in event_data:
raise OSError(f"{self.y} is not in event document.")

self.x_data.append(event_data[self.x])
self.y_data.append(event_data[self.y])

if self.yerr is not None:
assert self.yerr in event_data, f"{self.yerr} is not in event document."
if self.yerr not in event_data:
raise OSError(f"{self.yerr} is not in event document.")
self.yerr_data.append(event_data[self.yerr])

return super().event(doc)
Expand All @@ -104,10 +108,11 @@ def stop(self, doc: RunStop) -> None:
kwargs.update(self.livefit.result.values)
self.y_fit_data = self.livefit.result.model.eval(**kwargs)

self.stats = str(self.livefit.result.fit_report()).split("\n")
self.stats = str(self.livefit.result.fit_report())
self.stats = self.stats.replace('"', "").split("\n")

# Writing to csv file
with open(self.filename, "w", newline="") as csvfile:
with open(self.filename, "w", newline="", encoding="utf-8") as csvfile:
# Writing the data
self.csvwriter = csv.writer(csvfile)

Expand All @@ -122,7 +127,8 @@ def stop(self, doc: RunStop) -> None:
else:
self.write_fields_table_uncertainty()

logger.info(f"Fitting information successfully written to {self.filename}")
logging.basicConfig(format="%(message)s", level=logging.INFO)
logger.info("Fitting information successfully written to: %s", self.filename.name)

def write_fields_table(self) -> None:
"""Write collected run info to the fitting file."""
Expand Down
63 changes: 57 additions & 6 deletions tests/callbacks/fitting/test_fit_logging_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path
from unittest.mock import MagicMock, mock_open, patch

import pytest
from bluesky.plans import scan
from ophyd_async.core import soft_signal_rw

Expand Down Expand Up @@ -30,11 +31,9 @@ def test_after_fitting_callback_writes_to_file_successfully_no_y_uncertainity(
assert m.call_args_list[0].args == (filepath / f"{result.run_start_uids[0]}{postfix}.csv", "w") # type: ignore

handle = m()
rows = []
rows = [i.args[0] for i in handle.write.call_args_list]

# Check that it starts writing to the file in the expected way
for i in handle.write.call_args_list:
rows.append(i.args[0])

assert f" Model({Linear.__name__} [{Linear.equation}])\r\n" in rows
assert "x,y,modelled y\r\n" in rows
Expand Down Expand Up @@ -62,11 +61,9 @@ def test_after_fitting_callback_writes_to_file_successfully_with_y_uncertainity(
assert m.call_args_list[0].args == (filepath / f"{result.run_start_uids[0]}{postfix}.csv", "w") # type: ignore

handle = m()
rows = []
rows = [i.args[0] for i in handle.write.call_args_list]

# Check that it starts writing to the file in the expected way
for i in handle.write.call_args_list:
rows.append(i.args[0])

assert f" Model({Linear.__name__} [{Linear.equation}])\r\n" in rows
assert "x,y,y uncertainty,modelled y\r\n" in rows
Expand All @@ -91,3 +88,57 @@ def test_file_not_written_if_no_fitting_result(RE: run_engine.RunEngine):
RE(scan([invariant], mot, -1, 1, 3), [lf, lfl])

assert not m.called


def test_error_thrown_if_no_x_data_in_event(RE: run_engine.RunEngine):
filepath = Path("C:\\") / "instrument" / "var" / "logs"
postfix = "fit1"

lf = LiveFit(Linear.fit(), y="invariant", x="motor", update_every=50)
lfl = LiveFitLogger(lf, y="invariant", x="motor", output_dir=filepath, postfix=postfix)

with pytest.raises(IOError, match="motor is not in event document."):
lfl.event(
{
"data": { # type: ignore
"invariant": 2,
}
}
)


def test_error_thrown_if_no_y_data_in_event(RE: run_engine.RunEngine):
filepath = Path("C:\\") / "instrument" / "var" / "logs"
postfix = "fit1"

lf = LiveFit(Linear.fit(), y="invariant", x="motor", update_every=50)
lfl = LiveFitLogger(lf, y="invariant", x="motor", output_dir=filepath, postfix=postfix)

with pytest.raises(IOError, match="invariant is not in event document."):
lfl.event(
{
"data": { # type: ignore
"motor": 2,
}
}
)


def test_error_thrown_if_no_y_err_data_in_event(RE: run_engine.RunEngine):
filepath = Path("C:\\") / "instrument" / "var" / "logs"
postfix = "fit1"

lf = LiveFit(Linear.fit(), y="invariant", x="motor", update_every=50)
lfl = LiveFitLogger(
lf, y="invariant", x="motor", output_dir=filepath, postfix=postfix, yerr="yerr"
)

with pytest.raises(IOError, match="yerr is not in event document."):
lfl.event(
{
"data": { # type: ignore
"motor": 2,
"invariant": 2,
}
}
)

0 comments on commit d864579

Please sign in to comment.