Skip to content

Add tolerance option #217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
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
23 changes: 23 additions & 0 deletions news/tolerance.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* Option to set tolerance for the morph refinement (default 1e-08).

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
16 changes: 15 additions & 1 deletion src/diffpy/morph/morphapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ def custom_error(self, msg):
type="float",
help="Maximum r-value to use for PDF comparisons.",
)
parser.add_option(
"--tolerance",
"-t",
type="float",
metavar="TOL",
help="Specify refiner tolerance as TOL. Default: 10e-8.",
)
parser.add_option(
"--pearson",
action="store_true",
Expand Down Expand Up @@ -420,6 +427,11 @@ def single_morph(parser, opts, pargs, stdout_flag=True):
if y_target is None:
parser.error(f"No data table found in file: {pargs[1]}.")

# Get tolerance
tolerance = 1e-08
if opts.tolerance is not None:
tolerance = opts.tolerance

# Get configuration values
scale_in = "None"
stretch_in = "None"
Expand Down Expand Up @@ -519,7 +531,9 @@ def single_morph(parser, opts, pargs, stdout_flag=True):
refpars = list(set(refpars) - set(opts.exclude))

# Refine or execute the morph
refiner = refine.Refiner(chain, x_morph, y_morph, x_target, y_target)
refiner = refine.Refiner(
chain, x_morph, y_morph, x_target, y_target, tolerance=tolerance
)
if opts.pearson:
refiner.residual = refiner._pearson
if opts.addpearson:
Expand Down
11 changes: 9 additions & 2 deletions src/diffpy/morph/refine.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@ class Refiner(object):
to other functions.
"""

def __init__(self, chain, x_morph, y_morph, x_target, y_target):
def __init__(
self, chain, x_morph, y_morph, x_target, y_target, tolerance=1e-08
):
self.chain = chain
self.x_morph = x_morph
self.y_morph = y_morph
self.x_target = x_target
self.y_target = y_target
self.tolerance = tolerance
self.pars = []
self.residual = self._residual
self.flat_to_grouped = {}
Expand Down Expand Up @@ -143,7 +146,11 @@ def refine(self, *args, **kw):
self.flat_to_grouped[len(initial) - 1] = (p, None)

sol, cov_sol, infodict, emesg, ier = leastsq(
self.residual, initial, full_output=1
self.residual,
initial,
full_output=1,
ftol=self.tolerance,
xtol=self.tolerance,
)
fvec = infodict["fvec"]

Expand Down
42 changes: 39 additions & 3 deletions tests/test_refine.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_refine_morph(self, setup):

assert (x_morph == x_target).all()
assert numpy.allclose(y_morph, y_target)
pytest.approx(config["scale"], 3.0)
assert pytest.approx(config["scale"]) == 3.0
return

def test_refine_chain(self, setup):
Expand Down Expand Up @@ -74,8 +74,44 @@ def test_refine_chain(self, setup):
err = 15.0 * 2
res = sum(numpy.fabs(y_target - y_morph))
assert res < err
pytest.approx(chain.scale, 3, 2)
pytest.approx(chain.stretch, 0.1, 2)
assert pytest.approx(chain.scale, 0.01, 0.01) == 3.0
assert pytest.approx(chain.stretch, 0.01, 0.01) == 0.1
return

def test_refine_tolerance(self, setup):
# Check that small tolerance gives good result
stol = 1e-16
config = {
"scale": 1.0,
}
mscale = MorphScale(config)
refiner = Refiner(
mscale,
self.x_morph,
self.y_morph,
self.x_target,
self.y_target,
tolerance=stol,
)
refiner.refine()
assert pytest.approx(config["scale"], stol, stol) == 3.0

# Check that larger tolerance does not give as good of result
ltol = 100
config = {
"scale": 1.0,
}
mscale = MorphScale(config)
refiner = Refiner(
mscale,
self.x_morph,
self.y_morph,
self.x_target,
self.y_target,
tolerance=ltol,
)
refiner.refine()
assert not pytest.approx(config["scale"], stol, stol) == 3.0
return


Expand Down
Loading