From d059d43fa911d029dd1bf0896c5c2adb68699603 Mon Sep 17 00:00:00 2001 From: Yuchen Xiao Date: Mon, 26 Oct 2026 12:20:04 -0400 Subject: [PATCH 1/2] fix: update the constraint variable in each solve --- news/constraint-var.rst | 23 ++++++ .../refinement_session.py | 14 ++-- tests/test_refinement_session.py | 79 +++++++++++++++++++ 3 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 news/constraint-var.rst diff --git a/news/constraint-var.rst b/news/constraint-var.rst new file mode 100644 index 0000000..cf8e80f --- /dev/null +++ b/news/constraint-var.rst @@ -0,0 +1,23 @@ +**Added:** + +* No news added: Create new constraint variables in each solve to have clean setup. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/apps/parametric_refinement/refinement_session.py b/src/diffpy/apps/parametric_refinement/refinement_session.py index d5eec77..be469bb 100644 --- a/src/diffpy/apps/parametric_refinement/refinement_session.py +++ b/src/diffpy/apps/parametric_refinement/refinement_session.py @@ -537,9 +537,9 @@ def _solve( for var_name, value in constraints[0].items(): if var_name not in recipe._parameters: recipe.create_new_variable(var_name, value) - for constraint_name, constraint_eq_str in constraints[1].items(): - constraint_var = self.get_variable(constraint_name)["obj"] - recipe.add_constraint(constraint_var, constraint_eq_str) + for constrainted_name, constraint_eq_str in constraints[1].items(): + constrainted_var = self.get_variable(constrainted_name)["obj"] + recipe.add_constraint(constrainted_var, constraint_eq_str) variables = [self.get_variable(name)["obj"] for name in variable_names] variable_names = ["_".join(name.split(".")) for name in variable_names] @@ -548,6 +548,8 @@ def _solve( if var in recipe._parameters.values(): tag_manager.tag(var, "listed_var") continue + if var.name in recipe._parameters: + recipe.delete_variable(recipe._parameters[var.name]) recipe.add_variable(var, name=variable_names[i], tag="listed_var") for eq_or_var_name, arg_dict in bounds.items(): lb = arg_dict.get("lower_bound", -numpy.inf) @@ -582,9 +584,9 @@ def _solve( result_string = "Refinement Finished." if constraints: - for constraint_name, constraint_eq_str in constraints[1].items(): - constraint_var = self.get_variable(constraint_name)["obj"] - recipe.unconstrain(constraint_var) + for constrainted_name, constraint_eq_str in constraints[1].items(): + constrainted_var = self.get_variable(constrainted_name)["obj"] + recipe.remove_constraint(constrainted_var) return { "success": bool(result.success), "status": int(result.status), diff --git a/tests/test_refinement_session.py b/tests/test_refinement_session.py index d7fa50b..a89ee44 100644 --- a/tests/test_refinement_session.py +++ b/tests/test_refinement_session.py @@ -203,6 +203,85 @@ def test_refine_ni(): ) +def test_constraints(): + # C1: Mannly apply the constraints. + # Expect get the correct results and the contrsaints has no effect on + # the session. + session = RefinementSession() + session.add_profile_from_file( + profile_name="ni_profile", profile_path=str(_DATA_DIR / "Ni.gr") + ) + session.set_profile_calculation_range( + profile_name="ni_profile", + xmin=1.5, + xmax=20, + dx=0.01, + ) + session.update_profile_meta( + profile_name="ni_profile", + meta={"qmin": 0.1}, + ) + session.add_pdf_model( + model_name="pdf", + structure_file_path=str(_DATA_DIR / "Ni.cif"), + finite=False, + ) + session.set_variables_value( + name_value_dict={ + "pdf.phase.lattice.a": 3.52, + "pdf.scale": 0.4, + "pdf.phase.Ni0.Uiso": 0.005, + "pdf.delta2": 2, + "pdf.qdamp": 0.04, + "pdf.qbroad": 0.02, + "pdf.phase.lattice.alpha": 90, + "pdf.phase.lattice.beta": 90, + "pdf.phase.lattice.gamma": 90, + }, + ) + session.solve( + profile_names=["ni_profile"], + model_names=["pdf"], + constraints=[ + {"lattice": None, "uiso": None}, + { + "pdf.phase.lattice.a": "lattice", + "pdf.phase.lattice.b": "lattice", + "pdf.phase.lattice.c": "lattice", + "pdf.phase.Ni0.Uiso": "uiso", + "pdf.phase.Ni1.Uiso": "uiso", + "pdf.phase.Ni2.Uiso": "uiso", + "pdf.phase.Ni3.Uiso": "uiso", + }, + ], + variable_names=[ + "lattice", + "pdf.scale", + "uiso", + "pdf.delta2", + "pdf.qdamp", + "pdf.qbroad", + ], + ) + name_to_cmi_name = { + "pdf.scale": "s0", + "pdf.phase.lattice.a": "G1_a", + "pdf.phase.Ni0.Uiso": "G1_Uiso_0", + "pdf.delta2": "G1_delta2", + "pdf.qdamp": "qdamp", + "pdf.qbroad": "qbroad", + } + ni_refined_parameters = run_ni_example() + for name, cmi_name in name_to_cmi_name.items(): + assert numpy.isclose( + session.get_variable(name)["value"], + ni_refined_parameters[cmi_name], + rtol=1e-2, + ) + recipe = list(session.recipes_dict.values())[0] + assert len(recipe._constraints) == 0 + + def test_refine_multi_contribution(): session = RefinementSession() session.add_profile_from_file( From 32ce3e197383af321be3135b6c156df1b40d4765 Mon Sep 17 00:00:00 2001 From: Yuchen Xiao Date: Mon, 26 Oct 2026 15:08:49 -0400 Subject: [PATCH 2/2] chore: fix typo --- .../apps/parametric_refinement/refinement_session.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/diffpy/apps/parametric_refinement/refinement_session.py b/src/diffpy/apps/parametric_refinement/refinement_session.py index be469bb..9a2fedd 100644 --- a/src/diffpy/apps/parametric_refinement/refinement_session.py +++ b/src/diffpy/apps/parametric_refinement/refinement_session.py @@ -537,9 +537,9 @@ def _solve( for var_name, value in constraints[0].items(): if var_name not in recipe._parameters: recipe.create_new_variable(var_name, value) - for constrainted_name, constraint_eq_str in constraints[1].items(): - constrainted_var = self.get_variable(constrainted_name)["obj"] - recipe.add_constraint(constrainted_var, constraint_eq_str) + for constrained_name, constraint_eq_str in constraints[1].items(): + constrained_var = self.get_variable(constrained_name)["obj"] + recipe.add_constraint(constrained_var, constraint_eq_str) variables = [self.get_variable(name)["obj"] for name in variable_names] variable_names = ["_".join(name.split(".")) for name in variable_names] @@ -584,9 +584,9 @@ def _solve( result_string = "Refinement Finished." if constraints: - for constrainted_name, constraint_eq_str in constraints[1].items(): - constrainted_var = self.get_variable(constrainted_name)["obj"] - recipe.remove_constraint(constrainted_var) + for constrained_name, constraint_eq_str in constraints[1].items(): + constrained_var = self.get_variable(constrained_name)["obj"] + recipe.remove_constraint(constrained_var) return { "success": bool(result.success), "status": int(result.status),