#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit by DANSE Diffraction group
# Simon J. L. Billinge
# (c) 2009 The Trustees of Columbia University
# in the City of New York. All rights reserved.
#
# File coded by: Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""Wrappers for adapting pyobjcryst.crystal.Crystal to a srfit
ParameterSet.
This will adapt a Crystal or Molecule object from pyobjcryst into the
ParameterSet interface. The following classes are adapted:
- `ObjCrystCrystalParSet`: adapter for `pyobjcryst.crystal.Crystal`.
- `ObjCrystAtomParSet`: adapter for `pyobjcryst.atom.Atom`.
- `ObjCrystMoleculeParSet`: adapter for `pyobjcryst.molecule.Molecule`.
- `ObjCrystMolAtomParSet`: adapter for `pyobjcryst.molecule.MolAtom`.
Related to the adaptation of Molecule and MolAtom, there are adaptors
for specifying molecule restraints:
- `ObjCrystBondLengthRestraint`
- `ObjCrystBondAngleRestraint`
- `ObjCrystDihedralAngleRestraint`
There are also Parameters for encapsulating and modifying atoms via
their relative positions. These Parameters can also act like
constraints, and can modify the positions of multiple MolAtoms:
- `ObjCrystBondLengthParameter`
- `ObjCrystBondAngleParameter`
- `ObjCrystDihedralAngleParameter`
"""
__all__ = ["ObjCrystMoleculeParSet", "ObjCrystCrystalParSet"]
import numpy
from pyobjcryst.molecule import (
GetBondAngle,
GetBondLength,
GetDihedralAngle,
StretchModeBondAngle,
StretchModeBondLength,
StretchModeTorsion,
)
from diffpy.srfit.fitbase.parameter import (
Parameter,
ParameterAdapter,
ParameterProxy,
)
from diffpy.srfit.fitbase.parameterset import ParameterSet
from diffpy.srfit.structure.srrealparset import SrRealParSet
from diffpy.utils._deprecator import build_deprecation_message, deprecated
removal_version = "4.0.0"
bl_base = "diffpy.srfit.structure.objcrystparset.ObjCrystBondLengthParameter"
bl_setConst_dep_msg = build_deprecation_message(
bl_base,
"setConst",
"set_constant",
removal_version,
)
ba_base = "diffpy.srfit.structure.objcrystparset.ObjCrystBondAngleParameter"
ba_setConst_dep_msg = build_deprecation_message(
ba_base,
"setConst",
"set_constant",
removal_version,
)
da_base = (
"diffpy.srfit.structure.objcrystparset.ObjCrystDihedralAngleParameter"
)
da_setConst_dep_msg = build_deprecation_message(
da_base,
"setConst",
"set_constant",
removal_version,
)
class ObjCrystScattererParSet(ParameterSet):
"""A base adaptor for an Objcryst Scatterer.
This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet and
adapts pyobjcryst.scatterer.Scatterer derivatives (Molecule, Atom) and
objects with a similar interface (MolAtom). See the ParameterSet class for
base attributes.
Attributes
----------
scat
The adapted pyobjcryst object.
parent
The ParameterSet this belongs to
Managed Parameters
------------------
occ
Occupancy of the scatterer on its crystal site
(ParameterWraper)
"""
def __init__(self, name, scat, parent):
"""Initialize.
Parameters
----------
name
The name of the scatterer
scat
The pyobjcryst.Scatterer instance
parent
The ParameterSet this belongs to
"""
ParameterSet.__init__(self, name)
self.scat = scat
self.parent = parent
# x, y, z, occ
self.addParameter(ParameterAdapter("x", self.scat, attr="X"))
self.addParameter(ParameterAdapter("y", self.scat, attr="Y"))
self.addParameter(ParameterAdapter("z", self.scat, attr="Z"))
self.addParameter(ParameterAdapter("occ", self.scat, attr="Occupancy"))
return
def isDummy(self):
"""Indicate whether this scatterer is a dummy atom."""
return False
def hasScatterers(self):
"""Indicate if this scatterer has its own scatterers."""
return hasattr(self, "getScatterers")
# End class ObjCrystScattererParSet
class ObjCrystAtomParSet(ObjCrystScattererParSet):
"""A adaptor for a pyobjcryst.Atom.
This class derives from ObjCrystScattererParSet.
Attributes
----------
scat
The adapted pyobjcryst.atom.Atom.
element
Non-refinable name of the element (property).
parent
The ObjCrystCrystalParSet this belongs to.
Managed Parameters
------------------
occ
Occupancy of the atom on its crystal location
(ParameterAdapter)
Biso
Isotropic scattering factor (ParameterAdapter).
B11, B22, B33, B12, B21, B23, B32, B13, B31
-- Anisotropic displacement factor for scatterer
(ParameterAdapter or ParameterProxy). Note that the Bij and Bji
parameters are the same.
"""
def __init__(self, name, atom, parent):
"""Initialize.
Parameters
----------
name
The name of the scatterer
scat
The Scatterer instance
parent
The ObjCrystCrystalParSet this belongs to
"""
ObjCrystScattererParSet.__init__(self, name, atom, parent)
sp = atom.GetScatteringPower()
# The B-parameters
self.addParameter(ParameterAdapter("Biso", sp, attr="Biso"))
self.addParameter(ParameterAdapter("B11", sp, attr="B11"))
self.addParameter(ParameterAdapter("B22", sp, attr="B22"))
self.addParameter(ParameterAdapter("B33", sp, attr="B33"))
B12 = ParameterAdapter("B12", sp, attr="B12")
B21 = ParameterProxy("B21", B12)
B13 = ParameterAdapter("B13", sp, attr="B13")
B31 = ParameterProxy("B31", B13)
B23 = ParameterAdapter("B23", sp, attr="B23")
B32 = ParameterProxy("B32", B23)
self.addParameter(B12)
self.addParameter(B21)
self.addParameter(B13)
self.addParameter(B31)
self.addParameter(B23)
self.addParameter(B32)
# Give a value to Biso if it doesn't have one, and this is isotropic
if sp.IsIsotropic() and self.Biso.value == 0:
self.Biso.value = 0.5
return
def _getelem(self):
"""Getter for the element type."""
return self.scat.GetScatteringPower().GetSymbol()
element = property(_getelem)
# End class ObjCrystAtomParSet
[docs]
class ObjCrystMoleculeParSet(ObjCrystScattererParSet):
"""A adaptor for a pyobjcryst.Molecule.
This class derives from ObjCrystScattererParSet.
Attributes
----------
scat
The adapted pyobjcryst.molecule.Molecule.
stru
The adapted pyobjcryst.molecule.Molecule.
parent
The ObjCrystCrystalParSet this belongs to.
ObjCrystMoleculeParSets can be used on their own, in which
case this is None.
Managed Parameters
------------------
occ
Occupancy of the molecule on its crystal location
(ParameterAdapter)
q0, q1, q2, q3 -- Orientational quaternion (ParameterAdapter)
Other attributes are inherited from
diffpy.srfit.fitbase.parameterset.ParameterSet
"""
def __init__(self, name, molecule, parent=None):
"""Initialize.
Parameters
----------
name
The name of the scatterer
molecule
The pyobjcryst.Molecule instance
parent
The ObjCrystCrystalParSet this belongs to (default None).
"""
ObjCrystScattererParSet.__init__(self, name, molecule, parent)
self.stru = molecule
# Add orientation quaternion
self.addParameter(ParameterAdapter("q0", self.scat, attr="Q0"))
self.addParameter(ParameterAdapter("q1", self.scat, attr="Q1"))
self.addParameter(ParameterAdapter("q2", self.scat, attr="Q2"))
self.addParameter(ParameterAdapter("q3", self.scat, attr="Q3"))
# Wrap the MolAtoms within the molecule
self.atoms = []
anames = []
for a in molecule:
name = a.GetName()
if not name:
raise AttributeError("Each MolAtom must have a name")
if name in anames:
raise AttributeError("MolAtom name '%s' is duplicated" % name)
atom = ObjCrystMolAtomParSet(name, a, self)
atom.molecule = self
self.add_parameter_set(atom)
self.atoms.append(atom)
anames.append(name)
return
[docs]
@classmethod
def canAdapt(self, stru):
"""Return whether the structure can be adapted by this class."""
from pyobjcryst.molecule import Molecule
return isinstance(stru, Molecule)
# Part of SrRealParSet interface
[docs]
def useSymmetry(self, use=True):
"""Set this structure to use symmetry.
This structure object does not support symmetry.
"""
return
# Part of SrRealParSet interface
[docs]
def usingSymmetry(self):
"""Check if symmetry is being used.
This structure object does not support symmetry.
"""
return False
# Part of SrRealParSet interface
def _get_srreal_structure(self):
"""Get the structure object for use with SrReal calculators.
Molecule objects are never periodic. Return the object and let
the SrReal adapters do the proper thing.
"""
return self.stru
[docs]
def getLattice(self):
"""Get the ParameterSet containing the lattice Parameters."""
lattice = ParameterSet("lattice")
lattice.newPar("a", 1.0)
lattice.newPar("b", 1.0)
lattice.newPar("c", 1.0)
lattice.newPar("alpha", 90)
lattice.newPar("beta", 90)
lattice.newPar("gamma", 90)
lattice.angunits = "deg"
return lattice
[docs]
def getScatterers(self):
"""Get a list of ParameterSets that represents the scatterers.
The site positions must be accessible from the list entries via
the names "x", "y", and "z". The ADPs must be accessible as
well, but the name and nature of the ADPs (U-factors, B-factors,
isotropic, anisotropic) depends on the adapted structure.
"""
return self.atoms
[docs]
def wrapRestraints(self):
"""Wrap the restraints implicit to the molecule.
This will wrap MolBonds, MolBondAngles and MolDihedralAngles of
the Molecule as ObjCrystMoleculeRestraint objects.
"""
# Wrap restraints. Restraints wrapped in this way cannot be modified
# from within this class.
for b in self.scat.GetBondList():
res = ObjCrystMoleculeRestraint(b)
self._restraints.add(res)
for ba in self.scat.GetBondAngleList():
res = ObjCrystMoleculeRestraint(ba)
self._restraints.add(res)
for da in self.scat.GetDihedralAngleList():
res = ObjCrystMoleculeRestraint(da)
self._restraints.add(res)
return
[docs]
def wrapStretchModeParameters(self):
"""Wrap the stretch modes implicit to the Molecule as
Parameters.
This will wrap StretchModeBondLengths and StretchModeBondAngles of the
Molecule as Parameters. Note that this requires that the MolBondAtoms
in the Molecule came in with unique names. Torsion angles are not
wrapped, as there is not enough information to determine each MolAtom
in the angle.
The Parameters will be given the concatenated name of its constituents.
bond lengths: "bl_aname1_aname2"
bond angles: "ba_aname1_aname2_aname3"
"""
for mode in self.scat.GetStretchModeBondLengthList():
name1 = mode.mpAtom0.GetName()
name2 = mode.mpAtom1.GetName()
name = "bl_" + "_".join((name1, name2))
atom1 = getattr(self, name1)
atom2 = getattr(self, name2)
par = ObjCrystBondLengthParameter(name, atom1, atom2, mode=mode)
atoms = []
for a in mode.GetAtoms():
name = a.GetName()
atoms.append(getattr(self, name))
par.AddAtoms(atoms)
self.addParameter(par)
for mode in self.scat.GetStretchModeBondAngleList():
name1 = mode.mpAtom0.GetName()
name2 = mode.mpAtom1.GetName()
name3 = mode.mpAtom2.GetName()
name = "ba_" + "_".join((name1, name2, name3))
atom1 = getattr(self, name1)
atom2 = getattr(self, name2)
atom3 = getattr(self, name3)
par = ObjCrystBondAngleParameter(
name, atom1, atom2, atom3, mode=mode
)
atoms = []
for a in mode.GetAtoms():
name = a.GetName()
atoms.append(getattr(self, name))
par.AddAtoms(atoms)
self.addParameter(par)
return
[docs]
def restrainBondLength(
self, atom1, atom2, length, sigma, delta, scaled=False
):
"""Add a bond length restraint.
This creates an instance of ObjCrystBondLengthRestraint and adds it to
the ObjCrystMoleculeParSet.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the bond
atom2
Second atom (ObjCrystMolAtomParSet) in the bond
length
The length of the bond (Angstroms)
sigma
The uncertainty of the bond length (Angstroms)
delta
The width of the bond (Angstroms)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False)
Returns
-------
res
The ObjCrystBondLengthRestraint object for use with the
'unrestrain' method.
"""
res = ObjCrystBondLengthRestraint(
atom1, atom2, length, sigma, delta, scaled
)
self._restraints.add(res)
return res
[docs]
def restrainBondLengthParameter(
self, par, length, sigma, delta, scaled=False
):
"""Add a bond length restraint.
This creates an instance of ObjCrystBondLengthRestraint and adds it to
the ObjCrystMoleculeParSet.
Parameters
----------
par
A ObjCrystBondLengthParameter (see addBondLengthParameter)
length
The length of the bond (Angstroms)
sigma
The uncertainty of the bond length (Angstroms)
delta
The width of the bond (Angstroms)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False)
Returns
-------
ObjCrystBondLengthRestraint object
Returns the ObjCrystBondLengthRestraint object for use with the
'unrestrain' method.
"""
return self.restrainBondLength(
par.atom1, par.atom2, length, sigma, delta, scaled
)
[docs]
def restrainBondAngle(
self, atom1, atom2, atom3, angle, sigma, delta, scaled=False
):
"""Add a bond angle restraint.
This creates an instance of ObjCrystBondAngleRestraint and adds it to
the ObjCrystMoleculeParSet.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the bond angle
atom2
Second (central) atom (ObjCrystMolAtomParSet) in the bond
angle
atom3
Third atom (ObjCrystMolAtomParSet) in the bond angle
angle
The bond angle (radians)
sigma
The uncertainty of the bond angle (radians)
delta
The width of the bond angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
Returns
-------
ObjCrystBondAngleRestraint object
Returns the ObjCrystBondAngleRestraint object for use with the
'unrestrain' method.
"""
res = ObjCrystBondAngleRestraint(
atom1, atom2, atom3, angle, sigma, delta, scaled
)
self._restraints.add(res)
return res
[docs]
def restrainBondAngleParameter(
self, par, angle, sigma, delta, scaled=False
):
"""Add a bond angle restraint.
This creates an instance of ObjCrystBondAngleRestraint and adds it to
the ObjCrystMoleculeParSet.
Parameters
----------
par
A ObjCrystBondAngleParameter (see addBondAngleParameter)
angle
The bond angle (radians)
sigma
The uncertainty of the bond angle (radians)
delta
The width of the bond angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
Returns
-------
ObjCrystBondAngleRestraint object
Returns the ObjCrystBondAngleRestraint object for use with the
'unrestrain' method.
"""
return self.restrainBondAngle(
par.atom1, par.atom2, par.atom3, angle, sigma, delta, scaled
)
[docs]
def restrainDihedralAngle(
self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False
):
"""Add a dihedral angle restraint.
This creates an instance of ObjCrystDihedralAngleRestraint and adds it
to the ObjCrystMoleculeParSet.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the angle
atom2
Second (central) atom (ObjCrystMolAtomParSet) in the angle
atom3
Third (central) atom (ObjCrystMolAtomParSet) in the angle
atom4
Fourth atom in the angle (ObjCrystMolAtomParSet)
angle
The dihedral angle (radians)
sigma
The uncertainty of the dihedral angle (radians)
delta
The width of the dihedral angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
Returns
-------
ObjCrystDihedralAngleRestraint object
Returns the ObjCrystDihedralAngleRestraint object for use with the
'unrestrain' method.
"""
res = ObjCrystDihedralAngleRestraint(
atom1, atom2, atom3, atom4, angle, sigma, delta, scaled
)
self._restraints.add(res)
return res
[docs]
def restrainDihedralAngleParameter(
self, par, angle, sigma, delta, scaled=False
):
"""Add a dihedral angle restraint.
This creates an instance of ObjCrystDihedralAngleRestraint and adds it
to the ObjCrystMoleculeParSet.
Parameters
----------
par
A ObjCrystDihedralAngleParameter (see
addDihedralAngleParameter)
angle
The dihedral angle (radians)
sigma
The uncertainty of the dihedral angle (radians)
delta
The width of the dihedral angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
Returns
-------
ObjCrystDihedralAngleRestraint object
Returns the ObjCrystDihedralAngleRestraint object for use with the
'unrestrain' method.
"""
return self.restrainDihedralAngle(
par.atom1,
par.atom2,
par.atom3,
par.atom4,
angle,
sigma,
delta,
scaled,
)
[docs]
def addBondLengthParameter(
self, name, atom1, atom2, value=None, const=False
):
"""Add a bond length to the Molecule.
This creates a ObjCrystBondLengthParameter to the
ObjCrystMoleculeParSet that can be adjusted during the fit.
Parameters
----------
name
The name of the ObjCrystBondLengthParameter
atom1
The first atom (ObjCrystMolAtomParSet) in the bond
atom2
The second (mutated) atom (ObjCrystMolAtomParSet) in the
bond
value
An initial value for the bond length. If this is None
(default), then the current distance between the atoms will
be used.
const
A flag indicating whether the Parameter is constant
(default False)
Returns
-------
ObjCrystBondLengthParameter object
Returns the new ObjCrystBondLengthParameter.
"""
par = ObjCrystBondLengthParameter(name, atom1, atom2, value, const)
self.addParameter(par)
return par
[docs]
def addBondAngleParameter(
self, name, atom1, atom2, atom3, value=None, const=False
):
"""Add a bond angle to the Molecule.
This creates a ObjCrystBondAngleParameter to the ObjCrystMoleculeParSet
that can be adjusted during the fit.
Parameters
----------
name
The name of the ObjCrystBondAngleParameter
atom1
The first atom (ObjCrystMolAtomParSet) in the bond angle
atom2
The second (central) atom (ObjCrystMolAtomParSet) in the
bond angle
atom3
The third (mutated) atom (ObjCrystMolAtomParSet) in the
bond angle
value
An initial value for the bond angle. If this is None
(default), then the current bond angle between the atoms
will be used.
const
A flag indicating whether the Parameter is constant
(default False).
Returns
-------
ObjCrystBondAngleParameter object
Returns the new ObjCrystBondAngleParameter.
"""
par = ObjCrystBondAngleParameter(
name, atom1, atom2, atom3, value, const
)
self.addParameter(par)
return par
[docs]
def addDihedralAngleParameter(
self, name, atom1, atom2, atom3, atom4, value=None, const=False
):
"""Add a dihedral angle to the Molecule.
This creates a ObjCrystDihedralAngleParameter to the
ObjCrystMoleculeParSet that can be adjusted during the fit.
Parameters
----------
name
The name of the ObjCrystDihedralAngleParameter.
atom1
The first atom (ObjCrystMolAtomParSet) in the dihderal
angle.
atom2
The second (central) atom (ObjCrystMolAtomParSet) in the
dihderal angle
atom3
The third (central) atom (ObjCrystMolAtomParSet) in the
dihderal angle
atom4
The fourth (mutated) atom (ObjCrystMolAtomParSet) in the
dihderal angle
value
An initial value for the dihedral angle. If this is None
(default), then the current dihedral angle between atoms
will be used.
const
A flag indicating whether the Parameter is constant
(default False).
Returns
-------
ObjCrystDihedralAngleParameter object
Returns the new ObjCrystDihedralAngleParameter.
"""
par = ObjCrystDihedralAngleParameter(
name, atom1, atom2, atom3, atom4, value, const
)
self.addParameter(par)
return par
# End class ObjCrystMoleculeParSet
class ObjCrystMolAtomParSet(ObjCrystScattererParSet):
"""A adaptor for an pyobjcryst.molecule.MolAtom.
This class derives from srfit.fitbase.parameterset.ParameterSet. Note that
MolAtom does not derive from Scatterer, but the relevant interface is the
same within pyobjcryst. See the ParameterSet class for base attributes.
Attributes
----------
scat
The adapted pyobjcryst.molecule.MolAtom.
parent
The ObjCrystCrystalParSet this belongs to
element
Non-refinable name of the element (property).
Managed Parameters
------------------
occ
Occupancy of the atom on its crystal location
(ParameterAdapter)
Biso
Isotropic scattering factor (ParameterAdapter). This does
not exist for dummy atoms. See the 'isDummy' method.
B11, B22, B33, B12, B21, B23, B32, B13, B31
-- Anisotropic displacement factor for scatterer
(ParameterAdapter or ParameterProxy). Note that the Bij and Bji
parameters are the same.
"""
def __init__(self, name, scat, parent):
"""Initialize.
Parameters
----------
name
The name of the scatterer
scat
The Scatterer instance
parent
The ObjCrystCrystalParSet this belongs to
"""
ObjCrystScattererParSet.__init__(self, name, scat, parent)
sp = scat.GetScatteringPower()
# Only wrap this if there is a scattering power
if sp is not None:
self.addParameter(ParameterAdapter("Biso", sp, attr="Biso"))
self.addParameter(ParameterAdapter("B11", sp, attr="B11"))
self.addParameter(ParameterAdapter("B22", sp, attr="B22"))
self.addParameter(ParameterAdapter("B33", sp, attr="B33"))
B12 = ParameterAdapter("B12", sp, attr="B12")
B21 = ParameterProxy("B21", B12)
B13 = ParameterAdapter("B13", sp, attr="B13")
B31 = ParameterProxy("B31", B13)
B23 = ParameterAdapter("B23", sp, attr="B23")
B32 = ParameterProxy("B32", B23)
self.addParameter(B12)
self.addParameter(B21)
self.addParameter(B13)
self.addParameter(B31)
self.addParameter(B23)
self.addParameter(B32)
return
def _getelem(self):
"""Getter for the element type."""
sp = self.scat.GetScatteringPower()
if sp:
return sp.GetSymbol()
else:
return "dummy"
element = property(_getelem)
def isDummy(self):
"""Indicate whether this atom is a dummy atom."""
return self.scat.IsDummy()
# End class ObjCrystMolAtomParSet
class ObjCrystMoleculeRestraint(object):
"""Base class for adapting pyobjcryst Molecule restraints to srfit.
The 'penalty' method calls 'GetLogLikelihood' of the pyobjcryst restraint.
This implements the 'penalty' method from
diffpy.srfit.fitbase.restraint.Restraint. The 'restrain' method is not
needed or implemented.
Attributes
----------
res
The pyobjcryst Molecule restraint.
scaled
A flag indicating if the restraint is scaled (multiplied) by
the unrestrained point-average chi^2 (chi^2/numpoints) (default
False).
"""
def __init__(self, res, scaled=False):
"""Create a Restraint-like from a pyobjcryst Molecule restraint.
Parameters
----------
res
The pyobjcryst Molecule restraint.
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
"""
self.res = res
self.scaled = scaled
return
def penalty(self, w=1.0):
"""Calculate the penalty of the restraint.
Parameters
----------
w
The point-average chi^2 which is optionally used to scale the
penalty (default 1.0).
"""
penalty = self.res.GetLogLikelihood()
if self.scaled:
penalty *= w
return penalty
# End class ObjCrystMoleculeRestraint
class ObjCrystBondLengthRestraint(ObjCrystMoleculeRestraint):
"""Restrain the distance between two atoms.
Attributes
----------
atom1
The first atom in the bond (ObjCrystMolAtomParSet)
atom2
The second atom in the bond (ObjCrystMolAtomParSet)
length
The length of the bond (Angstroms)
sigma
The uncertainty of the bond length (Angstroms)
delta
The width of the bond (Angstroms)
res
The pyobjcryst BondLength restraint
scaled
A flag indicating if the restraint is scaled (multiplied) by
the unrestrained point-average chi^2 (chi^2/numpoints) (default
False)
"""
def __init__(self, atom1, atom2, length, sigma, delta, scaled=False):
"""Create a bond length restraint.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the bond
atom2
Second atom (ObjCrystMolAtomParSet) in the bond
length
The length of the bond (Angstroms)
sigma
The uncertainty of the bond length (Angstroms)
delta
The width of the bond (Angstroms)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False)
"""
self.atom1 = atom1
self.atom2 = atom2
m = self.atom1.scat.GetMolecule()
res = m.AddBond(atom1.scat, atom2.scat, length, sigma, delta)
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
return
# Give access to the parameters of the restraint
length = property(
lambda self: self.res.GetLength0(),
lambda self, val: self.res.SetLength0(val),
)
sigma = property(
lambda self: self.res.GetLengthSigma(),
lambda self, val: self.res.SetLengthSigma(val),
)
delta = property(
lambda self: self.res.GetLengthDelta(),
lambda self, val: self.res.SetLengthDelta(val),
)
# End class ObjCrystBondLengthRestraint
class ObjCrystBondAngleRestraint(ObjCrystMoleculeRestraint):
"""Restrain the angle defined by three atoms.
Attributes
----------
atom1
The first atom in the angle (ObjCrystMolAtomParSet)
atom2
The second atom in the angle (ObjCrystMolAtomParSet)
atom3
The third atom in the angle (ObjCrystMolAtomParSet)
angle
The bond angle (radians)
sigma
The uncertainty of the bond angle (radians)
delta
The width of the bond angle (radians)
res
The pyobjcryst BondAngle restraint
scaled
A flag indicating if the restraint is scaled (multiplied) by
the unrestrained point-average chi^2 (chi^2/numpoints) (default
False)
"""
def __init__(self, atom1, atom2, atom3, angle, sigma, delta, scaled=False):
"""Create a bond angle restraint.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the bond angle
atom2
Second (central) atom (ObjCrystMolAtomParSet) in the bond
angle
atom3
Third atom (ObjCrystMolAtomParSet) in the bond angle
angle
The bond angle (radians)
sigma
The uncertainty of the bond angle (radians)
delta
The width of the bond angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
"""
self.atom1 = atom1
self.atom2 = atom2
self.atom3 = atom3
m = self.atom1.scat.GetMolecule()
res = m.AddBondAngle(
atom1.scat, atom2.scat, atom3.scat, angle, sigma, delta
)
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
return
# Give access to the parameters of the restraint
angle = property(
lambda self: self.res.GetAngle0(),
lambda self, val: self.res.SetAngle0(val),
)
sigma = property(
lambda self: self.res.GetAngleSigma(),
lambda self, val: self.res.SetAngleSigma(val),
)
delta = property(
lambda self: self.res.GetAngleDelta(),
lambda self, val: self.res.SetAngleDelta(val),
)
# End class ObjCrystBondAngleRestraint
class ObjCrystDihedralAngleRestraint(ObjCrystMoleculeRestraint):
"""Restrain the dihedral (torsion) angle defined by four atoms.
Attributes
----------
atom1
The first atom in the angle (ObjCrystMolAtomParSet)
atom2
The second (central) atom in the angle (ObjCrystMolAtomParSet)
atom3
The third (central) atom in the angle (ObjCrystMolAtomParSet)
atom4
The fourth atom in the angle (ObjCrystMolAtomParSet)
angle
The dihedral angle (radians)
sigma
The uncertainty of the dihedral angle (radians)
delta
The width of the dihedral angle (radians)
res
The pyobjcryst DihedralAngle restraint
scaled
A flag indicating if the restraint is scaled (multiplied) by
the unrestrained point-average chi^2 (chi^2/numpoints) (default
False)
"""
def __init__(
self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False
):
"""Create a dihedral angle restraint.
Parameters
----------
atom1
First atom (ObjCrystMolAtomParSet) in the angle
atom2
Second (central) atom (ObjCrystMolAtomParSet) in the angle
atom3
Third (central) atom (ObjCrystMolAtomParSet) in the angle
atom4
Fourth atom in the angle (ObjCrystMolAtomParSet)
angle
The dihedral angle (radians)
sigma
The uncertainty of the dihedral angle (radians)
delta
The width of the dihedral angle (radians)
scaled
A flag indicating if the restraint is scaled (multiplied)
by the unrestrained point-average chi^2 (chi^2/numpoints)
(default False).
"""
self.atom1 = atom1
self.atom2 = atom2
self.atom3 = atom3
self.atom4 = atom4
m = self.atom1.scat.GetMolecule()
res = m.AddDihedralAngle(
atom1.scat, atom2.scat, atom3.scat, atom4.scat, angle, sigma, delta
)
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
return
# Give access to the parameters of the restraint
angle = property(
lambda self: self.res.GetAngle0(),
lambda self, val: self.res.SetAngle0(val),
)
sigma = property(
lambda self: self.res.GetAngleSigma(),
lambda self, val: self.res.SetAngleSigma(val),
)
delta = property(
lambda self: self.res.GetAngleDelta(),
lambda self, val: self.res.SetAngleDelta(val),
)
# End class ObjCrystDihedralAngleRestraint
class StretchModeParameter(Parameter):
"""Partial Parameter class encapsulating pyobjcryst stretch modes.
This class relies upon attributes that do not belong to it. Do not
instantiate this class.
Required attributes
-------------------
matoms
The set of all mutated AtomParSets
molecule
The ObjCrystMoleculeParSet the atoms belong to
mode
The pyobjcryst.molecule.StretchMode used to change atomic
positions.
keepcenter
Flag indicating whether to keep the center of mass of the
molecule stationary within the crystal when changing the
value of the parameter (bool, default True).
"""
def __init__(self, name, value=None, const=False):
"""Initialization.
Parameters
----------
name
The name of this Parameter (must be a valid attribute
identifier)
value
The initial value of this Parameter (default 0).
const
A flag inticating whether the Parameter is a constant (like
pi).
Raises ValueError if the name is not a valid attribute identifier
"""
Parameter.__init__(self, name, value, const)
self.keepcenter = True
def set_value(self, val):
"""Change the value of the Parameter."""
curval = self.getValue()
val = float(val)
if val == curval:
return self
# The StretchMode expects the change in mutated value.
delta = val - curval
self.mode.Stretch(delta, self.keepcenter)
# Let Parameter take care of the general details
Parameter.set_value(self, val)
return self
def addAtoms(self, atomlist):
"""Associate ObjCrystMolAtomParSets with the Parameter.
This will associate additional ObjCrystMolAtomParSets with the
Parameter. These will be mutated in the exact same way as the
primary mutated ObjCrystMolAtomParSet. This is useful when a
group of atoms should move rigidly in response to a change in a
bond property.
"""
if not hasattr(atomlist, "__iter__"):
atomlist = [atomlist]
# Record the added atoms in the Parameter
self.matoms.update(atomlist)
# Make sure we're observing these atoms
for a in atomlist:
a.x.addObserver(self._flush)
a.y.addObserver(self._flush)
a.z.addObserver(self._flush)
# Record the added atoms in the StretchMode
scatlist = [a.scat for a in atomlist]
self.mode.AddAtoms(scatlist)
return self
def notify(self, other=()):
"""Notify all mutated Parameters and observers.
Some of the mutated parameters will be observing us. At the same
time we need to observe them. Observable won't let us do both,
so we notify the Parameters that we mutate directly.
"""
noneother = ()
# Notify the atoms that have moved
for a in self.matoms:
a.x._flush(noneother)
a.y._flush(noneother)
a.z._flush(noneother)
# Notify the molecule position
self.molecule.x._flush(noneother)
self.molecule.y._flush(noneother)
self.molecule.z._flush(noneother)
# Notify observers
Parameter.notify(self, other)
return
# End class StretchModeParameter
class ObjCrystBondLengthParameter(StretchModeParameter):
"""Class for abstracting a bond length in a Molecule to a Parameter.
This wraps up a pyobjcryst.molecule.StretchModeBondLength object so that
the distance between two MolAtoms in a Molecule can be used as an
adjustable Parameter. When a bond length is adjusted, the second MolAtom is
moved, and the absolute position of the Molecule is altered to preserve the
location of the center of mass within the Crystal. Thus, the x, y and z
Parameters of the MolAtom and its parent Molecule are altered. This can be
changed by setting the 'keepcenter' attribute of the parameter to False.
This Parameter makes it possible to mutate a MolAtom multiple times in a
single refinement step. If these mutations are not orthogonal, then this
could lead to nonconvergence of a fit, depending on the optimizer. Consider
mutating atom2 of a bond directly, and via a ObjCrystBondLengthParameter.
The two mutations of atom2 may be determined independently by the
optimizer, in which case the composed mutation will have an unexpected
effect on the residual. It is best practice to either modify MolAtom
positions directly, or thorough BondLengthParameters, BondAngleParameters
and DihedralAngleParameters (which are mutually orthogonal).
Note that by making a ObjCrystBondLengthParameter constant it also makes
the underlying ObjCrystMolAtomParSets constant. When setting it as
nonconstant, each ObjCrystMolAtomParSet is set nonconstant. Changing the
bond length changes the position of the second MolAtom and Molecule, even
if either is set as constant.
Attributes
----------
atom1
The first ObjCrystMolAtomParSet in the bond
atom2
The second (mutated) ObjCrystMolAtomParSet in the bond
matoms
The set of all mutated ObjCrystMolAtomParSets
molecule
The ObjCrystMoleculeParSet the ObjCrystMolAtomParSets
belong to
mode
The pyobjcryst.molecule.StretchModeBondLength for the bond
Inherited Attributes
--------------------
name
A name for this Parameter.
const
A flag indicating whether this is considered a constant.
_value
The value of the Parameter. Modified with 'set_value'.
value
Property for 'getValue' and 'set_value'.
constraint
A callable that calculates the value of this Parameter. If
this is None (None), the the Parameter is responsible for its
own value. The callable takes no arguments.
bounds
A 2-list defining the bounds on the Parameter. This can be
used by some optimizers when the Parameter is varied.
"""
def __init__(self, name, atom1, atom2, value=None, const=False, mode=None):
"""Create a ObjCrystBondLengthParameter.
Parameters
----------
name
The name of the ObjCrystBondLengthParameter
atom1
The first atom (ObjCrystMolAtomParSet) in the bond
atom2
The second (mutated) atom (ObjCrystMolAtomParSet) in the
bond
value
An initial value for the bond length. If this is None
(default), then the current distance between the atoms will
be used.
const
A flag indicating whether the Parameter is constant
(default False)
mode
An extant pyobjcryst.molecule.StretchModeBondLength to use.
If this is None (default), then a new StretchModeBondLength
will be built.
"""
# Create the mode
self.mode = mode
if mode is None:
self.mode = StretchModeBondLength(atom1.scat, atom2.scat, None)
# We only add the last atom. This is the one that will move
self.mode.AddAtom(atom2.scat)
self.matoms = set([atom2])
# Observe the atom positions
for a in [atom1, atom2]:
a.x.addObserver(self._flush)
a.y.addObserver(self._flush)
a.z.addObserver(self._flush)
self.atom1 = atom1
self.atom2 = atom2
self.molecule = atom1.parent
# We do this last so the atoms are defined before we set any values.
if value is None:
value = GetBondLength(atom1.scat, atom2.scat)
StretchModeParameter.__init__(self, name, value, const)
self.set_constant(const)
return
def set_constant(self, is_constant=True, value=None):
"""Toggle the Parameter as constant.
This sets the underlying ObjCrystMolAtomParSet positions
constant as well.
Parameters
----------
is_constant
Flag indicating if the Parameter is constant (default
True).
value
An optional value for the Parameter (default None). If this
is not None, then the Parameter will get a new value,
constant or otherwise.
Return
------
self
Returns self so that mutators can be chained.
"""
StretchModeParameter.set_constant(self, is_constant, value)
for a in [self.atom1, self.atom2]:
a.x.set_constant(is_constant)
a.y.set_constant(is_constant)
a.z.set_constant(is_constant)
return self
@deprecated(bl_setConst_dep_msg)
def setConst(self, const=True, value=None):
"""This function has been deprecated and will be removed in version
4.0.0.
Please use
diffpy.srfit.structure.objcryst.ObjCrystBondLengthParameter.set_constant
instead.
"""
self.set_constant(const, value)
return self
def getValue(self):
"""This calculates the value if it might have been changed.
There is no guarantee that the ObjCrystMolAtomParSets underlying
the bond won't change, so the bond length is calculated if
necessary each time this is called.
"""
if self._value is None:
val = GetBondLength(self.atom1.scat, self.atom2.scat)
Parameter.set_value(self, val)
return self._value
# End class ObjCrystBondLengthParameter
class ObjCrystBondAngleParameter(StretchModeParameter):
"""Class for abstracting a bond angle in a Molecule to a Parameter.
This wraps up a pyobjcryst.molecule.StretchModeBondAngle object so that the
angle defined by three MolAtoms in a Molecule can be used as an adjustable
Parameter. When a bond angle is adjusted, the third MolAtom is moved, and
the absolute position of the Molecule is altered to preserve the location
of the center of mass within the crystal. This can be changed by setting
the 'keepcenter' attribute of the parameter to False.
See precautions in the ObjCrystBondLengthParameter class.
Attributes
----------
atom1
The first ObjCrystAtomParSet in the bond angle
atom2
The second (central) ObjCrystMolAtomParSet in the bond angle
atom3
The third (mutated) ObjCrystMolAtomParSet in the bond angle
matoms
The set of all mutated ObjCrystMolAtomParSets
molecule
The ObjCrystMoleculeParSet the ObjCrystMolAtomParSets
belong to
mode
The pyobjcryst.molecule.StretchModeBondAngle for the bond angle
Inherited Attributes
--------------------
name
A name for this Parameter.
const
A flag indicating whether this is considered a constant.
_value
The value of the Parameter. Modified with 'set_value'.
value
Property for 'getValue' and 'set_value'.
constraint
A callable that calculates the value of this Parameter. If
this is None (None), the the Parameter is responsible for its
own value. The callable takes no arguments.
bounds
A 2-list defining the bounds on the Parameter. This can be
used by some optimizers when the Parameter is varied.
"""
def __init__(
self, name, atom1, atom2, atom3, value=None, const=False, mode=None
):
"""Create a ObjCrystBondAngleParameter.
Parameters
----------
name
The name of the ObjCrystBondAngleParameter.
atom1
The first atom (ObjCrystMolAtomParSet) in the bond angle
atom2
The second (central) atom (ObjCrystMolAtomParSet) in the
bond angle
atom3
The third (mutated) atom (ObjCrystMolAtomParSet) in the
bond angle
value
An initial value for the bond length. If this is None
(default), then the current bond angle between the atoms
will be used.
const
A flag indicating whether the Parameter is constant
(default False).
mode
A pre-built mode to place in this Parameter. If this is
None (default), then a StretchMode will be built.
"""
# Create the stretch mode
self.mode = mode
if mode is None:
self.mode = StretchModeBondAngle(
atom1.scat, atom2.scat, atom3.scat, None
)
# We only add the last atom. This is the one that will move
self.mode.AddAtom(atom3.scat)
self.matoms = set([atom3])
# Observe the atom positions
for a in [atom1, atom2, atom3]:
a.x.addObserver(self._flush)
a.y.addObserver(self._flush)
a.z.addObserver(self._flush)
self.atom1 = atom1
self.atom2 = atom2
self.atom3 = atom3
self.molecule = atom1.parent
# We do this last so the atoms are defined before we set any values.
if value is None:
value = GetBondAngle(atom1.scat, atom2.scat, atom3.scat)
StretchModeParameter.__init__(self, name, value, const)
self.set_constant(const)
return
def set_constant(self, is_constant=True, value=None):
"""Toggle the Parameter as constant.
This sets the underlying ObjCrystMolAtomParSet positions
constant as well.
Parameters
----------
is_constant
Flag indicating if the Parameter is constant (default
True).
value
An optional value for the Parameter (default None). If this
is not None, then the Parameter will get a new value,
constant or otherwise.
Return
------
self
Returns self so that mutators can be chained.
"""
StretchModeParameter.set_constant(self, is_constant, value)
for a in [self.atom1, self.atom2, self.atom3]:
a.x.set_constant(is_constant)
a.y.set_constant(is_constant)
a.z.set_constant(is_constant)
return self
@deprecated(ba_setConst_dep_msg)
def setConst(self, const=True, value=None):
"""This function has been deprecated and will be removed in
version 4.0.0.
Please use
diffpy.srfit.structure.objcryst.ObjCrystBondAngleParameter.set_constant
instead.
"""
self.set_constant(const, value)
return self
def getValue(self):
"""This calculates the value if it might have been changed.
There is no guarantee that the MolAtoms underlying the bond
angle won't change, so the bond angle is calculated if necessary
each time this is called.
"""
if self._value is None:
val = GetBondAngle(
self.atom1.scat, self.atom2.scat, self.atom3.scat
)
Parameter.set_value(self, val)
return self._value
# End class ObjCrystBondAngleParameter
class ObjCrystDihedralAngleParameter(StretchModeParameter):
"""Class for abstracting a dihedral angle in a Molecule to a
Parameter.
This wraps up a pyobjcryst.molecule.StretchModeTorsion object so that the
angle defined by four MolAtoms ([a1-a2].[a3-a4]) in a Molecule can be used
as an adjustable parameter. When a dihedral angle is adjusted, the fourth
MolAtom is moved, and the absolute position of the Molecule is altered to
preserve the location of the center of mass within the crystal. This can
be changed by setting the 'keepcenter' attribute of the parameter to False.
See precautions in the ObjCrystBondLengthParameter class.
Attributes
----------
atom1
The first ObjCrystMolAtomParSet in the dihedral angle
atom2
The second (central) ObjCrystMolAtomParSet in the dihedral
angle
atom3
The third (central) ObjCrystMolAtomParSet in the dihedral angle
atom4
The fourth (mutated) ObjCrystMolAtomParSet in the dihedral
angle
matoms
The set of all mutated ObjCrystMolAtomParSets
molecule
The ObjCrystMoleculeParSet the atoms belong to
mode
The pyobjcryst.molecule.StretchModeTorsion for the dihedral
angle
Inherited Attributes
--------------------
name
A name for this Parameter.
const
A flag indicating whether this is considered a constant.
_value
The value of the Parameter. Modified with 'set_value'.
value
Property for 'getValue' and 'set_value'.
constraint
A callable that calculates the value of this Parameter. If
this is None (None), the the Parameter is responsible for its
own value. The callable takes no arguments.
bounds
A 2-list defining the bounds on the Parameter. This can be
used by some optimizers when the Parameter is varied.
"""
def __init__(
self,
name,
atom1,
atom2,
atom3,
atom4,
value=None,
const=False,
mode=None,
):
"""Create a ObjCrystDihedralAngleParameter.
Parameters
----------
name
The name of the ObjCrystDihedralAngleParameter
atom1
The first atom (ObjCrystMolAtomParSet) in the dihderal
angle
atom2
The second (central) atom (ObjCrystMolAtomParSet) in the
dihderal angle
atom3
The third (central) atom (ObjCrystMolAtomParSet) in the
dihderal angle
atom4
The fourth (mutated) atom (ObjCrystMolAtomParSet) in the
dihderal angle
value
An initial value for the bond length. If this is None
(default), then the current dihedral angle between atoms
will be used.
const
A flag indicating whether the Parameter is constant
(default False).
mode
A pre-built mode to place in this Parameter. If this is
None (default), then a StretchMode will be built.
"""
# Create the stretch mode
self.mode = mode
if mode is None:
self.mode = StretchModeTorsion(atom2.scat, atom3.scat, None)
# We only add the last atom. This is the one that will move
self.mode.AddAtom(atom4.scat)
self.matoms = set([atom4])
# Observe the atom positions
for a in [atom1, atom2, atom3, atom4]:
a.x.addObserver(self._flush)
a.y.addObserver(self._flush)
a.z.addObserver(self._flush)
self.atom1 = atom1
self.atom2 = atom2
self.atom3 = atom3
self.atom4 = atom4
self.molecule = atom1.parent
# We do this last so the atoms are defined before we set any values.
if value is None:
value = GetDihedralAngle(
atom1.scat, atom2.scat, atom3.scat, atom4.scat
)
StretchModeParameter.__init__(self, name, value, const)
self.set_constant(const)
return
def set_constant(self, is_constant=True, value=None):
"""Toggle the Parameter as constant.
This sets the underlying ObjCrystMolAtomParSet positions const as well.
Parameters
----------
is_constant
Flag indicating if the Parameter is constant (default
True).
value
An optional value for the Parameter (default None). If this
is not None, then the Parameter will get a new value,
constant or otherwise.
Return
------
self
Returns self so that mutators can be chained.
"""
StretchModeParameter.set_constant(self, is_constant, value)
for a in [self.atom1, self.atom2, self.atom3, self.atom4]:
a.x.set_constant(is_constant)
a.y.set_constant(is_constant)
a.z.set_constant(is_constant)
return self
@deprecated(da_setConst_dep_msg)
def setConst(self, const=True, value=None):
"""This function has been deprecated and will be removed in
version 4.0.0.
Please use
diffpy.srfit.structure.objcryst.ObjCrystDihedralAngleParameter.set_constant
instead.
"""
self.set_constant(const, value)
return self
def getValue(self):
"""This calculates the value if it might have been changed.
There is no guarantee that the ObjCrystMolAtomParSets underlying
the dihedral angle won't change from some other Parameter, so
the value is recalculated each time.
"""
if self._value is None:
val = GetDihedralAngle(
self.atom1.scat,
self.atom2.scat,
self.atom3.scat,
self.atom4.scat,
)
Parameter.set_value(self, val)
return self._value
# End class ObjCrystDihedralAngleParameter
[docs]
class ObjCrystCrystalParSet(SrRealParSet):
"""A adaptor for pyobjcryst.crystal.Crystal instance.
This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet.
See this class for base attributes.
Attributes
----------
stru
The adapted pyobjcryst.Crystal.
scatterers
The list of aggregated ScattererParSets (either
ObjCrystAtomParSet or ObjCrystMoleculeParSet), provided for
convenience.
_sgpars
A BaseSpaceGroupParameters object containing free structure
Parameters. See the diffpy.srfit.structure.sgconstraints
module.
sgpars
property that creates _sgpars when it is needed.
angunits
"rad", the units of angle
Parameters
----------
x
Scatterer position in crystal coordinates (ParameterWraper)
y
Scatterer position in crystal coordinates (ParameterWraper)
z
Scatterer position in crystal coordinates (ParameterWraper)
occ
Occupancy of the scatterer on its crystal site (ParameterWraper)
"""
def __init__(self, name, cryst):
"""Initialize.
Parameters
----------
name
A name for this ParameterSet
cryst
An pyobjcryst.Crystal instance.
"""
SrRealParSet.__init__(self, name)
self.angunits = "rad"
self.stru = cryst
self._sgpars = None
self.addParameter(ParameterAdapter("a", self.stru, attr="a"))
self.addParameter(ParameterAdapter("b", self.stru, attr="b"))
self.addParameter(ParameterAdapter("c", self.stru, attr="c"))
self.addParameter(ParameterAdapter("alpha", self.stru, attr="alpha"))
self.addParameter(ParameterAdapter("beta", self.stru, attr="beta"))
self.addParameter(ParameterAdapter("gamma", self.stru, attr="gamma"))
# Now we must loop over the scatterers and create parameter sets from
# them.
self.scatterers = []
snames = []
for j in range(self.stru.GetNbScatterer()):
s = self.stru.GetScatt(j)
name = s.GetName()
if not name:
raise ValueError("Each Scatterer must have a name")
if name in snames:
raise ValueError("Scatterer name '%s' is duplicated" % name)
# Now create the proper object
cname = s.GetClassName()
if cname == "Atom":
parset = ObjCrystAtomParSet(name, s, self)
elif cname == "Molecule":
parset = ObjCrystMoleculeParSet(name, s, self)
else:
raise TypeError("Unrecognized scatterer '%s'" % cname)
self.add_parameter_set(parset)
self.scatterers.append(parset)
snames.append(name)
return
def _constrain_space_group(self):
"""Constrain the space group."""
if self._sgpars is not None:
return self._sgpars
sg = self._create_space_group(self.stru.GetSpaceGroup())
from diffpy.srfit.structure.sgconstraints import (
_constrain_as_space_group,
)
adpsymbols = ["B11", "B22", "B33", "B12", "B13", "B23"]
isosymbol = "Biso"
sgoffset = [0, 0, 0]
self._sgpars = _constrain_as_space_group(
self,
sg,
self.scatterers,
sgoffset,
adpsymbols=adpsymbols,
isosymbol=isosymbol,
)
return self._sgpars
sgpars = property(_constrain_space_group)
@staticmethod
def _create_space_group(sgobjcryst):
"""Create a diffpy.structure SpaceGroup object from pyobjcryst.
Parameters
----------
sgobjcryst
A pyobjcryst.spacegroup.SpaceGroup instance.
This uses the actual space group operations from the
pyobjcryst.spacegroup.SpaceGroup instance so there is no ambiguity
about the actual space group.
"""
import copy
from diffpy.structure.spacegroups import GetSpaceGroup, SymOp
name = sgobjcryst.GetName()
extnstr = ":%s" % sgobjcryst.GetExtension()
if name.endswith(extnstr):
name = name[: -len(extnstr)]
# Get whatever spacegroup we can get by name. This will set the proper
# crystal system. Creating a copy of the singleton from GetSpaceGroup,
# as this function messes with sg.symop_list.
sg = copy.copy(GetSpaceGroup(name))
# Replace the symmetry operations to guarantee that we get it right.
symops = sgobjcryst.GetSymmetryOperations()
tranops = sgobjcryst.GetTranslationVectors()
sg.symop_list = []
for trans in tranops:
for shift, rot in symops:
tv = trans + shift
tv -= numpy.floor(tv)
sg.symop_list.append(SymOp(rot, tv))
if sgobjcryst.IsCentrosymmetric():
center = sgobjcryst.GetInversionCenter()
for trans in tranops:
for shift, rot in symops:
tv = center - trans - shift
tv -= numpy.floor(tv)
sg.symop_list.append(SymOp(-rot, tv))
return sg
[docs]
@classmethod
def canAdapt(self, stru):
"""Return whether the structure can be adapted by this class."""
from pyobjcryst.crystal import Crystal
return isinstance(stru, Crystal)
[docs]
def getLattice(self):
"""Get the ParameterSet containing the lattice Parameters."""
return self
[docs]
def getScatterers(self):
"""Get a list of ParameterSets that represents the scatterers.
The site positions must be accessible from the list entries via
the names "x", "y", and "z". The ADPs must be accessible as
well, but the name and nature of the ADPs (U-factors, B-factors,
isotropic, anisotropic) depends on the adapted structure.
"""
return self.scatterers
# End class ObjCrystCrystalParSet