diffpy.srfit 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffpy/__init__.py +15 -0
- diffpy/srfit/__init__.py +41 -0
- diffpy/srfit/equation/__init__.py +31 -0
- diffpy/srfit/equation/builder.py +702 -0
- diffpy/srfit/equation/equationmod.py +213 -0
- diffpy/srfit/equation/literals/__init__.py +71 -0
- diffpy/srfit/equation/literals/abcs.py +81 -0
- diffpy/srfit/equation/literals/argument.py +76 -0
- diffpy/srfit/equation/literals/literal.py +69 -0
- diffpy/srfit/equation/literals/operators.py +380 -0
- diffpy/srfit/equation/visitors/__init__.py +94 -0
- diffpy/srfit/equation/visitors/argfinder.py +62 -0
- diffpy/srfit/equation/visitors/printer.py +120 -0
- diffpy/srfit/equation/visitors/swapper.py +142 -0
- diffpy/srfit/equation/visitors/validator.py +113 -0
- diffpy/srfit/equation/visitors/visitor.py +62 -0
- diffpy/srfit/exceptions.py +29 -0
- diffpy/srfit/fitbase/__init__.py +53 -0
- diffpy/srfit/fitbase/calculator.py +110 -0
- diffpy/srfit/fitbase/configurable.py +57 -0
- diffpy/srfit/fitbase/constraint.py +117 -0
- diffpy/srfit/fitbase/fitcontribution.py +351 -0
- diffpy/srfit/fitbase/fithook.py +227 -0
- diffpy/srfit/fitbase/fitrecipe.py +826 -0
- diffpy/srfit/fitbase/fitresults.py +674 -0
- diffpy/srfit/fitbase/parameter.py +314 -0
- diffpy/srfit/fitbase/parameterset.py +110 -0
- diffpy/srfit/fitbase/profile.py +405 -0
- diffpy/srfit/fitbase/profilegenerator.py +168 -0
- diffpy/srfit/fitbase/profileparser.py +179 -0
- diffpy/srfit/fitbase/recipeorganizer.py +1074 -0
- diffpy/srfit/fitbase/restraint.py +117 -0
- diffpy/srfit/fitbase/simplerecipe.py +230 -0
- diffpy/srfit/fitbase/validatable.py +59 -0
- diffpy/srfit/interface/__init__.py +33 -0
- diffpy/srfit/interface/interface.py +180 -0
- diffpy/srfit/pdf/__init__.py +24 -0
- diffpy/srfit/pdf/basepdfgenerator.py +296 -0
- diffpy/srfit/pdf/characteristicfunctions.py +432 -0
- diffpy/srfit/pdf/debyepdfgenerator.py +115 -0
- diffpy/srfit/pdf/pdfcontribution.py +311 -0
- diffpy/srfit/pdf/pdfgenerator.py +77 -0
- diffpy/srfit/pdf/pdfparser.py +226 -0
- diffpy/srfit/sas/__init__.py +30 -0
- diffpy/srfit/sas/prcalculator.py +139 -0
- diffpy/srfit/sas/sasgenerator.py +70 -0
- diffpy/srfit/sas/sasimport.py +66 -0
- diffpy/srfit/sas/sasparameter.py +70 -0
- diffpy/srfit/sas/sasparser.py +133 -0
- diffpy/srfit/sas/sasprofile.py +94 -0
- diffpy/srfit/structure/__init__.py +60 -0
- diffpy/srfit/structure/basestructureparset.py +62 -0
- diffpy/srfit/structure/bvsrestraint.py +101 -0
- diffpy/srfit/structure/cctbxparset.py +292 -0
- diffpy/srfit/structure/diffpyparset.py +303 -0
- diffpy/srfit/structure/objcrystparset.py +1552 -0
- diffpy/srfit/structure/sgconstraints.py +761 -0
- diffpy/srfit/structure/srrealparset.py +92 -0
- diffpy/srfit/util/__init__.py +50 -0
- diffpy/srfit/util/argbinders.py +29 -0
- diffpy/srfit/util/inpututils.py +50 -0
- diffpy/srfit/util/nameutils.py +45 -0
- diffpy/srfit/util/observable.py +93 -0
- diffpy/srfit/util/tagmanager.py +150 -0
- diffpy/srfit/util/weakrefcallable.py +170 -0
- diffpy/srfit/version.py +26 -0
- diffpy_srfit-3.1.0.dist-info/METADATA +216 -0
- diffpy_srfit-3.1.0.dist-info/RECORD +73 -0
- diffpy_srfit-3.1.0.dist-info/WHEEL +5 -0
- diffpy_srfit-3.1.0.dist-info/licenses/AUTHORS.rst +10 -0
- diffpy_srfit-3.1.0.dist-info/licenses/LICENSE.rst +138 -0
- diffpy_srfit-3.1.0.dist-info/licenses/LICENSE_DANSE.rst +34 -0
- diffpy_srfit-3.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1552 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.srfit by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2009 The Trustees of Columbia University
|
|
7
|
+
# in the City of New York. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Chris Farrow
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
"""Wrappers for adapting pyobjcryst.crystal.Crystal to a srfit ParameterSet.
|
|
16
|
+
|
|
17
|
+
This will adapt a Crystal or Molecule object from pyobjcryst into the
|
|
18
|
+
ParameterSet interface. The following classes are adapted.
|
|
19
|
+
|
|
20
|
+
ObjCrystCrystalParSet -- Adapter for pyobjcryst.crystal.Crystal
|
|
21
|
+
ObjCrystAtomParSet -- Adapter for pyobjcryst.atom.Atom
|
|
22
|
+
ObjCrystMoleculeParSet -- Adapter for pyobjcryst.molecule.Molecule
|
|
23
|
+
ObjCrystMolAtomParSet -- Adapter for pyobjcryst.molecule.MolAtom
|
|
24
|
+
|
|
25
|
+
Related to the adaptation of Molecule and MolAtom, there are adaptors for
|
|
26
|
+
specifying molecule restraints.
|
|
27
|
+
ObjCrystBondLengthRestraint
|
|
28
|
+
ObjCrystBondAngleRestraint
|
|
29
|
+
ObjCrystDihedralAngleRestraint
|
|
30
|
+
|
|
31
|
+
There are also Parameters for encapsulating and modifying atoms via their
|
|
32
|
+
relative positions. These Parameters can also act like constraints, and can
|
|
33
|
+
modify the positions of multiple MolAtoms.
|
|
34
|
+
ObjCrystBondLengthParameter
|
|
35
|
+
ObjCrystBondAngleParameter
|
|
36
|
+
ObjCrystDihedralAngleParameter
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__all__ = ["ObjCrystMoleculeParSet", "ObjCrystCrystalParSet"]
|
|
40
|
+
|
|
41
|
+
import numpy
|
|
42
|
+
from pyobjcryst.molecule import (
|
|
43
|
+
GetBondAngle,
|
|
44
|
+
GetBondLength,
|
|
45
|
+
GetDihedralAngle,
|
|
46
|
+
StretchModeBondAngle,
|
|
47
|
+
StretchModeBondLength,
|
|
48
|
+
StretchModeTorsion,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
from diffpy.srfit.fitbase.parameter import (
|
|
52
|
+
Parameter,
|
|
53
|
+
ParameterAdapter,
|
|
54
|
+
ParameterProxy,
|
|
55
|
+
)
|
|
56
|
+
from diffpy.srfit.fitbase.parameterset import ParameterSet
|
|
57
|
+
from diffpy.srfit.structure.srrealparset import SrRealParSet
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ObjCrystScattererParSet(ParameterSet):
|
|
61
|
+
"""A base adaptor for an Objcryst Scatterer.
|
|
62
|
+
|
|
63
|
+
This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet and
|
|
64
|
+
adapts pyobjcryst.scatterer.Scatterer derivatives (Molecule, Atom) and
|
|
65
|
+
objects with a similar interface (MolAtom). See the ParameterSet class for
|
|
66
|
+
base attributes.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
scat -- The adapted pyobjcryst object.
|
|
70
|
+
parent -- The ParameterSet this belongs to
|
|
71
|
+
|
|
72
|
+
Managed Parameters:
|
|
73
|
+
x, y, z -- Scatterer position in crystal coordinates (ParameterWraper)
|
|
74
|
+
occ -- Occupancy of the scatterer on its crystal site
|
|
75
|
+
(ParameterWraper)
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, name, scat, parent):
|
|
79
|
+
"""Initialize.
|
|
80
|
+
|
|
81
|
+
name -- The name of the scatterer
|
|
82
|
+
scat -- The pyobjcryst.Scatterer instance
|
|
83
|
+
parent -- The ParameterSet this belongs to
|
|
84
|
+
"""
|
|
85
|
+
ParameterSet.__init__(self, name)
|
|
86
|
+
self.scat = scat
|
|
87
|
+
self.parent = parent
|
|
88
|
+
|
|
89
|
+
# x, y, z, occ
|
|
90
|
+
self.addParameter(ParameterAdapter("x", self.scat, attr="X"))
|
|
91
|
+
self.addParameter(ParameterAdapter("y", self.scat, attr="Y"))
|
|
92
|
+
self.addParameter(ParameterAdapter("z", self.scat, attr="Z"))
|
|
93
|
+
self.addParameter(ParameterAdapter("occ", self.scat, attr="Occupancy"))
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
def isDummy(self):
|
|
97
|
+
"""Indicate whether this scatterer is a dummy atom."""
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
def hasScatterers(self):
|
|
101
|
+
"""Indicate if this scatterer has its own scatterers."""
|
|
102
|
+
return hasattr(self, "getScatterers")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# End class ObjCrystScattererParSet
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ObjCrystAtomParSet(ObjCrystScattererParSet):
|
|
109
|
+
"""A adaptor for a pyobjcryst.Atom.
|
|
110
|
+
|
|
111
|
+
This class derives from ObjCrystScattererParSet.
|
|
112
|
+
|
|
113
|
+
Attributes:
|
|
114
|
+
scat -- The adapted pyobjcryst.atom.Atom.
|
|
115
|
+
element -- Non-refinable name of the element (property).
|
|
116
|
+
parent -- The ObjCrystCrystalParSet this belongs to.
|
|
117
|
+
|
|
118
|
+
Managed Parameters:
|
|
119
|
+
x, y, z -- Atom position in crystal coordinates (ParameterAdapter)
|
|
120
|
+
occ -- Occupancy of the atom on its crystal location
|
|
121
|
+
(ParameterAdapter)
|
|
122
|
+
Biso -- Isotropic scattering factor (ParameterAdapter).
|
|
123
|
+
B11, B22, B33, B12, B21, B23, B32, B13, B31
|
|
124
|
+
-- Anisotropic displacement factor for scatterer
|
|
125
|
+
(ParameterAdapter or ParameterProxy). Note that the Bij and Bji
|
|
126
|
+
parameters are the same.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, name, atom, parent):
|
|
130
|
+
"""Initialize.
|
|
131
|
+
|
|
132
|
+
name -- The name of the scatterer
|
|
133
|
+
scat -- The Scatterer instance
|
|
134
|
+
parent -- The ObjCrystCrystalParSet this belongs to
|
|
135
|
+
"""
|
|
136
|
+
ObjCrystScattererParSet.__init__(self, name, atom, parent)
|
|
137
|
+
sp = atom.GetScatteringPower()
|
|
138
|
+
|
|
139
|
+
# The B-parameters
|
|
140
|
+
self.addParameter(ParameterAdapter("Biso", sp, attr="Biso"))
|
|
141
|
+
self.addParameter(ParameterAdapter("B11", sp, attr="B11"))
|
|
142
|
+
self.addParameter(ParameterAdapter("B22", sp, attr="B22"))
|
|
143
|
+
self.addParameter(ParameterAdapter("B33", sp, attr="B33"))
|
|
144
|
+
B12 = ParameterAdapter("B12", sp, attr="B12")
|
|
145
|
+
B21 = ParameterProxy("B21", B12)
|
|
146
|
+
B13 = ParameterAdapter("B13", sp, attr="B13")
|
|
147
|
+
B31 = ParameterProxy("B31", B13)
|
|
148
|
+
B23 = ParameterAdapter("B23", sp, attr="B23")
|
|
149
|
+
B32 = ParameterProxy("B32", B23)
|
|
150
|
+
self.addParameter(B12)
|
|
151
|
+
self.addParameter(B21)
|
|
152
|
+
self.addParameter(B13)
|
|
153
|
+
self.addParameter(B31)
|
|
154
|
+
self.addParameter(B23)
|
|
155
|
+
self.addParameter(B32)
|
|
156
|
+
|
|
157
|
+
# Give a value to Biso if it doesn't have one, and this is isotropic
|
|
158
|
+
if sp.IsIsotropic() and self.Biso.value == 0:
|
|
159
|
+
self.Biso.value = 0.5
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
def _getElem(self):
|
|
163
|
+
"""Getter for the element type."""
|
|
164
|
+
return self.scat.GetScatteringPower().GetSymbol()
|
|
165
|
+
|
|
166
|
+
element = property(_getElem)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# End class ObjCrystAtomParSet
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ObjCrystMoleculeParSet(ObjCrystScattererParSet):
|
|
173
|
+
"""A adaptor for a pyobjcryst.Molecule.
|
|
174
|
+
|
|
175
|
+
This class derives from ObjCrystScattererParSet.
|
|
176
|
+
|
|
177
|
+
Attributes:
|
|
178
|
+
scat -- The adapted pyobjcryst.molecule.Molecule.
|
|
179
|
+
stru -- The adapted pyobjcryst.molecule.Molecule.
|
|
180
|
+
parent -- The ObjCrystCrystalParSet this belongs to.
|
|
181
|
+
ObjCrystMoleculeParSets can be used on their own, in which
|
|
182
|
+
case this is None.
|
|
183
|
+
|
|
184
|
+
Managed Parameters:
|
|
185
|
+
x, y, z -- Molecule position in crystal coordinates (ParameterAdapter)
|
|
186
|
+
occ -- Occupancy of the molecule on its crystal location
|
|
187
|
+
(ParameterAdapter)
|
|
188
|
+
q0, q1, q2, q3 -- Orientational quaternion (ParameterAdapter)
|
|
189
|
+
|
|
190
|
+
Other attributes are inherited from
|
|
191
|
+
diffpy.srfit.fitbase.parameterset.ParameterSet
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
def __init__(self, name, molecule, parent=None):
|
|
195
|
+
"""Initialize.
|
|
196
|
+
|
|
197
|
+
name -- The name of the scatterer
|
|
198
|
+
molecule -- The pyobjcryst.Molecule instance
|
|
199
|
+
parent -- The ObjCrystCrystalParSet this belongs to (default None).
|
|
200
|
+
"""
|
|
201
|
+
ObjCrystScattererParSet.__init__(self, name, molecule, parent)
|
|
202
|
+
self.stru = molecule
|
|
203
|
+
|
|
204
|
+
# Add orientation quaternion
|
|
205
|
+
self.addParameter(ParameterAdapter("q0", self.scat, attr="Q0"))
|
|
206
|
+
self.addParameter(ParameterAdapter("q1", self.scat, attr="Q1"))
|
|
207
|
+
self.addParameter(ParameterAdapter("q2", self.scat, attr="Q2"))
|
|
208
|
+
self.addParameter(ParameterAdapter("q3", self.scat, attr="Q3"))
|
|
209
|
+
|
|
210
|
+
# Wrap the MolAtoms within the molecule
|
|
211
|
+
self.atoms = []
|
|
212
|
+
anames = []
|
|
213
|
+
|
|
214
|
+
for a in molecule:
|
|
215
|
+
|
|
216
|
+
name = a.GetName()
|
|
217
|
+
if not name:
|
|
218
|
+
raise AttributeError("Each MolAtom must have a name")
|
|
219
|
+
if name in anames:
|
|
220
|
+
raise AttributeError("MolAtom name '%s' is duplicated" % name)
|
|
221
|
+
|
|
222
|
+
atom = ObjCrystMolAtomParSet(name, a, self)
|
|
223
|
+
atom.molecule = self
|
|
224
|
+
self.addParameterSet(atom)
|
|
225
|
+
self.atoms.append(atom)
|
|
226
|
+
anames.append(name)
|
|
227
|
+
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def canAdapt(self, stru):
|
|
232
|
+
"""Return whether the structure can be adapted by this class."""
|
|
233
|
+
from pyobjcryst.molecule import Molecule
|
|
234
|
+
|
|
235
|
+
return isinstance(stru, Molecule)
|
|
236
|
+
|
|
237
|
+
# Part of SrRealParSet interface
|
|
238
|
+
def useSymmetry(self, use=True):
|
|
239
|
+
"""Set this structure to use symmetry.
|
|
240
|
+
|
|
241
|
+
This structure object does not support symmetry.
|
|
242
|
+
"""
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
# Part of SrRealParSet interface
|
|
246
|
+
def usingSymmetry(self):
|
|
247
|
+
"""Check if symmetry is being used.
|
|
248
|
+
|
|
249
|
+
This structure object does not support symmetry.
|
|
250
|
+
"""
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
# Part of SrRealParSet interface
|
|
254
|
+
def _getSrRealStructure(self):
|
|
255
|
+
"""Get the structure object for use with SrReal calculators.
|
|
256
|
+
|
|
257
|
+
Molecule objects are never periodic. Return the object and let
|
|
258
|
+
the SrReal adapters do the proper thing.
|
|
259
|
+
"""
|
|
260
|
+
return self.stru
|
|
261
|
+
|
|
262
|
+
def getLattice(self):
|
|
263
|
+
"""Get the ParameterSet containing the lattice Parameters."""
|
|
264
|
+
lattice = ParameterSet("lattice")
|
|
265
|
+
lattice.newPar("a", 1.0)
|
|
266
|
+
lattice.newPar("b", 1.0)
|
|
267
|
+
lattice.newPar("c", 1.0)
|
|
268
|
+
lattice.newPar("alpha", 90)
|
|
269
|
+
lattice.newPar("beta", 90)
|
|
270
|
+
lattice.newPar("gamma", 90)
|
|
271
|
+
lattice.angunits = "deg"
|
|
272
|
+
return lattice
|
|
273
|
+
|
|
274
|
+
def getScatterers(self):
|
|
275
|
+
"""Get a list of ParameterSets that represents the scatterers.
|
|
276
|
+
|
|
277
|
+
The site positions must be accessible from the list entries via
|
|
278
|
+
the names "x", "y", and "z". The ADPs must be accessible as
|
|
279
|
+
well, but the name and nature of the ADPs (U-factors, B-factors,
|
|
280
|
+
isotropic, anisotropic) depends on the adapted structure.
|
|
281
|
+
"""
|
|
282
|
+
return self.atoms
|
|
283
|
+
|
|
284
|
+
def wrapRestraints(self):
|
|
285
|
+
"""Wrap the restraints implicit to the molecule.
|
|
286
|
+
|
|
287
|
+
This will wrap MolBonds, MolBondAngles and MolDihedralAngles of
|
|
288
|
+
the Molecule as ObjCrystMoleculeRestraint objects.
|
|
289
|
+
"""
|
|
290
|
+
# Wrap restraints. Restraints wrapped in this way cannot be modified
|
|
291
|
+
# from within this class.
|
|
292
|
+
for b in self.scat.GetBondList():
|
|
293
|
+
res = ObjCrystMoleculeRestraint(b)
|
|
294
|
+
self._restraints.add(res)
|
|
295
|
+
|
|
296
|
+
for ba in self.scat.GetBondAngleList():
|
|
297
|
+
res = ObjCrystMoleculeRestraint(ba)
|
|
298
|
+
self._restraints.add(res)
|
|
299
|
+
|
|
300
|
+
for da in self.scat.GetDihedralAngleList():
|
|
301
|
+
res = ObjCrystMoleculeRestraint(da)
|
|
302
|
+
self._restraints.add(res)
|
|
303
|
+
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
def wrapStretchModeParameters(self):
|
|
307
|
+
"""Wrap the stretch modes implicit to the Molecule as Parameters.
|
|
308
|
+
|
|
309
|
+
This will wrap StretchModeBondLengths and StretchModeBondAngles of the
|
|
310
|
+
Molecule as Parameters. Note that this requires that the MolBondAtoms
|
|
311
|
+
in the Molecule came in with unique names. Torsion angles are not
|
|
312
|
+
wrapped, as there is not enough information to determine each MolAtom
|
|
313
|
+
in the angle.
|
|
314
|
+
|
|
315
|
+
The Parameters will be given the concatenated name of its constituents.
|
|
316
|
+
bond lengths: "bl_aname1_aname2"
|
|
317
|
+
bond angles: "ba_aname1_aname2_aname3"
|
|
318
|
+
"""
|
|
319
|
+
for mode in self.scat.GetStretchModeBondLengthList():
|
|
320
|
+
name1 = mode.mpAtom0.GetName()
|
|
321
|
+
name2 = mode.mpAtom1.GetName()
|
|
322
|
+
|
|
323
|
+
name = "bl_" + "_".join((name1, name2))
|
|
324
|
+
|
|
325
|
+
atom1 = getattr(self, name1)
|
|
326
|
+
atom2 = getattr(self, name2)
|
|
327
|
+
|
|
328
|
+
par = ObjCrystBondLengthParameter(name, atom1, atom2, mode=mode)
|
|
329
|
+
|
|
330
|
+
atoms = []
|
|
331
|
+
for a in mode.GetAtoms():
|
|
332
|
+
name = a.GetName()
|
|
333
|
+
atoms.append(getattr(self, name))
|
|
334
|
+
|
|
335
|
+
par.AddAtoms(atoms)
|
|
336
|
+
|
|
337
|
+
self.addParameter(par)
|
|
338
|
+
|
|
339
|
+
for mode in self.scat.GetStretchModeBondAngleList():
|
|
340
|
+
name1 = mode.mpAtom0.GetName()
|
|
341
|
+
name2 = mode.mpAtom1.GetName()
|
|
342
|
+
name3 = mode.mpAtom2.GetName()
|
|
343
|
+
|
|
344
|
+
name = "ba_" + "_".join((name1, name2, name3))
|
|
345
|
+
|
|
346
|
+
atom1 = getattr(self, name1)
|
|
347
|
+
atom2 = getattr(self, name2)
|
|
348
|
+
atom3 = getattr(self, name3)
|
|
349
|
+
|
|
350
|
+
par = ObjCrystBondAngleParameter(
|
|
351
|
+
name, atom1, atom2, atom3, mode=mode
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
atoms = []
|
|
355
|
+
for a in mode.GetAtoms():
|
|
356
|
+
name = a.GetName()
|
|
357
|
+
atoms.append(getattr(self, name))
|
|
358
|
+
par.AddAtoms(atoms)
|
|
359
|
+
|
|
360
|
+
self.addParameter(par)
|
|
361
|
+
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
def restrainBondLength(
|
|
365
|
+
self, atom1, atom2, length, sigma, delta, scaled=False
|
|
366
|
+
):
|
|
367
|
+
"""Add a bond length restraint.
|
|
368
|
+
|
|
369
|
+
This creates an instance of ObjCrystBondLengthRestraint and adds it to
|
|
370
|
+
the ObjCrystMoleculeParSet.
|
|
371
|
+
|
|
372
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the bond
|
|
373
|
+
atom2 -- Second atom (ObjCrystMolAtomParSet) in the bond
|
|
374
|
+
length -- The length of the bond (Angstroms)
|
|
375
|
+
sigma -- The uncertainty of the bond length (Angstroms)
|
|
376
|
+
delta -- The width of the bond (Angstroms)
|
|
377
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
378
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
379
|
+
(default False)
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
The ObjCrystBondLengthRestraint object for use with the
|
|
384
|
+
'unrestrain' method.
|
|
385
|
+
"""
|
|
386
|
+
res = ObjCrystBondLengthRestraint(
|
|
387
|
+
atom1, atom2, length, sigma, delta, scaled
|
|
388
|
+
)
|
|
389
|
+
self._restraints.add(res)
|
|
390
|
+
|
|
391
|
+
return res
|
|
392
|
+
|
|
393
|
+
def restrainBondLengthParameter(
|
|
394
|
+
self, par, length, sigma, delta, scaled=False
|
|
395
|
+
):
|
|
396
|
+
"""Add a bond length restraint.
|
|
397
|
+
|
|
398
|
+
This creates an instance of ObjCrystBondLengthRestraint and adds it to
|
|
399
|
+
the ObjCrystMoleculeParSet.
|
|
400
|
+
|
|
401
|
+
par -- A ObjCrystBondLengthParameter (see addBondLengthParameter)
|
|
402
|
+
length -- The length of the bond (Angstroms)
|
|
403
|
+
sigma -- The uncertainty of the bond length (Angstroms)
|
|
404
|
+
delta -- The width of the bond (Angstroms)
|
|
405
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
406
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
407
|
+
(default False)
|
|
408
|
+
|
|
409
|
+
Returns the ObjCrystBondLengthRestraint object for use with the
|
|
410
|
+
'unrestrain' method.
|
|
411
|
+
"""
|
|
412
|
+
return self.restrainBondLength(
|
|
413
|
+
par.atom1, par.atom2, length, sigma, delta, scaled
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def restrainBondAngle(
|
|
417
|
+
self, atom1, atom2, atom3, angle, sigma, delta, scaled=False
|
|
418
|
+
):
|
|
419
|
+
"""Add a bond angle restraint.
|
|
420
|
+
|
|
421
|
+
This creates an instance of ObjCrystBondAngleRestraint and adds it to
|
|
422
|
+
the ObjCrystMoleculeParSet.
|
|
423
|
+
|
|
424
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the bond angle
|
|
425
|
+
atom2 -- Second (central) atom (ObjCrystMolAtomParSet) in the bond
|
|
426
|
+
angle
|
|
427
|
+
atom3 -- Third atom (ObjCrystMolAtomParSet) in the bond angle
|
|
428
|
+
angle -- The bond angle (radians)
|
|
429
|
+
sigma -- The uncertainty of the bond angle (radians)
|
|
430
|
+
delta -- The width of the bond angle (radians)
|
|
431
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
432
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
433
|
+
(default False).
|
|
434
|
+
|
|
435
|
+
Returns the ObjCrystBondAngleRestraint object for use with the
|
|
436
|
+
'unrestrain' method.
|
|
437
|
+
"""
|
|
438
|
+
res = ObjCrystBondAngleRestraint(
|
|
439
|
+
atom1, atom2, atom3, angle, sigma, delta, scaled
|
|
440
|
+
)
|
|
441
|
+
self._restraints.add(res)
|
|
442
|
+
|
|
443
|
+
return res
|
|
444
|
+
|
|
445
|
+
def restrainBondAngleParameter(
|
|
446
|
+
self, par, angle, sigma, delta, scaled=False
|
|
447
|
+
):
|
|
448
|
+
"""Add a bond angle restraint.
|
|
449
|
+
|
|
450
|
+
This creates an instance of ObjCrystBondAngleRestraint and adds it to
|
|
451
|
+
the ObjCrystMoleculeParSet.
|
|
452
|
+
|
|
453
|
+
par -- A ObjCrystBondAngleParameter (see addBondAngleParameter)
|
|
454
|
+
angle -- The bond angle (radians)
|
|
455
|
+
sigma -- The uncertainty of the bond angle (radians)
|
|
456
|
+
delta -- The width of the bond angle (radians)
|
|
457
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
458
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
459
|
+
(default False).
|
|
460
|
+
|
|
461
|
+
Returns the ObjCrystBondAngleRestraint object for use with the
|
|
462
|
+
'unrestrain' method.
|
|
463
|
+
"""
|
|
464
|
+
return self.restrainBondAngle(
|
|
465
|
+
par.atom1, par.atom2, par.atom3, angle, sigma, delta, scaled
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
def restrainDihedralAngle(
|
|
469
|
+
self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False
|
|
470
|
+
):
|
|
471
|
+
"""Add a dihedral angle restraint.
|
|
472
|
+
|
|
473
|
+
This creates an instance of ObjCrystDihedralAngleRestraint and adds it
|
|
474
|
+
to the ObjCrystMoleculeParSet.
|
|
475
|
+
|
|
476
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the angle
|
|
477
|
+
atom2 -- Second (central) atom (ObjCrystMolAtomParSet) in the angle
|
|
478
|
+
atom3 -- Third (central) atom (ObjCrystMolAtomParSet) in the angle
|
|
479
|
+
atom4 -- Fourth atom in the angle (ObjCrystMolAtomParSet)
|
|
480
|
+
angle -- The dihedral angle (radians)
|
|
481
|
+
sigma -- The uncertainty of the dihedral angle (radians)
|
|
482
|
+
delta -- The width of the dihedral angle (radians)
|
|
483
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
484
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
485
|
+
(default False).
|
|
486
|
+
|
|
487
|
+
Returns the ObjCrystDihedralAngleRestraint object for use with the
|
|
488
|
+
'unrestrain' method.
|
|
489
|
+
"""
|
|
490
|
+
res = ObjCrystDihedralAngleRestraint(
|
|
491
|
+
atom1, atom2, atom3, atom4, angle, sigma, delta, scaled
|
|
492
|
+
)
|
|
493
|
+
self._restraints.add(res)
|
|
494
|
+
|
|
495
|
+
return res
|
|
496
|
+
|
|
497
|
+
def restrainDihedralAngleParameter(
|
|
498
|
+
self, par, angle, sigma, delta, scaled=False
|
|
499
|
+
):
|
|
500
|
+
"""Add a dihedral angle restraint.
|
|
501
|
+
|
|
502
|
+
This creates an instance of ObjCrystDihedralAngleRestraint and adds it
|
|
503
|
+
to the ObjCrystMoleculeParSet.
|
|
504
|
+
|
|
505
|
+
par -- A ObjCrystDihedralAngleParameter (see
|
|
506
|
+
addDihedralAngleParameter)
|
|
507
|
+
angle -- The dihedral angle (radians)
|
|
508
|
+
sigma -- The uncertainty of the dihedral angle (radians)
|
|
509
|
+
delta -- The width of the dihedral angle (radians)
|
|
510
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
511
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
512
|
+
(default False).
|
|
513
|
+
|
|
514
|
+
Returns the ObjCrystDihedralAngleRestraint object for use with the
|
|
515
|
+
'unrestrain' method.
|
|
516
|
+
"""
|
|
517
|
+
return self.restrainDihedralAngle(
|
|
518
|
+
par.atom1,
|
|
519
|
+
par.atom2,
|
|
520
|
+
par.atom3,
|
|
521
|
+
par.atom4,
|
|
522
|
+
angle,
|
|
523
|
+
sigma,
|
|
524
|
+
delta,
|
|
525
|
+
scaled,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
def addBondLengthParameter(
|
|
529
|
+
self, name, atom1, atom2, value=None, const=False
|
|
530
|
+
):
|
|
531
|
+
"""Add a bond length to the Molecule.
|
|
532
|
+
|
|
533
|
+
This creates a ObjCrystBondLengthParameter to the
|
|
534
|
+
ObjCrystMoleculeParSet that can be adjusted during the fit.
|
|
535
|
+
|
|
536
|
+
name -- The name of the ObjCrystBondLengthParameter
|
|
537
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the bond
|
|
538
|
+
atom2 -- The second (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
539
|
+
bond
|
|
540
|
+
value -- An initial value for the bond length. If this is None
|
|
541
|
+
(default), then the current distance between the atoms will
|
|
542
|
+
be used.
|
|
543
|
+
const -- A flag indicating whether the Parameter is constant
|
|
544
|
+
(default False)
|
|
545
|
+
|
|
546
|
+
Returns the new ObjCrystBondLengthParameter.
|
|
547
|
+
"""
|
|
548
|
+
par = ObjCrystBondLengthParameter(name, atom1, atom2, value, const)
|
|
549
|
+
self.addParameter(par)
|
|
550
|
+
|
|
551
|
+
return par
|
|
552
|
+
|
|
553
|
+
def addBondAngleParameter(
|
|
554
|
+
self, name, atom1, atom2, atom3, value=None, const=False
|
|
555
|
+
):
|
|
556
|
+
"""Add a bond angle to the Molecule.
|
|
557
|
+
|
|
558
|
+
This creates a ObjCrystBondAngleParameter to the ObjCrystMoleculeParSet
|
|
559
|
+
that can be adjusted during the fit.
|
|
560
|
+
|
|
561
|
+
name -- The name of the ObjCrystBondAngleParameter
|
|
562
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the bond angle
|
|
563
|
+
atom2 -- The second (central) atom (ObjCrystMolAtomParSet) in the
|
|
564
|
+
bond angle
|
|
565
|
+
atom3 -- The third (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
566
|
+
bond angle
|
|
567
|
+
value -- An initial value for the bond angle. If this is None
|
|
568
|
+
(default), then the current bond angle between the atoms
|
|
569
|
+
will be used.
|
|
570
|
+
const -- A flag indicating whether the Parameter is constant
|
|
571
|
+
(default False).
|
|
572
|
+
|
|
573
|
+
Returns the new ObjCrystBondAngleParameter.
|
|
574
|
+
"""
|
|
575
|
+
par = ObjCrystBondAngleParameter(
|
|
576
|
+
name, atom1, atom2, atom3, value, const
|
|
577
|
+
)
|
|
578
|
+
self.addParameter(par)
|
|
579
|
+
|
|
580
|
+
return par
|
|
581
|
+
|
|
582
|
+
def addDihedralAngleParameter(
|
|
583
|
+
self, name, atom1, atom2, atom3, atom4, value=None, const=False
|
|
584
|
+
):
|
|
585
|
+
"""Add a dihedral angle to the Molecule.
|
|
586
|
+
|
|
587
|
+
This creates a ObjCrystDihedralAngleParameter to the
|
|
588
|
+
ObjCrystMoleculeParSet that can be adjusted during the fit.
|
|
589
|
+
|
|
590
|
+
name -- The name of the ObjCrystDihedralAngleParameter.
|
|
591
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the dihderal
|
|
592
|
+
angle.
|
|
593
|
+
atom2 -- The second (central) atom (ObjCrystMolAtomParSet) in the
|
|
594
|
+
dihderal angle
|
|
595
|
+
atom3 -- The third (central) atom (ObjCrystMolAtomParSet) in the
|
|
596
|
+
dihderal angle
|
|
597
|
+
atom4 -- The fourth (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
598
|
+
dihderal angle
|
|
599
|
+
value -- An initial value for the dihedral angle. If this is None
|
|
600
|
+
(default), then the current dihedral angle between atoms
|
|
601
|
+
will be used.
|
|
602
|
+
const -- A flag indicating whether the Parameter is constant
|
|
603
|
+
(default False).
|
|
604
|
+
|
|
605
|
+
Returns the new ObjCrystDihedralAngleParameter.
|
|
606
|
+
"""
|
|
607
|
+
par = ObjCrystDihedralAngleParameter(
|
|
608
|
+
name, atom1, atom2, atom3, atom4, value, const
|
|
609
|
+
)
|
|
610
|
+
self.addParameter(par)
|
|
611
|
+
|
|
612
|
+
return par
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
# End class ObjCrystMoleculeParSet
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class ObjCrystMolAtomParSet(ObjCrystScattererParSet):
|
|
619
|
+
"""A adaptor for an pyobjcryst.molecule.MolAtom.
|
|
620
|
+
|
|
621
|
+
This class derives from srfit.fitbase.parameterset.ParameterSet. Note that
|
|
622
|
+
MolAtom does not derive from Scatterer, but the relevant interface is the
|
|
623
|
+
same within pyobjcryst. See the ParameterSet class for base attributes.
|
|
624
|
+
|
|
625
|
+
Attributes:
|
|
626
|
+
scat -- The adapted pyobjcryst.molecule.MolAtom.
|
|
627
|
+
parent -- The ObjCrystCrystalParSet this belongs to
|
|
628
|
+
element -- Non-refinable name of the element (property).
|
|
629
|
+
|
|
630
|
+
Managed Parameters:
|
|
631
|
+
x, y, z -- Atom position in crystal coordinates (ParameterAdapter)
|
|
632
|
+
occ -- Occupancy of the atom on its crystal location
|
|
633
|
+
(ParameterAdapter)
|
|
634
|
+
Biso -- Isotropic scattering factor (ParameterAdapter). This does
|
|
635
|
+
not exist for dummy atoms. See the 'isDummy' method.
|
|
636
|
+
B11, B22, B33, B12, B21, B23, B32, B13, B31
|
|
637
|
+
-- Anisotropic displacement factor for scatterer
|
|
638
|
+
(ParameterAdapter or ParameterProxy). Note that the Bij and Bji
|
|
639
|
+
parameters are the same.
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
def __init__(self, name, scat, parent):
|
|
643
|
+
"""Initialize.
|
|
644
|
+
|
|
645
|
+
name -- The name of the scatterer
|
|
646
|
+
scat -- The Scatterer instance
|
|
647
|
+
parent -- The ObjCrystCrystalParSet this belongs to
|
|
648
|
+
"""
|
|
649
|
+
ObjCrystScattererParSet.__init__(self, name, scat, parent)
|
|
650
|
+
sp = scat.GetScatteringPower()
|
|
651
|
+
|
|
652
|
+
# Only wrap this if there is a scattering power
|
|
653
|
+
if sp is not None:
|
|
654
|
+
self.addParameter(ParameterAdapter("Biso", sp, attr="Biso"))
|
|
655
|
+
self.addParameter(ParameterAdapter("B11", sp, attr="B11"))
|
|
656
|
+
self.addParameter(ParameterAdapter("B22", sp, attr="B22"))
|
|
657
|
+
self.addParameter(ParameterAdapter("B33", sp, attr="B33"))
|
|
658
|
+
B12 = ParameterAdapter("B12", sp, attr="B12")
|
|
659
|
+
B21 = ParameterProxy("B21", B12)
|
|
660
|
+
B13 = ParameterAdapter("B13", sp, attr="B13")
|
|
661
|
+
B31 = ParameterProxy("B31", B13)
|
|
662
|
+
B23 = ParameterAdapter("B23", sp, attr="B23")
|
|
663
|
+
B32 = ParameterProxy("B32", B23)
|
|
664
|
+
self.addParameter(B12)
|
|
665
|
+
self.addParameter(B21)
|
|
666
|
+
self.addParameter(B13)
|
|
667
|
+
self.addParameter(B31)
|
|
668
|
+
self.addParameter(B23)
|
|
669
|
+
self.addParameter(B32)
|
|
670
|
+
|
|
671
|
+
return
|
|
672
|
+
|
|
673
|
+
def _getElem(self):
|
|
674
|
+
"""Getter for the element type."""
|
|
675
|
+
sp = self.scat.GetScatteringPower()
|
|
676
|
+
if sp:
|
|
677
|
+
return sp.GetSymbol()
|
|
678
|
+
else:
|
|
679
|
+
return "dummy"
|
|
680
|
+
|
|
681
|
+
element = property(_getElem)
|
|
682
|
+
|
|
683
|
+
def isDummy(self):
|
|
684
|
+
"""Indicate whether this atom is a dummy atom."""
|
|
685
|
+
return self.scat.IsDummy()
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
# End class ObjCrystMolAtomParSet
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
class ObjCrystMoleculeRestraint(object):
|
|
692
|
+
"""Base class for adapting pyobjcryst Molecule restraints to srfit.
|
|
693
|
+
|
|
694
|
+
The 'penalty' method calls 'GetLogLikelihood' of the pyobjcryst restraint.
|
|
695
|
+
This implements the 'penalty' method from
|
|
696
|
+
diffpy.srfit.fitbase.restraint.Restraint. The 'restrain' method is not
|
|
697
|
+
needed or implemented.
|
|
698
|
+
|
|
699
|
+
Attributes:
|
|
700
|
+
res -- The pyobjcryst Molecule restraint.
|
|
701
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied) by
|
|
702
|
+
the unrestrained point-average chi^2 (chi^2/numpoints) (default
|
|
703
|
+
False).
|
|
704
|
+
"""
|
|
705
|
+
|
|
706
|
+
def __init__(self, res, scaled=False):
|
|
707
|
+
"""Create a Restraint-like from a pyobjcryst Molecule restraint.
|
|
708
|
+
|
|
709
|
+
res -- The pyobjcryst Molecule restraint.
|
|
710
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
711
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
712
|
+
(default False).
|
|
713
|
+
"""
|
|
714
|
+
self.res = res
|
|
715
|
+
self.scaled = scaled
|
|
716
|
+
return
|
|
717
|
+
|
|
718
|
+
def penalty(self, w=1.0):
|
|
719
|
+
"""Calculate the penalty of the restraint.
|
|
720
|
+
|
|
721
|
+
w -- The point-average chi^2 which is optionally used to scale the
|
|
722
|
+
penalty (default 1.0).
|
|
723
|
+
"""
|
|
724
|
+
penalty = self.res.GetLogLikelihood()
|
|
725
|
+
if self.scaled:
|
|
726
|
+
penalty *= w
|
|
727
|
+
return penalty
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
# End class ObjCrystMoleculeRestraint
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
class ObjCrystBondLengthRestraint(ObjCrystMoleculeRestraint):
|
|
734
|
+
"""Restrain the distance between two atoms.
|
|
735
|
+
|
|
736
|
+
Attributes:
|
|
737
|
+
atom1 -- The first atom in the bond (ObjCrystMolAtomParSet)
|
|
738
|
+
atom2 -- The second atom in the bond (ObjCrystMolAtomParSet)
|
|
739
|
+
length -- The length of the bond (Angstroms)
|
|
740
|
+
sigma -- The uncertainty of the bond length (Angstroms)
|
|
741
|
+
delta -- The width of the bond (Angstroms)
|
|
742
|
+
res -- The pyobjcryst BondLength restraint
|
|
743
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied) by
|
|
744
|
+
the unrestrained point-average chi^2 (chi^2/numpoints) (default
|
|
745
|
+
False)
|
|
746
|
+
"""
|
|
747
|
+
|
|
748
|
+
def __init__(self, atom1, atom2, length, sigma, delta, scaled=False):
|
|
749
|
+
"""Create a bond length restraint.
|
|
750
|
+
|
|
751
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the bond
|
|
752
|
+
atom2 -- Second atom (ObjCrystMolAtomParSet) in the bond
|
|
753
|
+
length -- The length of the bond (Angstroms)
|
|
754
|
+
sigma -- The uncertainty of the bond length (Angstroms)
|
|
755
|
+
delta -- The width of the bond (Angstroms)
|
|
756
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
757
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
758
|
+
(default False)
|
|
759
|
+
"""
|
|
760
|
+
self.atom1 = atom1
|
|
761
|
+
self.atom2 = atom2
|
|
762
|
+
|
|
763
|
+
m = self.atom1.scat.GetMolecule()
|
|
764
|
+
res = m.AddBond(atom1.scat, atom2.scat, length, sigma, delta)
|
|
765
|
+
|
|
766
|
+
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
|
|
767
|
+
return
|
|
768
|
+
|
|
769
|
+
# Give access to the parameters of the restraint
|
|
770
|
+
length = property(
|
|
771
|
+
lambda self: self.res.GetLength0(),
|
|
772
|
+
lambda self, val: self.res.SetLength0(val),
|
|
773
|
+
)
|
|
774
|
+
sigma = property(
|
|
775
|
+
lambda self: self.res.GetLengthSigma(),
|
|
776
|
+
lambda self, val: self.res.SetLengthSigma(val),
|
|
777
|
+
)
|
|
778
|
+
delta = property(
|
|
779
|
+
lambda self: self.res.GetLengthDelta(),
|
|
780
|
+
lambda self, val: self.res.SetLengthDelta(val),
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
# End class ObjCrystBondLengthRestraint
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
class ObjCrystBondAngleRestraint(ObjCrystMoleculeRestraint):
|
|
788
|
+
"""Restrain the angle defined by three atoms.
|
|
789
|
+
|
|
790
|
+
Attributes:
|
|
791
|
+
atom1 -- The first atom in the angle (ObjCrystMolAtomParSet)
|
|
792
|
+
atom2 -- The second atom in the angle (ObjCrystMolAtomParSet)
|
|
793
|
+
atom3 -- The third atom in the angle (ObjCrystMolAtomParSet)
|
|
794
|
+
angle -- The bond angle (radians)
|
|
795
|
+
sigma -- The uncertainty of the bond angle (radians)
|
|
796
|
+
delta -- The width of the bond angle (radians)
|
|
797
|
+
res -- The pyobjcryst BondAngle restraint
|
|
798
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied) by
|
|
799
|
+
the unrestrained point-average chi^2 (chi^2/numpoints) (default
|
|
800
|
+
False)
|
|
801
|
+
"""
|
|
802
|
+
|
|
803
|
+
def __init__(self, atom1, atom2, atom3, angle, sigma, delta, scaled=False):
|
|
804
|
+
"""Create a bond angle restraint.
|
|
805
|
+
|
|
806
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the bond angle
|
|
807
|
+
atom2 -- Second (central) atom (ObjCrystMolAtomParSet) in the bond
|
|
808
|
+
angle
|
|
809
|
+
atom3 -- Third atom (ObjCrystMolAtomParSet) in the bond angle
|
|
810
|
+
angle -- The bond angle (radians)
|
|
811
|
+
sigma -- The uncertainty of the bond angle (radians)
|
|
812
|
+
delta -- The width of the bond angle (radians)
|
|
813
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
814
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
815
|
+
(default False).
|
|
816
|
+
"""
|
|
817
|
+
self.atom1 = atom1
|
|
818
|
+
self.atom2 = atom2
|
|
819
|
+
self.atom3 = atom3
|
|
820
|
+
|
|
821
|
+
m = self.atom1.scat.GetMolecule()
|
|
822
|
+
res = m.AddBondAngle(
|
|
823
|
+
atom1.scat, atom2.scat, atom3.scat, angle, sigma, delta
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
|
|
827
|
+
return
|
|
828
|
+
|
|
829
|
+
# Give access to the parameters of the restraint
|
|
830
|
+
angle = property(
|
|
831
|
+
lambda self: self.res.GetAngle0(),
|
|
832
|
+
lambda self, val: self.res.SetAngle0(val),
|
|
833
|
+
)
|
|
834
|
+
sigma = property(
|
|
835
|
+
lambda self: self.res.GetAngleSigma(),
|
|
836
|
+
lambda self, val: self.res.SetAngleSigma(val),
|
|
837
|
+
)
|
|
838
|
+
delta = property(
|
|
839
|
+
lambda self: self.res.GetAngleDelta(),
|
|
840
|
+
lambda self, val: self.res.SetAngleDelta(val),
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
# End class ObjCrystBondAngleRestraint
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
class ObjCrystDihedralAngleRestraint(ObjCrystMoleculeRestraint):
|
|
848
|
+
"""Restrain the dihedral (torsion) angle defined by four atoms.
|
|
849
|
+
|
|
850
|
+
Attributes:
|
|
851
|
+
atom1 -- The first atom in the angle (ObjCrystMolAtomParSet)
|
|
852
|
+
atom2 -- The second (central) atom in the angle (ObjCrystMolAtomParSet)
|
|
853
|
+
atom3 -- The third (central) atom in the angle (ObjCrystMolAtomParSet)
|
|
854
|
+
atom4 -- The fourth atom in the angle (ObjCrystMolAtomParSet)
|
|
855
|
+
angle -- The dihedral angle (radians)
|
|
856
|
+
sigma -- The uncertainty of the dihedral angle (radians)
|
|
857
|
+
delta -- The width of the dihedral angle (radians)
|
|
858
|
+
res -- The pyobjcryst DihedralAngle restraint
|
|
859
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied) by
|
|
860
|
+
the unrestrained point-average chi^2 (chi^2/numpoints) (default
|
|
861
|
+
False)
|
|
862
|
+
"""
|
|
863
|
+
|
|
864
|
+
def __init__(
|
|
865
|
+
self, atom1, atom2, atom3, atom4, angle, sigma, delta, scaled=False
|
|
866
|
+
):
|
|
867
|
+
"""Create a dihedral angle restraint.
|
|
868
|
+
|
|
869
|
+
atom1 -- First atom (ObjCrystMolAtomParSet) in the angle
|
|
870
|
+
atom2 -- Second (central) atom (ObjCrystMolAtomParSet) in the angle
|
|
871
|
+
atom3 -- Third (central) atom (ObjCrystMolAtomParSet) in the angle
|
|
872
|
+
atom4 -- Fourth atom in the angle (ObjCrystMolAtomParSet)
|
|
873
|
+
angle -- The dihedral angle (radians)
|
|
874
|
+
sigma -- The uncertainty of the dihedral angle (radians)
|
|
875
|
+
delta -- The width of the dihedral angle (radians)
|
|
876
|
+
scaled -- A flag indicating if the restraint is scaled (multiplied)
|
|
877
|
+
by the unrestrained point-average chi^2 (chi^2/numpoints)
|
|
878
|
+
(default False).
|
|
879
|
+
"""
|
|
880
|
+
self.atom1 = atom1
|
|
881
|
+
self.atom2 = atom2
|
|
882
|
+
self.atom3 = atom3
|
|
883
|
+
self.atom4 = atom4
|
|
884
|
+
|
|
885
|
+
m = self.atom1.scat.GetMolecule()
|
|
886
|
+
res = m.AddDihedralAngle(
|
|
887
|
+
atom1.scat, atom2.scat, atom3.scat, atom4.scat, angle, sigma, delta
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
ObjCrystMoleculeRestraint.__init__(self, res, scaled)
|
|
891
|
+
return
|
|
892
|
+
|
|
893
|
+
# Give access to the parameters of the restraint
|
|
894
|
+
angle = property(
|
|
895
|
+
lambda self: self.res.GetAngle0(),
|
|
896
|
+
lambda self, val: self.res.SetAngle0(val),
|
|
897
|
+
)
|
|
898
|
+
sigma = property(
|
|
899
|
+
lambda self: self.res.GetAngleSigma(),
|
|
900
|
+
lambda self, val: self.res.SetAngleSigma(val),
|
|
901
|
+
)
|
|
902
|
+
delta = property(
|
|
903
|
+
lambda self: self.res.GetAngleDelta(),
|
|
904
|
+
lambda self, val: self.res.SetAngleDelta(val),
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
# End class ObjCrystDihedralAngleRestraint
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
class StretchModeParameter(Parameter):
|
|
912
|
+
"""Partial Parameter class encapsulating pyobjcryst stretch modes.
|
|
913
|
+
|
|
914
|
+
This class relies upon attributes that do not belong to it. Do not
|
|
915
|
+
instantiate this class.
|
|
916
|
+
|
|
917
|
+
Required attributes:
|
|
918
|
+
matoms -- The set of all mutated AtomParSets
|
|
919
|
+
molecule -- The ObjCrystMoleculeParSet the atoms belong to
|
|
920
|
+
mode -- The pyobjcryst.molecule.StretchMode used to change atomic
|
|
921
|
+
positions.
|
|
922
|
+
keepcenter -- Flag indicating whether to keep the center of mass of the
|
|
923
|
+
molecule stationary within the crystal when changing the
|
|
924
|
+
value of the parameter (bool, default True).
|
|
925
|
+
"""
|
|
926
|
+
|
|
927
|
+
def __init__(self, name, value=None, const=False):
|
|
928
|
+
"""Initialization.
|
|
929
|
+
|
|
930
|
+
name -- The name of this Parameter (must be a valid attribute
|
|
931
|
+
identifier)
|
|
932
|
+
value -- The initial value of this Parameter (default 0).
|
|
933
|
+
const -- A flag inticating whether the Parameter is a constant (like
|
|
934
|
+
pi).
|
|
935
|
+
|
|
936
|
+
Raises ValueError if the name is not a valid attribute identifier
|
|
937
|
+
"""
|
|
938
|
+
Parameter.__init__(self, name, value, const)
|
|
939
|
+
self.keepcenter = True
|
|
940
|
+
|
|
941
|
+
def setValue(self, val):
|
|
942
|
+
"""Change the value of the Parameter."""
|
|
943
|
+
curval = self.getValue()
|
|
944
|
+
val = float(val)
|
|
945
|
+
|
|
946
|
+
if val == curval:
|
|
947
|
+
return self
|
|
948
|
+
|
|
949
|
+
# The StretchMode expects the change in mutated value.
|
|
950
|
+
delta = val - curval
|
|
951
|
+
self.mode.Stretch(delta, self.keepcenter)
|
|
952
|
+
|
|
953
|
+
# Let Parameter take care of the general details
|
|
954
|
+
Parameter.setValue(self, val)
|
|
955
|
+
|
|
956
|
+
return self
|
|
957
|
+
|
|
958
|
+
def addAtoms(self, atomlist):
|
|
959
|
+
"""Associate ObjCrystMolAtomParSets with the Parameter.
|
|
960
|
+
|
|
961
|
+
This will associate additional ObjCrystMolAtomParSets with the
|
|
962
|
+
Parameter. These will be mutated in the exact same way as the
|
|
963
|
+
primary mutated ObjCrystMolAtomParSet. This is useful when a
|
|
964
|
+
group of atoms should move rigidly in response to a change in a
|
|
965
|
+
bond property.
|
|
966
|
+
"""
|
|
967
|
+
if not hasattr(atomlist, "__iter__"):
|
|
968
|
+
atomlist = [atomlist]
|
|
969
|
+
# Record the added atoms in the Parameter
|
|
970
|
+
self.matoms.update(atomlist)
|
|
971
|
+
# Make sure we're observing these atoms
|
|
972
|
+
for a in atomlist:
|
|
973
|
+
a.x.addObserver(self._flush)
|
|
974
|
+
a.y.addObserver(self._flush)
|
|
975
|
+
a.z.addObserver(self._flush)
|
|
976
|
+
|
|
977
|
+
# Record the added atoms in the StretchMode
|
|
978
|
+
scatlist = [a.scat for a in atomlist]
|
|
979
|
+
self.mode.AddAtoms(scatlist)
|
|
980
|
+
return self
|
|
981
|
+
|
|
982
|
+
def notify(self, other=()):
|
|
983
|
+
"""Notify all mutated Parameters and observers.
|
|
984
|
+
|
|
985
|
+
Some of the mutated parameters will be observing us. At the same
|
|
986
|
+
time we need to observe them. Observable won't let us do both,
|
|
987
|
+
so we notify the Parameters that we mutate directly.
|
|
988
|
+
"""
|
|
989
|
+
noneother = ()
|
|
990
|
+
# Notify the atoms that have moved
|
|
991
|
+
for a in self.matoms:
|
|
992
|
+
a.x._flush(noneother)
|
|
993
|
+
a.y._flush(noneother)
|
|
994
|
+
a.z._flush(noneother)
|
|
995
|
+
# Notify the molecule position
|
|
996
|
+
self.molecule.x._flush(noneother)
|
|
997
|
+
self.molecule.y._flush(noneother)
|
|
998
|
+
self.molecule.z._flush(noneother)
|
|
999
|
+
|
|
1000
|
+
# Notify observers
|
|
1001
|
+
Parameter.notify(self, other)
|
|
1002
|
+
return
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
# End class StretchModeParameter
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
class ObjCrystBondLengthParameter(StretchModeParameter):
|
|
1009
|
+
"""Class for abstracting a bond length in a Molecule to a Parameter.
|
|
1010
|
+
|
|
1011
|
+
This wraps up a pyobjcryst.molecule.StretchModeBondLength object so that
|
|
1012
|
+
the distance between two MolAtoms in a Molecule can be used as an
|
|
1013
|
+
adjustable Parameter. When a bond length is adjusted, the second MolAtom is
|
|
1014
|
+
moved, and the absolute position of the Molecule is altered to preserve the
|
|
1015
|
+
location of the center of mass within the Crystal. Thus, the x, y and z
|
|
1016
|
+
Parameters of the MolAtom and its parent Molecule are altered. This can be
|
|
1017
|
+
changed by setting the 'keepcenter' attribute of the parameter to False.
|
|
1018
|
+
|
|
1019
|
+
This Parameter makes it possible to mutate a MolAtom multiple times in a
|
|
1020
|
+
single refinement step. If these mutations are not orthogonal, then this
|
|
1021
|
+
could lead to nonconvergence of a fit, depending on the optimizer. Consider
|
|
1022
|
+
mutating atom2 of a bond directly, and via a ObjCrystBondLengthParameter.
|
|
1023
|
+
The two mutations of atom2 may be determined independently by the
|
|
1024
|
+
optimizer, in which case the composed mutation will have an unexpected
|
|
1025
|
+
effect on the residual. It is best practice to either modify MolAtom
|
|
1026
|
+
positions directly, or thorough BondLengthParameters, BondAngleParameters
|
|
1027
|
+
and DihedralAngleParameters (which are mutually orthogonal).
|
|
1028
|
+
|
|
1029
|
+
Note that by making a ObjCrystBondLengthParameter constant it also makes
|
|
1030
|
+
the underlying ObjCrystMolAtomParSets constant. When setting it as
|
|
1031
|
+
nonconstant, each ObjCrystMolAtomParSet is set nonconstant. Changing the
|
|
1032
|
+
bond length changes the position of the second MolAtom and Molecule, even
|
|
1033
|
+
if either is set as constant.
|
|
1034
|
+
|
|
1035
|
+
Attributes:
|
|
1036
|
+
atom1 -- The first ObjCrystMolAtomParSet in the bond
|
|
1037
|
+
atom2 -- The second (mutated) ObjCrystMolAtomParSet in the bond
|
|
1038
|
+
matoms -- The set of all mutated ObjCrystMolAtomParSets
|
|
1039
|
+
molecule -- The ObjCrystMoleculeParSet the ObjCrystMolAtomParSets
|
|
1040
|
+
belong to
|
|
1041
|
+
mode -- The pyobjcryst.molecule.StretchModeBondLength for the bond
|
|
1042
|
+
|
|
1043
|
+
Inherited Attributes
|
|
1044
|
+
name -- A name for this Parameter.
|
|
1045
|
+
const -- A flag indicating whether this is considered a constant.
|
|
1046
|
+
_value -- The value of the Parameter. Modified with 'setValue'.
|
|
1047
|
+
value -- Property for 'getValue' and 'setValue'.
|
|
1048
|
+
constraint -- A callable that calculates the value of this Parameter. If
|
|
1049
|
+
this is None (None), the the Parameter is responsible for its
|
|
1050
|
+
own value. The callable takes no arguments.
|
|
1051
|
+
bounds -- A 2-list defining the bounds on the Parameter. This can be
|
|
1052
|
+
used by some optimizers when the Parameter is varied.
|
|
1053
|
+
"""
|
|
1054
|
+
|
|
1055
|
+
def __init__(self, name, atom1, atom2, value=None, const=False, mode=None):
|
|
1056
|
+
"""Create a ObjCrystBondLengthParameter.
|
|
1057
|
+
|
|
1058
|
+
name -- The name of the ObjCrystBondLengthParameter
|
|
1059
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the bond
|
|
1060
|
+
atom2 -- The second (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
1061
|
+
bond
|
|
1062
|
+
value -- An initial value for the bond length. If this is None
|
|
1063
|
+
(default), then the current distance between the atoms will
|
|
1064
|
+
be used.
|
|
1065
|
+
const -- A flag indicating whether the Parameter is constant
|
|
1066
|
+
(default False)
|
|
1067
|
+
mode -- An extant pyobjcryst.molecule.StretchModeBondLength to use.
|
|
1068
|
+
If this is None (default), then a new StretchModeBondLength
|
|
1069
|
+
will be built.
|
|
1070
|
+
"""
|
|
1071
|
+
|
|
1072
|
+
# Create the mode
|
|
1073
|
+
self.mode = mode
|
|
1074
|
+
if mode is None:
|
|
1075
|
+
self.mode = StretchModeBondLength(atom1.scat, atom2.scat, None)
|
|
1076
|
+
# We only add the last atom. This is the one that will move
|
|
1077
|
+
self.mode.AddAtom(atom2.scat)
|
|
1078
|
+
self.matoms = set([atom2])
|
|
1079
|
+
|
|
1080
|
+
# Observe the atom positions
|
|
1081
|
+
for a in [atom1, atom2]:
|
|
1082
|
+
a.x.addObserver(self._flush)
|
|
1083
|
+
a.y.addObserver(self._flush)
|
|
1084
|
+
a.z.addObserver(self._flush)
|
|
1085
|
+
|
|
1086
|
+
self.atom1 = atom1
|
|
1087
|
+
self.atom2 = atom2
|
|
1088
|
+
self.molecule = atom1.parent
|
|
1089
|
+
|
|
1090
|
+
# We do this last so the atoms are defined before we set any values.
|
|
1091
|
+
if value is None:
|
|
1092
|
+
value = GetBondLength(atom1.scat, atom2.scat)
|
|
1093
|
+
StretchModeParameter.__init__(self, name, value, const)
|
|
1094
|
+
self.setConst(const)
|
|
1095
|
+
|
|
1096
|
+
return
|
|
1097
|
+
|
|
1098
|
+
def setConst(self, const=True, value=None):
|
|
1099
|
+
"""Toggle the Parameter as constant.
|
|
1100
|
+
|
|
1101
|
+
This sets the underlying ObjCrystMolAtomParSet positions const as well.
|
|
1102
|
+
|
|
1103
|
+
const -- Flag indicating if the Parameter is constant (default
|
|
1104
|
+
True).
|
|
1105
|
+
value -- An optional value for the Parameter (default None). If this
|
|
1106
|
+
is not None, then the Parameter will get a new value,
|
|
1107
|
+
constant or otherwise.
|
|
1108
|
+
"""
|
|
1109
|
+
StretchModeParameter.setConst(self, const, value)
|
|
1110
|
+
|
|
1111
|
+
for a in [self.atom1, self.atom2]:
|
|
1112
|
+
a.x.setConst(const)
|
|
1113
|
+
a.y.setConst(const)
|
|
1114
|
+
a.z.setConst(const)
|
|
1115
|
+
return self
|
|
1116
|
+
|
|
1117
|
+
def getValue(self):
|
|
1118
|
+
"""This calculates the value if it might have been changed.
|
|
1119
|
+
|
|
1120
|
+
There is no guarantee that the ObjCrystMolAtomParSets underlying
|
|
1121
|
+
the bond won't change, so the bond length is calculated if
|
|
1122
|
+
necessary each time this is called.
|
|
1123
|
+
"""
|
|
1124
|
+
if self._value is None:
|
|
1125
|
+
val = GetBondLength(self.atom1.scat, self.atom2.scat)
|
|
1126
|
+
Parameter.setValue(self, val)
|
|
1127
|
+
|
|
1128
|
+
return self._value
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
# End class ObjCrystBondLengthParameter
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
class ObjCrystBondAngleParameter(StretchModeParameter):
|
|
1135
|
+
"""Class for abstracting a bond angle in a Molecule to a Parameter.
|
|
1136
|
+
|
|
1137
|
+
This wraps up a pyobjcryst.molecule.StretchModeBondAngle object so that the
|
|
1138
|
+
angle defined by three MolAtoms in a Molecule can be used as an adjustable
|
|
1139
|
+
Parameter. When a bond angle is adjusted, the third MolAtom is moved, and
|
|
1140
|
+
the absolute position of the Molecule is altered to preserve the location
|
|
1141
|
+
of the center of mass within the crystal. This can be changed by setting
|
|
1142
|
+
the 'keepcenter' attribute of the parameter to False.
|
|
1143
|
+
|
|
1144
|
+
See precautions in the ObjCrystBondLengthParameter class.
|
|
1145
|
+
|
|
1146
|
+
Attributes
|
|
1147
|
+
atom1 -- The first ObjCrystAtomParSet in the bond angle
|
|
1148
|
+
atom2 -- The second (central) ObjCrystMolAtomParSet in the bond angle
|
|
1149
|
+
atom3 -- The third (mutated) ObjCrystMolAtomParSet in the bond angle
|
|
1150
|
+
matoms -- The set of all mutated ObjCrystMolAtomParSets
|
|
1151
|
+
molecule -- The ObjCrystMoleculeParSet the ObjCrystMolAtomParSets
|
|
1152
|
+
belong to
|
|
1153
|
+
mode -- The pyobjcryst.molecule.StretchModeBondAngle for the bond angle
|
|
1154
|
+
|
|
1155
|
+
Inherited Attributes
|
|
1156
|
+
name -- A name for this Parameter.
|
|
1157
|
+
const -- A flag indicating whether this is considered a constant.
|
|
1158
|
+
_value -- The value of the Parameter. Modified with 'setValue'.
|
|
1159
|
+
value -- Property for 'getValue' and 'setValue'.
|
|
1160
|
+
constraint -- A callable that calculates the value of this Parameter. If
|
|
1161
|
+
this is None (None), the the Parameter is responsible for its
|
|
1162
|
+
own value. The callable takes no arguments.
|
|
1163
|
+
bounds -- A 2-list defining the bounds on the Parameter. This can be
|
|
1164
|
+
used by some optimizers when the Parameter is varied.
|
|
1165
|
+
"""
|
|
1166
|
+
|
|
1167
|
+
def __init__(
|
|
1168
|
+
self, name, atom1, atom2, atom3, value=None, const=False, mode=None
|
|
1169
|
+
):
|
|
1170
|
+
"""Create a ObjCrystBondAngleParameter.
|
|
1171
|
+
|
|
1172
|
+
name -- The name of the ObjCrystBondAngleParameter.
|
|
1173
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the bond angle
|
|
1174
|
+
atom2 -- The second (central) atom (ObjCrystMolAtomParSet) in the
|
|
1175
|
+
bond angle
|
|
1176
|
+
atom3 -- The third (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
1177
|
+
bond angle
|
|
1178
|
+
value -- An initial value for the bond length. If this is None
|
|
1179
|
+
(default), then the current bond angle between the atoms
|
|
1180
|
+
will be used.
|
|
1181
|
+
const -- A flag indicating whether the Parameter is constant
|
|
1182
|
+
(default False).
|
|
1183
|
+
mode -- A pre-built mode to place in this Parameter. If this is
|
|
1184
|
+
None (default), then a StretchMode will be built.
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
# Create the stretch mode
|
|
1188
|
+
self.mode = mode
|
|
1189
|
+
if mode is None:
|
|
1190
|
+
self.mode = StretchModeBondAngle(
|
|
1191
|
+
atom1.scat, atom2.scat, atom3.scat, None
|
|
1192
|
+
)
|
|
1193
|
+
# We only add the last atom. This is the one that will move
|
|
1194
|
+
self.mode.AddAtom(atom3.scat)
|
|
1195
|
+
self.matoms = set([atom3])
|
|
1196
|
+
|
|
1197
|
+
# Observe the atom positions
|
|
1198
|
+
for a in [atom1, atom2, atom3]:
|
|
1199
|
+
a.x.addObserver(self._flush)
|
|
1200
|
+
a.y.addObserver(self._flush)
|
|
1201
|
+
a.z.addObserver(self._flush)
|
|
1202
|
+
|
|
1203
|
+
self.atom1 = atom1
|
|
1204
|
+
self.atom2 = atom2
|
|
1205
|
+
self.atom3 = atom3
|
|
1206
|
+
self.molecule = atom1.parent
|
|
1207
|
+
|
|
1208
|
+
# We do this last so the atoms are defined before we set any values.
|
|
1209
|
+
if value is None:
|
|
1210
|
+
value = GetBondAngle(atom1.scat, atom2.scat, atom3.scat)
|
|
1211
|
+
StretchModeParameter.__init__(self, name, value, const)
|
|
1212
|
+
self.setConst(const)
|
|
1213
|
+
|
|
1214
|
+
return
|
|
1215
|
+
|
|
1216
|
+
def setConst(self, const=True, value=None):
|
|
1217
|
+
"""Toggle the Parameter as constant.
|
|
1218
|
+
|
|
1219
|
+
This sets the underlying ObjCrystMolAtomParSet positions const as well.
|
|
1220
|
+
|
|
1221
|
+
const -- Flag indicating if the Parameter is constant (default
|
|
1222
|
+
True).
|
|
1223
|
+
value -- An optional value for the Parameter (default None). If this
|
|
1224
|
+
is not None, then the Parameter will get a new value,
|
|
1225
|
+
constant or otherwise.
|
|
1226
|
+
"""
|
|
1227
|
+
StretchModeParameter.setConst(self, const, value)
|
|
1228
|
+
for a in [self.atom1, self.atom2, self.atom3]:
|
|
1229
|
+
a.x.setConst(const)
|
|
1230
|
+
a.y.setConst(const)
|
|
1231
|
+
a.z.setConst(const)
|
|
1232
|
+
return self
|
|
1233
|
+
|
|
1234
|
+
def getValue(self):
|
|
1235
|
+
"""This calculates the value if it might have been changed.
|
|
1236
|
+
|
|
1237
|
+
There is no guarantee that the MolAtoms underlying the bond
|
|
1238
|
+
angle won't change, so the bond angle is calculated if necessary
|
|
1239
|
+
each time this is called.
|
|
1240
|
+
"""
|
|
1241
|
+
if self._value is None:
|
|
1242
|
+
val = GetBondAngle(
|
|
1243
|
+
self.atom1.scat, self.atom2.scat, self.atom3.scat
|
|
1244
|
+
)
|
|
1245
|
+
Parameter.setValue(self, val)
|
|
1246
|
+
|
|
1247
|
+
return self._value
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
# End class ObjCrystBondAngleParameter
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
class ObjCrystDihedralAngleParameter(StretchModeParameter):
|
|
1254
|
+
"""Class for abstracting a dihedral angle in a Molecule to a Parameter.
|
|
1255
|
+
|
|
1256
|
+
This wraps up a pyobjcryst.molecule.StretchModeTorsion object so that the
|
|
1257
|
+
angle defined by four MolAtoms ([a1-a2].[a3-a4]) in a Molecule can be used
|
|
1258
|
+
as an adjustable parameter. When a dihedral angle is adjusted, the fourth
|
|
1259
|
+
MolAtom is moved, and the absolute position of the Molecule is altered to
|
|
1260
|
+
preserve the location of the center of mass within the crystal. This can
|
|
1261
|
+
be changed by setting the 'keepcenter' attribute of the parameter to False.
|
|
1262
|
+
|
|
1263
|
+
See precautions in the ObjCrystBondLengthParameter class.
|
|
1264
|
+
|
|
1265
|
+
Attributes
|
|
1266
|
+
atom1 -- The first ObjCrystMolAtomParSet in the dihedral angle
|
|
1267
|
+
atom2 -- The second (central) ObjCrystMolAtomParSet in the dihedral
|
|
1268
|
+
angle
|
|
1269
|
+
atom3 -- The third (central) ObjCrystMolAtomParSet in the dihedral angle
|
|
1270
|
+
atom4 -- The fourth (mutated) ObjCrystMolAtomParSet in the dihedral
|
|
1271
|
+
angle
|
|
1272
|
+
matoms -- The set of all mutated ObjCrystMolAtomParSets
|
|
1273
|
+
molecule -- The ObjCrystMoleculeParSet the atoms belong to
|
|
1274
|
+
mode -- The pyobjcryst.molecule.StretchModeTorsion for the dihedral
|
|
1275
|
+
angle
|
|
1276
|
+
|
|
1277
|
+
Inherited Attributes
|
|
1278
|
+
name -- A name for this Parameter.
|
|
1279
|
+
const -- A flag indicating whether this is considered a constant.
|
|
1280
|
+
_value -- The value of the Parameter. Modified with 'setValue'.
|
|
1281
|
+
value -- Property for 'getValue' and 'setValue'.
|
|
1282
|
+
constraint -- A callable that calculates the value of this Parameter. If
|
|
1283
|
+
this is None (None), the the Parameter is responsible for its
|
|
1284
|
+
own value. The callable takes no arguments.
|
|
1285
|
+
bounds -- A 2-list defining the bounds on the Parameter. This can be
|
|
1286
|
+
used by some optimizers when the Parameter is varied.
|
|
1287
|
+
"""
|
|
1288
|
+
|
|
1289
|
+
def __init__(
|
|
1290
|
+
self,
|
|
1291
|
+
name,
|
|
1292
|
+
atom1,
|
|
1293
|
+
atom2,
|
|
1294
|
+
atom3,
|
|
1295
|
+
atom4,
|
|
1296
|
+
value=None,
|
|
1297
|
+
const=False,
|
|
1298
|
+
mode=None,
|
|
1299
|
+
):
|
|
1300
|
+
"""Create a ObjCrystDihedralAngleParameter.
|
|
1301
|
+
|
|
1302
|
+
name -- The name of the ObjCrystDihedralAngleParameter
|
|
1303
|
+
atom1 -- The first atom (ObjCrystMolAtomParSet) in the dihderal
|
|
1304
|
+
angle
|
|
1305
|
+
atom2 -- The second (central) atom (ObjCrystMolAtomParSet) in the
|
|
1306
|
+
dihderal angle
|
|
1307
|
+
atom3 -- The third (central) atom (ObjCrystMolAtomParSet) in the
|
|
1308
|
+
dihderal angle
|
|
1309
|
+
atom4 -- The fourth (mutated) atom (ObjCrystMolAtomParSet) in the
|
|
1310
|
+
dihderal angle
|
|
1311
|
+
value -- An initial value for the bond length. If this is None
|
|
1312
|
+
(default), then the current dihedral angle between atoms
|
|
1313
|
+
will be used.
|
|
1314
|
+
const -- A flag indicating whether the Parameter is constant
|
|
1315
|
+
(default False).
|
|
1316
|
+
mode -- A pre-built mode to place in this Parameter. If this is
|
|
1317
|
+
None (default), then a StretchMode will be built.
|
|
1318
|
+
"""
|
|
1319
|
+
|
|
1320
|
+
# Create the stretch mode
|
|
1321
|
+
self.mode = mode
|
|
1322
|
+
if mode is None:
|
|
1323
|
+
self.mode = StretchModeTorsion(atom2.scat, atom3.scat, None)
|
|
1324
|
+
# We only add the last atom. This is the one that will move
|
|
1325
|
+
self.mode.AddAtom(atom4.scat)
|
|
1326
|
+
self.matoms = set([atom4])
|
|
1327
|
+
|
|
1328
|
+
# Observe the atom positions
|
|
1329
|
+
for a in [atom1, atom2, atom3, atom4]:
|
|
1330
|
+
a.x.addObserver(self._flush)
|
|
1331
|
+
a.y.addObserver(self._flush)
|
|
1332
|
+
a.z.addObserver(self._flush)
|
|
1333
|
+
|
|
1334
|
+
self.atom1 = atom1
|
|
1335
|
+
self.atom2 = atom2
|
|
1336
|
+
self.atom3 = atom3
|
|
1337
|
+
self.atom4 = atom4
|
|
1338
|
+
self.molecule = atom1.parent
|
|
1339
|
+
|
|
1340
|
+
# We do this last so the atoms are defined before we set any values.
|
|
1341
|
+
if value is None:
|
|
1342
|
+
value = GetDihedralAngle(
|
|
1343
|
+
atom1.scat, atom2.scat, atom3.scat, atom4.scat
|
|
1344
|
+
)
|
|
1345
|
+
StretchModeParameter.__init__(self, name, value, const)
|
|
1346
|
+
self.setConst(const)
|
|
1347
|
+
|
|
1348
|
+
return
|
|
1349
|
+
|
|
1350
|
+
def setConst(self, const=True, value=None):
|
|
1351
|
+
"""Toggle the Parameter as constant.
|
|
1352
|
+
|
|
1353
|
+
This sets the underlying ObjCrystMolAtomParSet positions const as well.
|
|
1354
|
+
|
|
1355
|
+
const -- Flag indicating if the Parameter is constant (default
|
|
1356
|
+
True).
|
|
1357
|
+
value -- An optional value for the Parameter (default None). If this
|
|
1358
|
+
is not None, then the Parameter will get a new value,
|
|
1359
|
+
constant or otherwise.
|
|
1360
|
+
"""
|
|
1361
|
+
StretchModeParameter.setConst(self, const, value)
|
|
1362
|
+
for a in [self.atom1, self.atom2, self.atom3, self.atom4]:
|
|
1363
|
+
a.x.setConst(const)
|
|
1364
|
+
a.y.setConst(const)
|
|
1365
|
+
a.z.setConst(const)
|
|
1366
|
+
return self
|
|
1367
|
+
|
|
1368
|
+
def getValue(self):
|
|
1369
|
+
"""This calculates the value if it might have been changed.
|
|
1370
|
+
|
|
1371
|
+
There is no guarantee that the ObjCrystMolAtomParSets underlying
|
|
1372
|
+
the dihedral angle won't change from some other Parameter, so
|
|
1373
|
+
the value is recalculated each time.
|
|
1374
|
+
"""
|
|
1375
|
+
if self._value is None:
|
|
1376
|
+
val = GetDihedralAngle(
|
|
1377
|
+
self.atom1.scat,
|
|
1378
|
+
self.atom2.scat,
|
|
1379
|
+
self.atom3.scat,
|
|
1380
|
+
self.atom4.scat,
|
|
1381
|
+
)
|
|
1382
|
+
Parameter.setValue(self, val)
|
|
1383
|
+
|
|
1384
|
+
return self._value
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
# End class ObjCrystDihedralAngleParameter
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
class ObjCrystCrystalParSet(SrRealParSet):
|
|
1391
|
+
"""A adaptor for pyobjcryst.crystal.Crystal instance.
|
|
1392
|
+
|
|
1393
|
+
This class derives from diffpy.srfit.fitbase.parameterset.ParameterSet. See
|
|
1394
|
+
this class for base attributes.
|
|
1395
|
+
|
|
1396
|
+
Attributes:
|
|
1397
|
+
stru -- The adapted pyobjcryst.Crystal.
|
|
1398
|
+
scatterers -- The list of aggregated ScattererParSets (either
|
|
1399
|
+
ObjCrystAtomParSet or ObjCrystMoleculeParSet), provided for
|
|
1400
|
+
convenience.
|
|
1401
|
+
_sgpars -- A BaseSpaceGroupParameters object containing free structure
|
|
1402
|
+
Parameters. See the diffpy.srfit.structure.sgconstraints
|
|
1403
|
+
module.
|
|
1404
|
+
sgpars -- property that creates _sgpars when it is needed.
|
|
1405
|
+
angunits -- "rad", the units of angle
|
|
1406
|
+
|
|
1407
|
+
Managed Parameters:
|
|
1408
|
+
a, b, c, alpha, beta, gamma -- Lattice parameters (ParameterAdapter)
|
|
1409
|
+
|
|
1410
|
+
Managed ParameterSets:
|
|
1411
|
+
<sname> -- A ObjCrystScattererParSet (either ObjCrystAtomParSet or
|
|
1412
|
+
ObjCrystMoleculeParSet), where <sname> is the name of the
|
|
1413
|
+
adapted pyobjcryst.atom.Atom or
|
|
1414
|
+
pyobjcryst.molecule.Molecule.
|
|
1415
|
+
"""
|
|
1416
|
+
|
|
1417
|
+
def __init__(self, name, cryst):
|
|
1418
|
+
"""Initialize.
|
|
1419
|
+
|
|
1420
|
+
name -- A name for this ParameterSet
|
|
1421
|
+
cryst -- An pyobjcryst.Crystal instance.
|
|
1422
|
+
"""
|
|
1423
|
+
SrRealParSet.__init__(self, name)
|
|
1424
|
+
self.angunits = "rad"
|
|
1425
|
+
self.stru = cryst
|
|
1426
|
+
self._sgpars = None
|
|
1427
|
+
|
|
1428
|
+
self.addParameter(ParameterAdapter("a", self.stru, attr="a"))
|
|
1429
|
+
self.addParameter(ParameterAdapter("b", self.stru, attr="b"))
|
|
1430
|
+
self.addParameter(ParameterAdapter("c", self.stru, attr="c"))
|
|
1431
|
+
self.addParameter(ParameterAdapter("alpha", self.stru, attr="alpha"))
|
|
1432
|
+
self.addParameter(ParameterAdapter("beta", self.stru, attr="beta"))
|
|
1433
|
+
self.addParameter(ParameterAdapter("gamma", self.stru, attr="gamma"))
|
|
1434
|
+
|
|
1435
|
+
# Now we must loop over the scatterers and create parameter sets from
|
|
1436
|
+
# them.
|
|
1437
|
+
self.scatterers = []
|
|
1438
|
+
snames = []
|
|
1439
|
+
|
|
1440
|
+
for j in range(self.stru.GetNbScatterer()):
|
|
1441
|
+
s = self.stru.GetScatt(j)
|
|
1442
|
+
name = s.GetName()
|
|
1443
|
+
if not name:
|
|
1444
|
+
raise ValueError("Each Scatterer must have a name")
|
|
1445
|
+
if name in snames:
|
|
1446
|
+
raise ValueError("Scatterer name '%s' is duplicated" % name)
|
|
1447
|
+
|
|
1448
|
+
# Now create the proper object
|
|
1449
|
+
cname = s.GetClassName()
|
|
1450
|
+
if cname == "Atom":
|
|
1451
|
+
parset = ObjCrystAtomParSet(name, s, self)
|
|
1452
|
+
elif cname == "Molecule":
|
|
1453
|
+
parset = ObjCrystMoleculeParSet(name, s, self)
|
|
1454
|
+
else:
|
|
1455
|
+
raise TypeError("Unrecognized scatterer '%s'" % cname)
|
|
1456
|
+
|
|
1457
|
+
self.addParameterSet(parset)
|
|
1458
|
+
self.scatterers.append(parset)
|
|
1459
|
+
snames.append(name)
|
|
1460
|
+
|
|
1461
|
+
return
|
|
1462
|
+
|
|
1463
|
+
def _constrainSpaceGroup(self):
|
|
1464
|
+
"""Constrain the space group."""
|
|
1465
|
+
if self._sgpars is not None:
|
|
1466
|
+
return self._sgpars
|
|
1467
|
+
sg = self._createSpaceGroup(self.stru.GetSpaceGroup())
|
|
1468
|
+
from diffpy.srfit.structure.sgconstraints import _constrainAsSpaceGroup
|
|
1469
|
+
|
|
1470
|
+
adpsymbols = ["B11", "B22", "B33", "B12", "B13", "B23"]
|
|
1471
|
+
isosymbol = "Biso"
|
|
1472
|
+
sgoffset = [0, 0, 0]
|
|
1473
|
+
self._sgpars = _constrainAsSpaceGroup(
|
|
1474
|
+
self,
|
|
1475
|
+
sg,
|
|
1476
|
+
self.scatterers,
|
|
1477
|
+
sgoffset,
|
|
1478
|
+
adpsymbols=adpsymbols,
|
|
1479
|
+
isosymbol=isosymbol,
|
|
1480
|
+
)
|
|
1481
|
+
return self._sgpars
|
|
1482
|
+
|
|
1483
|
+
sgpars = property(_constrainSpaceGroup)
|
|
1484
|
+
|
|
1485
|
+
@staticmethod
|
|
1486
|
+
def _createSpaceGroup(sgobjcryst):
|
|
1487
|
+
"""Create a diffpy.structure SpaceGroup object from pyobjcryst.
|
|
1488
|
+
|
|
1489
|
+
sgobjcryst -- A pyobjcryst.spacegroup.SpaceGroup instance.
|
|
1490
|
+
|
|
1491
|
+
This uses the actual space group operations from the
|
|
1492
|
+
pyobjcryst.spacegroup.SpaceGroup instance so there is no ambiguity
|
|
1493
|
+
about the actual space group.
|
|
1494
|
+
"""
|
|
1495
|
+
import copy
|
|
1496
|
+
|
|
1497
|
+
from diffpy.structure.spacegroups import GetSpaceGroup, SymOp
|
|
1498
|
+
|
|
1499
|
+
name = sgobjcryst.GetName()
|
|
1500
|
+
extnstr = ":%s" % sgobjcryst.GetExtension()
|
|
1501
|
+
if name.endswith(extnstr):
|
|
1502
|
+
name = name[: -len(extnstr)]
|
|
1503
|
+
|
|
1504
|
+
# Get whatever spacegroup we can get by name. This will set the proper
|
|
1505
|
+
# crystal system. Creating a copy of the singleton from GetSpaceGroup,
|
|
1506
|
+
# as this function messes with sg.symop_list.
|
|
1507
|
+
sg = copy.copy(GetSpaceGroup(name))
|
|
1508
|
+
|
|
1509
|
+
# Replace the symmetry operations to guarantee that we get it right.
|
|
1510
|
+
symops = sgobjcryst.GetSymmetryOperations()
|
|
1511
|
+
tranops = sgobjcryst.GetTranslationVectors()
|
|
1512
|
+
sg.symop_list = []
|
|
1513
|
+
|
|
1514
|
+
for trans in tranops:
|
|
1515
|
+
for shift, rot in symops:
|
|
1516
|
+
tv = trans + shift
|
|
1517
|
+
tv -= numpy.floor(tv)
|
|
1518
|
+
sg.symop_list.append(SymOp(rot, tv))
|
|
1519
|
+
|
|
1520
|
+
if sgobjcryst.IsCentrosymmetric():
|
|
1521
|
+
center = sgobjcryst.GetInversionCenter()
|
|
1522
|
+
for trans in tranops:
|
|
1523
|
+
for shift, rot in symops:
|
|
1524
|
+
tv = center - trans - shift
|
|
1525
|
+
tv -= numpy.floor(tv)
|
|
1526
|
+
sg.symop_list.append(SymOp(-rot, tv))
|
|
1527
|
+
|
|
1528
|
+
return sg
|
|
1529
|
+
|
|
1530
|
+
@classmethod
|
|
1531
|
+
def canAdapt(self, stru):
|
|
1532
|
+
"""Return whether the structure can be adapted by this class."""
|
|
1533
|
+
from pyobjcryst.crystal import Crystal
|
|
1534
|
+
|
|
1535
|
+
return isinstance(stru, Crystal)
|
|
1536
|
+
|
|
1537
|
+
def getLattice(self):
|
|
1538
|
+
"""Get the ParameterSet containing the lattice Parameters."""
|
|
1539
|
+
return self
|
|
1540
|
+
|
|
1541
|
+
def getScatterers(self):
|
|
1542
|
+
"""Get a list of ParameterSets that represents the scatterers.
|
|
1543
|
+
|
|
1544
|
+
The site positions must be accessible from the list entries via
|
|
1545
|
+
the names "x", "y", and "z". The ADPs must be accessible as
|
|
1546
|
+
well, but the name and nature of the ADPs (U-factors, B-factors,
|
|
1547
|
+
isotropic, anisotropic) depends on the adapted structure.
|
|
1548
|
+
"""
|
|
1549
|
+
return self.scatterers
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
# End class ObjCrystCrystalParSet
|