diff --git a/docs/examples/sequential_fit_example.py b/docs/examples/sequential_fit_example.py index 9824fa2..bccf4b6 100644 --- a/docs/examples/sequential_fit_example.py +++ b/docs/examples/sequential_fit_example.py @@ -1,12 +1,12 @@ -from pdfbl.sequential.sequential_runner import SequentialPDFFitRunner +from pdfbl.sequential.sequential_cmi_runner import SequentialCMIRunner -sts = SequentialPDFFitRunner() +sts = SequentialCMIRunner() sts.load_inputs( - input_data_dir="data/input_files", - structure_path="data/Ni.cif", - output_result_dir="data/results", + input_data_dir="docs/examples/input_files", + structure_path="docs/examples/Ni.cif", + output_result_dir="docs/examples/results", filename_order_pattern=r"(\d+)K\.gr", - refine_variable_names=[ + refinable_variable_names=[ "a_1", "s0", "Uiso_0_1", @@ -27,9 +27,9 @@ dx=0.01, qmax=25, qmin=0.1, - # whether_plot_y=True, - # whether_plot_ycalc=True, + whether_plot_y=True, + whether_plot_ycalc=True, plot_variable_names=["a_1"], plot_result_entry_names=["residual"], ) -sts.start(mode="stream") +sts.run(mode="stream") diff --git a/news/clean-up.rst b/news/clean-up.rst new file mode 100644 index 0000000..d0647be --- /dev/null +++ b/news/clean-up.rst @@ -0,0 +1,23 @@ +**Added:** + +* No news added: Completed some clean up jobs. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/requirements/conda.txt b/requirements/conda.txt index d117d69..9a9a174 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -3,4 +3,5 @@ diffpy.cmi scipy prompt_toolkit matplotlib -bg_mpl_stylesheets +bg-mpl-stylesheets +psutil diff --git a/src/pdfbl/sequential/pdfadapter.py b/src/pdfbl/sequential/pdfadapter.py index a689339..954c180 100644 --- a/src/pdfbl/sequential/pdfadapter.py +++ b/src/pdfbl/sequential/pdfadapter.py @@ -14,7 +14,7 @@ from diffpy.srfit.pdf import PDFGenerator, PDFParser from diffpy.srfit.structure import constrainAsSpaceGroup from diffpy.structure.parsers import getParser -from scipy.optimize import least_squares +from scipy.optimize import minimize class PDFAdapter: @@ -44,8 +44,8 @@ class PDFAdapter: Update parameter values from the provided dictionary. refine_variables(variable_names) Refine the parameters specified in the list and in that order. - get_parameter_names() - Get the names of all parameters in the recipe. + get_variable_names() + Get the names of all variables in the recipe. save_results(mode="str", filename=None) Save the fitting results. """ @@ -278,19 +278,18 @@ def refine_variables(self, variable_names: list[str]): """ for vname in variable_names: self.recipe.free(vname) - least_squares( - self.recipe.residual, + minimize( + self.recipe.scalarResidual, self.recipe.values, - x_scale="jac", ) - def get_parameter_names(self) -> list[str]: - """Get the names of all parameters in the recipe. + def get_variable_names(self) -> list[str]: + """Get the names of all variables in the recipe. Returns ------- list of str - A list of parameter names. + A list of variable names. """ return list(self.recipe._parameters.keys()) diff --git a/src/pdfbl/sequential/sequential_runner.py b/src/pdfbl/sequential/sequential_cmi_runner.py similarity index 86% rename from src/pdfbl/sequential/sequential_runner.py rename to src/pdfbl/sequential/sequential_cmi_runner.py index d30b9fb..25bad59 100644 --- a/src/pdfbl/sequential/sequential_runner.py +++ b/src/pdfbl/sequential/sequential_cmi_runner.py @@ -16,13 +16,13 @@ plt.style.use(all_styles["bg-style"]) -class SequentialPDFFitRunner: +class SequentialCMIRunner: def __init__(self): self.input_files_known = [] self.input_files_completed = [] self.input_files_running = [] self.adapter = PDFAdapter() - self.plot_data = {} + self.data_for_plot = {} def load_inputs( self, @@ -34,7 +34,7 @@ def load_inputs( whether_plot_ycalc=False, plot_variable_names=None, plot_result_entry_names=None, - refine_variable_names=None, + refinable_variable_names=None, initial_variable_values=None, xmin=None, xmax=None, @@ -52,7 +52,7 @@ def load_inputs( "dx": dx, "qmin": qmin, "qmax": qmax, - "refine_variable_names": refine_variable_names or [], + "refinable_variable_names": refinable_variable_names or [], "initial_variable_values": initial_variable_values or {}, "whether_plot_y": whether_plot_y, "whether_plot_ycalc": whether_plot_ycalc, @@ -66,7 +66,7 @@ def load_inputs( label="ycalc", color=plt.rcParams["axes.prop_cycle"].by_key()["color"][0], ) - self.plot_data["ycalc"] = { + self.data_for_plot["ycalc"] = { "line": line, "xdata": Queue(), "ydata": Queue(), @@ -77,7 +77,7 @@ def load_inputs( label="y", color=plt.rcParams["axes.prop_cycle"].by_key()["color"][1], ) - self.plot_data["y"] = { + self.data_for_plot["y"] = { "line": line, "xdata": Queue(), "ydata": Queue(), @@ -85,7 +85,7 @@ def load_inputs( elif whether_plot_ycalc: fig, ax = plt.subplots() (line,) = ax.plot([], [], label="ycalc") - self.plot_data["ycalc"] = { + self.data_for_plot["ycalc"] = { "line": line, "xdata": Queue(), "ydata": Queue(), @@ -93,26 +93,26 @@ def load_inputs( elif whether_plot_y: fig, ax = plt.subplots() (line,) = ax.plot([], [], label="y") - self.plot_data["y"] = { + self.data_for_plot["y"] = { "line": line, "xdata": Queue(), "ydata": Queue(), } if plot_variable_names: - self.plot_data["variables"] = {} + self.data_for_plot["variables"] = {} for var_name in plot_variable_names: fig, ax = plt.subplots() (line,) = ax.plot([], [], label=var_name, marker="o") - self.plot_data["variables"][var_name] = { + self.data_for_plot["variables"][var_name] = { var_name: {"line": line, "buffer": [], "ydata": Queue()} } fig.suptitle(f"Variable: {var_name}") if plot_result_entry_names: - self.plot_data["result_entries"] = {} + self.data_for_plot["result_entries"] = {} for entry_name in plot_result_entry_names: fig, ax = plt.subplots() (line,) = ax.plot([], [], label=entry_name, marker="o") - self.plot_data["result_entries"][entry_name] = { + self.data_for_plot["result_entries"][entry_name] = { entry_name: {"line": line, "buffer": [], "ydata": Queue()} } fig.suptitle(f"Result Entry: {entry_name}") @@ -169,7 +169,7 @@ def set_start_input_file( } self.last_result_variables_values = last_result_variables_values - def run_one_round(self): + def run_one_cycle(self): self.check_for_new_data() xmin = self.inputs["xmin"] xmax = self.inputs["xmax"] @@ -179,7 +179,7 @@ def run_one_round(self): structure_path = self.inputs["structure_path"] output_result_dir = self.inputs["output_result_dir"] initial_variable_values = self.inputs["initial_variable_values"] - refine_variable_names = self.inputs["refine_variable_names"] + refinable_variable_names = self.inputs["refinable_variable_names"] if not self.input_files_running: return None for input_file in self.input_files_running: @@ -191,7 +191,7 @@ def run_one_round(self): qmin=qmin, qmax=qmax, ) - self.adapter.init_structures(structure_path) + self.adapter.init_structures([structure_path]) self.adapter.init_contribution() self.adapter.init_recipe() if not hasattr(self, "last_result_variables_values"): @@ -199,9 +199,9 @@ def run_one_round(self): self.adapter.set_initial_variable_values( self.last_result_variables_values ) - if refine_variable_names is None: - refine_variable_names = list(initial_variable_values.keys()) - self.adapter.refine_variables(refine_variable_names) + if refinable_variable_names is None: + refinable_variable_names = list(initial_variable_values.keys()) + self.adapter.refine_variables(refinable_variable_names) results = self.adapter.save_results( filename=str( Path(output_result_dir) / f"{input_file.stem}_result.json" @@ -213,25 +213,25 @@ def run_one_round(self): for name, pack in results["variables"].items() } self.input_files_completed.append(input_file) - if "ycalc" in self.plot_data: + if "ycalc" in self.data_for_plot: xdata = self.adapter.recipe.pdfcontribution.profile.x ydata = self.adapter.recipe.pdfcontribution.profile.ycalc - self.plot_data["ycalc"]["xdata"].put(xdata) - self.plot_data["ycalc"]["ydata"].put(ydata) - if "y" in self.plot_data: + self.data_for_plot["ycalc"]["xdata"].put(xdata) + self.data_for_plot["ycalc"]["ydata"].put(ydata) + if "y" in self.data_for_plot: xdata = self.adapter.recipe.pdfcontribution.profile.x ydata = self.adapter.recipe.pdfcontribution.profile.y - self.plot_data["y"]["xdata"].put(xdata) - self.plot_data["y"]["ydata"].put(ydata) - for var_name in self.plot_data.get("variables", {}): + self.data_for_plot["y"]["xdata"].put(xdata) + self.data_for_plot["y"]["ydata"].put(ydata) + for var_name in self.data_for_plot.get("variables", {}): new_value = self.adapter.recipe._parameters[var_name].value - self.plot_data["variables"][var_name][var_name]["ydata"].put( - new_value - ) - for entry_name in self.plot_data.get("result_entries", {}): + self.data_for_plot["variables"][var_name][var_name][ + "ydata" + ].put(new_value) + for entry_name in self.data_for_plot.get("result_entries", {}): fit_results = FitResults(self.adapter.recipe) entry_value = getattr(fit_results, entry_name) - self.plot_data["result_entries"][entry_name][entry_name][ + self.data_for_plot["result_entries"][entry_name][entry_name][ "ydata" ].put(entry_value) print(f"Completed processing {input_file.name}.") @@ -239,17 +239,17 @@ def run_one_round(self): def run(self, mode: Literal["batch", "stream"]): if mode == "batch": - self.run_one_round() + self.run_one_cycle() elif mode == "stream": stop_event = threading.Event() session = PromptSession() - if self.plot_data is not None: + if self.data_for_plot is not None: plt.ion() plt.pause(1) # Update plot every 1s def stream_loop(): while not stop_event.is_set(): - self.run_one_round() + self.run_one_cycle() stop_event.wait(1) # Check for new data every 1 second def input_loop(): @@ -275,7 +275,7 @@ def input_loop(): fit_thread = threading.Thread(target=stream_loop) fit_thread.start() while not stop_event.is_set(): - for key, plot_pack in self.plot_data.items(): + for key, plot_pack in self.data_for_plot.items(): if key in ["ycalc", "y"]: line = plot_pack["line"] if not plot_pack["xdata"].empty(): diff --git a/tests/test_pdfadapter.py b/tests/test_pdfadapter.py index 55257fe..2f2a6e2 100644 --- a/tests/test_pdfadapter.py +++ b/tests/test_pdfadapter.py @@ -1,6 +1,7 @@ import sys from pathlib import Path +import numpy from scipy.optimize import least_squares from pdfbl.sequential.pdfadapter import PDFAdapter @@ -10,6 +11,8 @@ def test_pdfadapter(): + # C1: Run the same fit with pdfadapter and diffpy_cmi + # Expect the refined parameters to be the same within 1e-5 # diffpy_cmi fitting structure_path = Path(__file__).parent / "data" / "Ni.cif" profile_path = Path(__file__).parent / "data" / "Ni.gr" @@ -66,10 +69,8 @@ def test_pdfadapter(): for pname, parameter in adapter.recipe._parameters.items(): pdfadapter_pv_dict[pname] = parameter.value for diffpy_pname, adapter_pname in diffpyname_to_adaptername.items(): - assert ( - abs( - diffpy_pv_dict[diffpy_pname] - - pdfadapter_pv_dict[adapter_pname] - ) - < 1e-5 + assert numpy.isclose( + diffpy_pv_dict[diffpy_pname], + pdfadapter_pv_dict[adapter_pname], + atol=1e-5, )