Source code for diffpy.cmistructure.diffpyparset

#!/usr/bin/env python
##############################################################################
#
# (c) 2009 The Trustees of Columbia University in the City of New York.
# (c) 2026 Contributors to diffpy.cmistructure.
# All rights reserved.
#
# File coded by: Chris Farrow and members of the diffpy community.
#
# Originally developed in diffpy.srfit by the DANSE Diffraction group and
# Simon J. L. Billinge.
#
# See GitHub contributions for a more detailed list of contributors.
# https://github.com/diffpy/diffpy.cmistructure/graphs/contributors
#
# See LICENSE.rst and LICENSE_DANSE.rst for license information.
#
##############################################################################
"""Adapters for interfacing a diffpy.structure.Structure with SrFit.

A diffpy.structure.Structure object is meant to be passed to a
DiffpyStructureParSet object from this module, which can then be used as a
ParameterSet. (It has other methods for interfacing with SrReal calculator
adapters.) Any change to the lattice or existing atoms will be registered with
the Structure. Changes in the number of atoms will not be recognized.  Thus,
the diffpy.structure.Structure object should be fully configured before passing
it to DiffpyStructureParSet.

The following classes are adapted:

- `DiffpyStructureParSet`: adapter for `diffpy.structure.Structure`.
- `DiffpyLatticeParSet`: adapter for `diffpy.structure.Lattice`.
- `DiffpyAtomParSet`: adapter for `diffpy.structure.Atom`.
"""

__all__ = ["DiffpyStructureParSet"]

from diffpy.cmistructure.srrealparset import SrRealParSet
from diffpy.srfit.fitbase.parameter import ParameterAdapter, ParameterProxy
from diffpy.srfit.fitbase.parameterset import ParameterSet
from diffpy.srfit.util.argbinders import bind2nd


# Accessor for xyz of atoms
class _xyzgetter(object):

    def __init__(self, i):
        self.i = i

    def __call__(self, atom):
        return atom.xyz[self.i]


class _xyzsetter(object):

    def __init__(self, i):
        self.i = i

    def __call__(self, atom, value):
        atom.xyz[self.i] = value


class DiffpyAtomParSet(ParameterSet):
    """Adapt a diffpy.structure.Atom to the ParameterSet interface.

    This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet.
    See that class for base attributes.

    Attributes
    ----------
    atom : diffpy.structure.Atom
        The atom this is adapting.
    element : str
        The element name (property).
    x, y, z : ParameterAdapter
        The fractional coordinates of the atom.
    occupancy : ParameterAdapter
        The occupancy of the atom on its crystal location.
    occ : ParameterProxy
        The proxy for `occupancy`.
    Uij : ParameterAdapter or ParameterProxy
        The anisotropic displacement factors U11, U22, U33, U12, U21, U13,
        U31, U23 and U32 of the atom. The Uij and Uji parameters are the
        same.
    Uiso : ParameterAdapter
        The isotropic displacement factor of the atom.
    Bij : ParameterAdapter or ParameterProxy
        The anisotropic displacement factors B11, B22, B33, B12, B21, B13,
        B31, B23 and B32 of the atom, with Bij = 8*pi**2*Uij. The Bij and
        Bji parameters are the same.
    Biso : ParameterAdapter
        The isotropic displacement factor of the atom, as a B-factor.
    """

    def __init__(self, name, atom):
        """Initialize the atom ParameterSet.

        Parameters
        ----------
        name : str
            The name of this ParameterSet.
        atom : diffpy.structure.Atom
            The atom to adapt.
        """
        ParameterSet.__init__(self, name)
        self.atom = atom
        a = atom
        # x, y, z, occupancy
        self.add_parameter(
            ParameterAdapter("x", a, _xyzgetter(0), _xyzsetter(0))
        )
        self.add_parameter(
            ParameterAdapter("y", a, _xyzgetter(1), _xyzsetter(1))
        )
        self.add_parameter(
            ParameterAdapter("z", a, _xyzgetter(2), _xyzsetter(2))
        )
        occupancy = ParameterAdapter("occupancy", a, attr="occupancy")
        self.add_parameter(occupancy)
        self.add_parameter(ParameterProxy("occ", occupancy))
        # U
        self.add_parameter(ParameterAdapter("U11", a, attr="U11"))
        self.add_parameter(ParameterAdapter("U22", a, attr="U22"))
        self.add_parameter(ParameterAdapter("U33", a, attr="U33"))
        U12 = ParameterAdapter("U12", a, attr="U12")
        U21 = ParameterProxy("U21", U12)
        U13 = ParameterAdapter("U13", a, attr="U13")
        U31 = ParameterProxy("U31", U13)
        U23 = ParameterAdapter("U23", a, attr="U23")
        U32 = ParameterProxy("U32", U23)
        self.add_parameter(U12)
        self.add_parameter(U21)
        self.add_parameter(U13)
        self.add_parameter(U31)
        self.add_parameter(U23)
        self.add_parameter(U32)
        self.add_parameter(ParameterAdapter("Uiso", a, attr="Uisoequiv"))
        # B
        self.add_parameter(ParameterAdapter("B11", a, attr="B11"))
        self.add_parameter(ParameterAdapter("B22", a, attr="B22"))
        self.add_parameter(ParameterAdapter("B33", a, attr="B33"))
        B12 = ParameterAdapter("B12", a, attr="B12")
        B21 = ParameterProxy("B21", B12)
        B13 = ParameterAdapter("B13", a, attr="B13")
        B31 = ParameterProxy("B31", B13)
        B23 = ParameterAdapter("B23", a, attr="B23")
        B32 = ParameterProxy("B32", B23)
        self.add_parameter(B12)
        self.add_parameter(B21)
        self.add_parameter(B13)
        self.add_parameter(B31)
        self.add_parameter(B23)
        self.add_parameter(B32)
        self.add_parameter(ParameterAdapter("Biso", a, attr="Bisoequiv"))
        return

    def __repr__(self):
        return repr(self.atom)

    def _getelem(self):
        return self.atom.element

    def _setelem(self, el):
        self.atom.element = el

    element = property(_getelem, _setelem, "type of atom")


# End class DiffpyAtomParSet


def _latgetter(parameter):
    return bind2nd(getattr, parameter)


def _latsetter(parameter):
    return bind2nd(setattr, parameter)


class DiffpyLatticeParSet(ParameterSet):
    """Adapt a diffpy.structure.Lattice to the ParameterSet interface.

    This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet.
    See that class for base attributes.

    Attributes
    ----------
    lattice : diffpy.structure.Lattice
        The lattice this is adapting.
    name : str
        The name of this ParameterSet, always "lattice".
    angle_units : str
        The units of the lattice angles, always "deg".
    a, b, c, alpha, beta, gamma : ParameterAdapter
        The unit cell parameters.
    """

    def __init__(self, lattice):
        """Initialize the lattice ParameterSet.

        Parameters
        ----------
        lattice : diffpy.structure.Lattice
            The lattice to adapt.
        """
        ParameterSet.__init__(self, "lattice")
        self.angle_units = "deg"
        self.lattice = lattice
        lat = lattice
        self.add_parameter(
            ParameterAdapter("a", lat, _latgetter("a"), _latsetter("a"))
        )
        self.add_parameter(
            ParameterAdapter("b", lat, _latgetter("b"), _latsetter("b"))
        )
        self.add_parameter(
            ParameterAdapter("c", lat, _latgetter("c"), _latsetter("c"))
        )
        self.add_parameter(
            ParameterAdapter(
                "alpha", lat, _latgetter("alpha"), _latsetter("alpha")
            )
        )
        self.add_parameter(
            ParameterAdapter(
                "beta", lat, _latgetter("beta"), _latsetter("beta")
            )
        )
        self.add_parameter(
            ParameterAdapter(
                "gamma", lat, _latgetter("gamma"), _latsetter("gamma")
            )
        )
        return

    def __repr__(self):
        return repr(self.lattice)


# End class DiffpyLatticeParSet


[docs] class DiffpyStructureParSet(SrRealParSet): """Adapt a diffpy.structure.Structure to the ParameterSet interface. This class derives from SrRealParSet. See that class for base attributes. Attributes ---------- atoms : list of DiffpyAtomParSet The atom ParameterSets, provided for convenience. structure : diffpy.structure.Structure The structure this is adapting. lattice : DiffpyLatticeParSet The managed lattice ParameterSet. <el><idx> : DiffpyAtomParSet The managed atom ParameterSets. <el> is the atomic element and <idx> is the index of that element in the structure, starting from zero. For nickel in P1 symmetry, the managed DiffpyAtomParSets are named "Ni0", "Ni1", "Ni2" and "Ni3". """ def __init__(self, name, structure): """Initialize the structure ParameterSet. Parameters ---------- name : str The name of the structure. structure : diffpy.structure.Structure The structure to adapt. """ SrRealParSet.__init__(self, name) self.structure = structure self.add_parameter_set(DiffpyLatticeParSet(structure.lattice)) self.atoms = [] cdict = {} for a in structure: el = a.element.title() # Try to sanitize the name. el = el.replace("+", "p") el = el.replace("-", "m") i = cdict.get(el, 0) aname = "%s%i" % (el, i) cdict[el] = i + 1 atom = DiffpyAtomParSet(aname, a) self.add_parameter_set(atom) self.atoms.append(atom) return def __repr__(self): return repr(self.structure)
[docs] def get_lattice(self): """Return the ParameterSet containing the lattice Parameters. Returns ------- DiffpyLatticeParSet The lattice ParameterSet of the structure. """ return self.lattice
[docs] @classmethod def can_adapt(self, structure): """Return whether the structure can be adapted by this class. Parameters ---------- structure : object The structure object to check. Returns ------- bool The flag indicating if `structure` is a diffpy.structure.Structure. """ from diffpy.structure import Structure return isinstance(structure, Structure)
[docs] def get_scatterers(self): """Return the list of ParameterSets that represent the scatterers. Returns ------- list of DiffpyAtomParSet The atom ParameterSets of the structure. """ return self.atoms
def _get_srreal_structure(self): """Get the structure object for use with SrReal calculators. If this is periodic, then return the structure, otherwise, pass it inside of a nosymmetry wrapper. This takes the extra step of wrapping the structure in a nometa wrapper. """ from diffpy.srreal.structureadapter import nometa structure = SrRealParSet._get_srreal_structure(self) return nometa(structure)
# End class DiffpyStructureParSet