Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/examples/sequential_fit_example.py
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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")
23 changes: 23 additions & 0 deletions news/clean-up.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* No news added: Completed some clean up jobs.

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
3 changes: 2 additions & 1 deletion requirements/conda.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ diffpy.cmi
scipy
prompt_toolkit
matplotlib
bg_mpl_stylesheets
bg-mpl-stylesheets
psutil
17 changes: 8 additions & 9 deletions src/pdfbl/sequential/pdfadapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
"""
Expand Down Expand Up @@ -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())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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(),
Expand All @@ -77,42 +77,42 @@ 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(),
}
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(),
}
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}")
Expand Down Expand Up @@ -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"]
Expand All @@ -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:
Expand All @@ -191,17 +191,17 @@ 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"):
self.last_result_variables_values = initial_variable_values
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"
Expand All @@ -213,43 +213,43 @@ 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}.")
self.input_files_running = []

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():
Expand All @@ -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():
Expand Down
13 changes: 7 additions & 6 deletions tests/test_pdfadapter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
from pathlib import Path

import numpy
from scipy.optimize import least_squares

from pdfbl.sequential.pdfadapter import PDFAdapter
Expand All @@ -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"
Expand Down Expand Up @@ -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,
)
Loading