Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add morphfuncxy (scrappy)
  • Loading branch information
Sparks29032 committed Jul 31, 2025
commit 87ac6ac7eeb523c5ee27f9a35c5d48e39d401d19
22 changes: 15 additions & 7 deletions src/diffpy/morph/morph_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,19 @@ def single_morph_output(
rw_pos + idx, (f"squeeze a{idx}", sq_dict[f"a{idx}"])
)
mr_copy = dict(morph_results_list)
funcy_function = None
func_function = None
if "function" in mr_copy:
funcy_function = mr_copy.pop("function")
print(funcy_function)
if "funcy" in mr_copy:
func_function = mr_copy.pop("function")
if "funcxy" in mr_copy:
fxy_dict = mr_copy.pop("funcxy")
rw_pos = list(mr_copy.keys()).index("Rw")
morph_results_list = list(mr_copy.items())
for idx, key in enumerate(fxy_dict):
morph_results_list.insert(
rw_pos + idx, (f"funcxy {key}", fxy_dict[key])
)
mr_copy = dict(morph_results_list)
elif "funcy" in mr_copy:
fy_dict = mr_copy.pop("funcy")
rw_pos = list(mr_copy.keys()).index("Rw")
morph_results_list = list(mr_copy.items())
Expand All @@ -97,9 +105,9 @@ def single_morph_output(
f"# {key} = {mr_copy[key]:.6f}" for key in mr_copy.keys()
)
# Special inputs (functional)
if funcy_function is not None:
morphs_in += '# funcy function =\n"""\n'
f_code, _ = inspect.getsourcelines(funcy_function)
if func_function is not None:
morphs_in += '# function =\n"""\n'
f_code, _ = inspect.getsourcelines(func_function)
n_leading = len(f_code[0]) - len(f_code[0].lstrip())
for idx, f_line in enumerate(f_code):
f_code[idx] = f_line[n_leading:]
Expand Down
21 changes: 19 additions & 2 deletions src/diffpy/morph/morphapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,16 @@ def single_morph(

# Python-Specific Morphs
if pymorphs is not None:
# funcxy value is a tuple (function,{param_dict})
if "funcxy" in pymorphs:
mfxy_function = pymorphs["funcxy"][0]
mfxy_params = pymorphs["funcxy"][1]
chain.append(morphs.MorphFuncxy())
config["function"] = mfxy_function
config["funcxy"] = mfxy_params
refpars.append("funcxy")
# funcy value is a tuple (function,{param_dict})
if "funcy" in pymorphs:
elif "funcy" in pymorphs:
mfy_function = pymorphs["funcy"][0]
mfy_params = pymorphs["funcy"][1]
chain.append(morphs.MorphFuncy())
Expand Down Expand Up @@ -705,7 +713,16 @@ def single_morph(
for idx, _ in enumerate(squeeze_dict):
morph_inputs.update({f"squeeze a{idx}": squeeze_dict[f"a{idx}"]})
if pymorphs is not None:
if "funcy" in pymorphs:
if "funcxy" in pymorphs:
for funcxy_param in pymorphs["funcxy"][1].keys():
morph_inputs.update(
{
f"funcxy {funcxy_param}": pymorphs["funcxy"][1][
funcxy_param
]
}
)
elif "funcy" in pymorphs:
for funcy_param in pymorphs["funcy"][1].keys():
morph_inputs.update(
{f"funcy {funcy_param}": pymorphs["funcy"][1][funcy_param]}
Expand Down
2 changes: 1 addition & 1 deletion src/diffpy/morph/morphpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get_args(parser, params, kwargs):

def __get_morph_opts__(parser, scale, stretch, smear, plot, **kwargs):
# Check for Python-specific options
python_morphs = ["funcy"]
python_morphs = ["funcy", "funcx", "funcxy"]
pymorphs = {}
for pmorph in python_morphs:
if pmorph in kwargs:
Expand Down
2 changes: 2 additions & 0 deletions src/diffpy/morph/morphs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from diffpy.morph.morphs.morph import Morph # noqa: F401
from diffpy.morph.morphs.morphchain import MorphChain # noqa: F401
from diffpy.morph.morphs.morphfuncxy import MorphFuncxy
from diffpy.morph.morphs.morphfuncy import MorphFuncy
from diffpy.morph.morphs.morphishape import MorphISphere, MorphISpheroid
from diffpy.morph.morphs.morphresolution import MorphResolutionDamping
Expand All @@ -42,6 +43,7 @@
MorphShift,
MorphSqueeze,
MorphFuncy,
MorphFuncxy,
]

# End of file
77 changes: 77 additions & 0 deletions src/diffpy/morph/morphs/morphfuncxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Class MorphFuncxy -- apply a user-supplied python function to the x
and y axes."""

from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph


class MorphFuncxy(Morph):
"""Apply a custom function to the y-axis of the morph function.

General morph function that applies a user-supplied function to the
y-coordinates of morph data to make it align with a target.

Configuration Variables
-----------------------
function: callable
The user-supplied function that applies a transformation to the
y-coordinates of the data.

parameters: dict
A dictionary of parameters to pass to the function.

Returns
-------
A tuple (x_morph_out, y_morph_out, x_target_out, y_target_out)
where the target values remain the same and the morph data is
transformed according to the user-specified function and parameters
The morphed data is returned on the same grid as the unmorphed data

Example
-------
Import the funcy morph function:

>>> from diffpy.morph.morphs.morphfuncy import MorphFuncy

Define or import the user-supplied transformation function:

>>> def sine_function(x, y, amplitude, frequency):
>>> return amplitude * np.sin(frequency * x) * y

Provide initial guess for parameters:

>>> parameters = {'amplitude': 2, 'frequency': 2}

Run the funcy morph given input morph array (x_morph, y_morph)and target
array (x_target, y_target):

>>> morph = MorphFuncy()
>>> morph.function = sine_function
>>> morph.funcy = parameters
>>> x_morph_out, y_morph_out, x_target_out, y_target_out =
... morph.morph(x_morph, y_morph, x_target, y_target)

To access parameters from the morph instance:

>>> x_morph_in = morph.x_morph_in
>>> y_morph_in = morph.y_morph_in
>>> x_target_in = morph.x_target_in
>>> y_target_in = morph.y_target_in
>>> parameters_out = morph.funcy
"""

# Define input output types
summary = "Apply a Python function to the y-axis data"
xinlabel = LABEL_RA
yinlabel = LABEL_GR
xoutlabel = LABEL_RA
youtlabel = LABEL_GR
parnames = ["function", "funcxy"]

def morph(self, x_morph, y_morph, x_target, y_target):
"""Apply the user-supplied Python function to the y-coordinates
of the morph data."""
Morph.morph(self, x_morph, y_morph, x_target, y_target)
self.x_morph_out, self.y_morph_out = self.function(
self.x_morph_in, self.y_morph_in, **self.funcxy
)
return self.xyallout
2 changes: 1 addition & 1 deletion tests/testdata/funcy_target.cgr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# funcy a0 = 1.0
# funcy a1 = 2.0
# funcy a2 = 3.0
# funcy function =
# function =
"""
def quadratic(x, y, a0, a1, a2):
return a0 + a1 * x + a2 * y**2
Expand Down