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,761 @@
|
|
|
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
|
+
"""Code to set space group constraints for a crystal structure."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
import numpy
|
|
21
|
+
|
|
22
|
+
from diffpy.srfit.fitbase.parameter import ParameterProxy
|
|
23
|
+
from diffpy.srfit.fitbase.recipeorganizer import RecipeContainer
|
|
24
|
+
|
|
25
|
+
__all__ = ["constrainAsSpaceGroup"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def constrainAsSpaceGroup(
|
|
29
|
+
phase,
|
|
30
|
+
spacegroup,
|
|
31
|
+
scatterers=None,
|
|
32
|
+
sgoffset=[0, 0, 0],
|
|
33
|
+
constrainlat=True,
|
|
34
|
+
constrainadps=True,
|
|
35
|
+
adpsymbols=None,
|
|
36
|
+
isosymbol="Uiso",
|
|
37
|
+
):
|
|
38
|
+
"""Constrain the structure to the space group.
|
|
39
|
+
|
|
40
|
+
This applies space group constraints to a StructureParSet with P1
|
|
41
|
+
symmetry. Passed scatterers are explicitly constrained to the
|
|
42
|
+
specified space group. The ADPs and lattice may be constrained as well.
|
|
43
|
+
|
|
44
|
+
Arguments:
|
|
45
|
+
phase -- A BaseStructure object.
|
|
46
|
+
spacegroup -- The space group number, symbol or an instance of
|
|
47
|
+
SpaceGroup class from diffpy.structure package.
|
|
48
|
+
sgoffset -- Optional offset for sg origin (default [0, 0, 0]).
|
|
49
|
+
scatterers -- The scatterer ParameterSets to constrain. If scatterers
|
|
50
|
+
is None (default), then all scatterers accessible from
|
|
51
|
+
phase.getScatterers will be constrained.
|
|
52
|
+
constrainlat -- Flag indicating whether to constrain the lattice
|
|
53
|
+
(default True).
|
|
54
|
+
constrainadps -- Flag indicating whether to constrain the ADPs
|
|
55
|
+
(default True).
|
|
56
|
+
adpsymbols -- A list of the ADP names. By default this is equal to
|
|
57
|
+
diffpy.structure.symmetryutilities.stdUsymbols (U11,
|
|
58
|
+
U22, etc.). The names must be given in the same order
|
|
59
|
+
as stdUsymbols.
|
|
60
|
+
isosymbol -- Symbol for isotropic ADP (default "Uiso"). If None,
|
|
61
|
+
isotropic ADPs will be constrained via the anisotropic ADPs.
|
|
62
|
+
|
|
63
|
+
New Parameters that are used in constraints are created within a
|
|
64
|
+
SpaceGroupParameters object, which is returned from this function.
|
|
65
|
+
Constraints are created in ParameterSet that contains the constrained
|
|
66
|
+
Parameter. This will erase any constraints or constant flags on the
|
|
67
|
+
scatterers, lattice or ADPs if they are to be constrained.
|
|
68
|
+
|
|
69
|
+
The lattice constraints are applied as following.
|
|
70
|
+
|
|
71
|
+
Crystal System:
|
|
72
|
+
Triclinic -- No constraints.
|
|
73
|
+
Monoclinic -- alpha and beta are fixed to 90 unless alpha != beta and
|
|
74
|
+
alpha == gamma, in which case alpha and gamma are fixed
|
|
75
|
+
to 90.
|
|
76
|
+
Orthorhombic -- alpha, beta and gamma are fixed to 90.
|
|
77
|
+
Tetragonal -- b is constrained to a and alpha, beta and gamma are
|
|
78
|
+
fixed to 90.
|
|
79
|
+
Trigonal -- If gamma == 120, then b is constrained to a, alpha
|
|
80
|
+
and beta are fixed to 90 and gamma is fixed to 120.
|
|
81
|
+
Otherwise, b and c are constrained to a, beta and gamma
|
|
82
|
+
are fixed to alpha.
|
|
83
|
+
Hexagonal -- b is constrained to a, alpha and beta are fixed to 90
|
|
84
|
+
and gamma is fixed to 120.
|
|
85
|
+
Cubic -- b and c are constrained to a, and alpha, beta and
|
|
86
|
+
gamma are fixed to 90.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
from diffpy.structure.spacegroups import GetSpaceGroup, SpaceGroup
|
|
90
|
+
|
|
91
|
+
sg = spacegroup
|
|
92
|
+
if not isinstance(spacegroup, SpaceGroup):
|
|
93
|
+
sg = GetSpaceGroup(spacegroup)
|
|
94
|
+
sgp = _constrainAsSpaceGroup(
|
|
95
|
+
phase,
|
|
96
|
+
sg,
|
|
97
|
+
scatterers,
|
|
98
|
+
sgoffset,
|
|
99
|
+
constrainlat,
|
|
100
|
+
constrainadps,
|
|
101
|
+
adpsymbols,
|
|
102
|
+
isosymbol,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return sgp
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _constrainAsSpaceGroup(
|
|
109
|
+
phase,
|
|
110
|
+
sg,
|
|
111
|
+
scatterers=None,
|
|
112
|
+
sgoffset=[0, 0, 0],
|
|
113
|
+
constrainlat=True,
|
|
114
|
+
constrainadps=True,
|
|
115
|
+
adpsymbols=None,
|
|
116
|
+
isosymbol="Uiso",
|
|
117
|
+
):
|
|
118
|
+
"""Restricted interface to constrainAsSpaceGroup.
|
|
119
|
+
|
|
120
|
+
Arguments: As constrainAsSpaceGroup, except
|
|
121
|
+
sg -- diffpy.structure.spacegroups.SpaceGroup instance
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
from diffpy.structure.symmetryutilities import stdUsymbols
|
|
125
|
+
|
|
126
|
+
if scatterers is None:
|
|
127
|
+
scatterers = phase.getScatterers()
|
|
128
|
+
if adpsymbols is None:
|
|
129
|
+
adpsymbols = stdUsymbols
|
|
130
|
+
|
|
131
|
+
sgp = SpaceGroupParameters(
|
|
132
|
+
phase,
|
|
133
|
+
sg,
|
|
134
|
+
scatterers,
|
|
135
|
+
sgoffset,
|
|
136
|
+
constrainlat,
|
|
137
|
+
constrainadps,
|
|
138
|
+
adpsymbols,
|
|
139
|
+
isosymbol,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return sgp
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# End constrainAsSpaceGroup
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class BaseSpaceGroupParameters(RecipeContainer):
|
|
149
|
+
"""Base class for holding space group Parameters.
|
|
150
|
+
|
|
151
|
+
This class is used to store the variable Parameters of a structure, leaving
|
|
152
|
+
out those that constrained or fixed due to space group. This class has the
|
|
153
|
+
same Parameter attribute access of a ParameterSet. The purpose of this
|
|
154
|
+
class is to make it easy to access the free variables of a structure for
|
|
155
|
+
scripting purposes.
|
|
156
|
+
|
|
157
|
+
Attributes
|
|
158
|
+
name -- "sgpars"
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(self, name="sgpars"):
|
|
162
|
+
"""Create the BaseSpaceGroupParameters object.
|
|
163
|
+
|
|
164
|
+
This initializes the attributes.
|
|
165
|
+
"""
|
|
166
|
+
RecipeContainer.__init__(self, name)
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
def addParameter(self, par, check=True):
|
|
170
|
+
"""Store a Parameter.
|
|
171
|
+
|
|
172
|
+
par -- The Parameter to be stored.
|
|
173
|
+
check -- If True (default), a ValueError is raised a Parameter of
|
|
174
|
+
the specified name has already been inserted.
|
|
175
|
+
|
|
176
|
+
Raises ValueError if the Parameter has no name.
|
|
177
|
+
"""
|
|
178
|
+
# Store the Parameter
|
|
179
|
+
RecipeContainer._addObject(self, par, self._parameters, check)
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# End class BaseSpaceGroupParameters
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class SpaceGroupParameters(BaseSpaceGroupParameters):
|
|
187
|
+
"""Class for holding and creating space group Parameters.
|
|
188
|
+
|
|
189
|
+
This class is used to store the variable Parameters of a structure, leaving
|
|
190
|
+
out those that constrained or fixed due to space group. This does the work
|
|
191
|
+
of the constrainAsSpaceGroup method. This class has the same Parameter
|
|
192
|
+
attribute access of a ParameterSet.
|
|
193
|
+
|
|
194
|
+
Attributes
|
|
195
|
+
name -- "sgpars"
|
|
196
|
+
phase -- The constrained BaseStructure object.
|
|
197
|
+
sg -- The diffpy.structure.spacegroups.SpaceGroup object
|
|
198
|
+
corresponding to the space group.
|
|
199
|
+
sgoffset -- Optional offset for the space group origin.
|
|
200
|
+
scatterers -- The constrained scatterer ParameterSets.
|
|
201
|
+
constrainlat -- Flag indicating whether the lattice is constrained.
|
|
202
|
+
constrainadps -- Flag indicating whether the ADPs are constrained.
|
|
203
|
+
adpsymbols -- A list of the ADP names.
|
|
204
|
+
_xyzpars -- BaseSpaceGroupParameters of free xyz Parameters that are
|
|
205
|
+
constrained to.
|
|
206
|
+
xyzpars -- Property that populates _xyzpars.
|
|
207
|
+
_latpars -- BaseSpaceGroupParameters of free lattice Parameters that
|
|
208
|
+
are constrained to.
|
|
209
|
+
latpars -- Property that populates _latpars.
|
|
210
|
+
_adppars -- BaseSpaceGroupParameters of free ADPs that are constrained
|
|
211
|
+
to.
|
|
212
|
+
adppars -- Property that populates _adppars.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
def __init__(
|
|
216
|
+
self,
|
|
217
|
+
phase,
|
|
218
|
+
sg,
|
|
219
|
+
scatterers,
|
|
220
|
+
sgoffset,
|
|
221
|
+
constrainlat,
|
|
222
|
+
constrainadps,
|
|
223
|
+
adpsymbols,
|
|
224
|
+
isosymbol,
|
|
225
|
+
):
|
|
226
|
+
"""Create the SpaceGroupParameters object.
|
|
227
|
+
|
|
228
|
+
Arguments:
|
|
229
|
+
phase -- A BaseStructure object to be constrained.
|
|
230
|
+
sg -- The space group number or symbol (compatible with
|
|
231
|
+
diffpy.structure.spacegroups.GetSpaceGroup.
|
|
232
|
+
sgoffset -- Optional offset for sg origin.
|
|
233
|
+
scatterers -- The scatterer ParameterSets to constrain. If scatterers
|
|
234
|
+
is None, then all scatterers accessible from
|
|
235
|
+
phase.getScatterers will be constrained.
|
|
236
|
+
constrainlat -- Flag indicating whether to constrain the lattice.
|
|
237
|
+
constrainadps -- Flag indicating whether to constrain the ADPs.
|
|
238
|
+
adpsymbols -- A list of the ADP names. The names must be given in the
|
|
239
|
+
same order as
|
|
240
|
+
diffpy.structure.symmetryutilities.stdUsymbols.
|
|
241
|
+
isosymbol -- Symbol for isotropic ADP (default "Uiso"). If None,
|
|
242
|
+
isotropic ADPs will be constrained via the anisotropic
|
|
243
|
+
ADPs.
|
|
244
|
+
"""
|
|
245
|
+
BaseSpaceGroupParameters.__init__(self)
|
|
246
|
+
self._latpars = None
|
|
247
|
+
self._xyzpars = None
|
|
248
|
+
self._adppars = None
|
|
249
|
+
|
|
250
|
+
self._parsets = {}
|
|
251
|
+
self._manage(self._parsets)
|
|
252
|
+
|
|
253
|
+
self.phase = phase
|
|
254
|
+
self.sg = sg
|
|
255
|
+
self.sgoffset = sgoffset
|
|
256
|
+
self.scatterers = scatterers
|
|
257
|
+
self.constrainlat = constrainlat
|
|
258
|
+
self.constrainadps = constrainadps
|
|
259
|
+
self.adpsymbols = adpsymbols
|
|
260
|
+
self.isosymbol = isosymbol
|
|
261
|
+
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
def __iter__(self):
|
|
265
|
+
"""Iterate over top-level parameters."""
|
|
266
|
+
if (
|
|
267
|
+
self._latpars is None
|
|
268
|
+
or self._xyzpars is None
|
|
269
|
+
or self._adppars is None
|
|
270
|
+
):
|
|
271
|
+
self._makeConstraints()
|
|
272
|
+
return RecipeContainer.__iter__(self)
|
|
273
|
+
|
|
274
|
+
latpars = property(lambda self: self._getLatPars())
|
|
275
|
+
|
|
276
|
+
def _getLatPars(self):
|
|
277
|
+
"""Accessor for _latpars."""
|
|
278
|
+
if self._latpars is None:
|
|
279
|
+
self._constrainLattice()
|
|
280
|
+
return self._latpars
|
|
281
|
+
|
|
282
|
+
xyzpars = property(lambda self: self._getXYZPars())
|
|
283
|
+
|
|
284
|
+
def _getXYZPars(self):
|
|
285
|
+
"""Accessor for _xyzpars."""
|
|
286
|
+
positions = []
|
|
287
|
+
for scatterer in self.scatterers:
|
|
288
|
+
xyz = [scatterer.x, scatterer.y, scatterer.z]
|
|
289
|
+
positions.append([p.value for p in xyz])
|
|
290
|
+
if self._xyzpars is None:
|
|
291
|
+
self._constrainXYZs(positions)
|
|
292
|
+
return self._xyzpars
|
|
293
|
+
|
|
294
|
+
adppars = property(lambda self: self._getADPPars())
|
|
295
|
+
|
|
296
|
+
def _getADPPars(self):
|
|
297
|
+
"""Accessor for _adppars."""
|
|
298
|
+
positions = []
|
|
299
|
+
for scatterer in self.scatterers:
|
|
300
|
+
xyz = [scatterer.x, scatterer.y, scatterer.z]
|
|
301
|
+
positions.append([p.value for p in xyz])
|
|
302
|
+
if self._adppars is None:
|
|
303
|
+
self._constrainADPs(positions)
|
|
304
|
+
return self._adppars
|
|
305
|
+
|
|
306
|
+
def _makeConstraints(self):
|
|
307
|
+
"""Constrain the structure to the space group.
|
|
308
|
+
|
|
309
|
+
This works as described by the constrainAsSpaceGroup method.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
# Start by clearing the constraints
|
|
313
|
+
self._clearConstraints()
|
|
314
|
+
|
|
315
|
+
scatterers = self.scatterers
|
|
316
|
+
|
|
317
|
+
# Prepare positions
|
|
318
|
+
positions = []
|
|
319
|
+
for scatterer in scatterers:
|
|
320
|
+
xyz = [scatterer.x, scatterer.y, scatterer.z]
|
|
321
|
+
positions.append([p.value for p in xyz])
|
|
322
|
+
|
|
323
|
+
self._constrainLattice()
|
|
324
|
+
self._constrainXYZs(positions)
|
|
325
|
+
self._constrainADPs(positions)
|
|
326
|
+
|
|
327
|
+
return
|
|
328
|
+
|
|
329
|
+
def _clearConstraints(self):
|
|
330
|
+
"""Clear old constraints.
|
|
331
|
+
|
|
332
|
+
This only clears constraints where new ones are going to be
|
|
333
|
+
applied.
|
|
334
|
+
"""
|
|
335
|
+
phase = self.phase
|
|
336
|
+
scatterers = self.scatterers
|
|
337
|
+
isosymbol = self.isosymbol
|
|
338
|
+
adpsymbols = self.adpsymbols
|
|
339
|
+
|
|
340
|
+
# Clear xyz
|
|
341
|
+
for scatterer in scatterers:
|
|
342
|
+
|
|
343
|
+
for par in [scatterer.x, scatterer.y, scatterer.z]:
|
|
344
|
+
if scatterer.isConstrained(par):
|
|
345
|
+
scatterer.unconstrain(par)
|
|
346
|
+
par.setConst(False)
|
|
347
|
+
|
|
348
|
+
# Clear the lattice
|
|
349
|
+
if self.constrainlat:
|
|
350
|
+
|
|
351
|
+
lattice = phase.getLattice()
|
|
352
|
+
latpars = [
|
|
353
|
+
lattice.a,
|
|
354
|
+
lattice.b,
|
|
355
|
+
lattice.c,
|
|
356
|
+
lattice.alpha,
|
|
357
|
+
lattice.beta,
|
|
358
|
+
lattice.gamma,
|
|
359
|
+
]
|
|
360
|
+
for par in latpars:
|
|
361
|
+
if lattice.isConstrained(par):
|
|
362
|
+
lattice.unconstrain(par)
|
|
363
|
+
par.setConst(False)
|
|
364
|
+
|
|
365
|
+
# Clear ADPs
|
|
366
|
+
if self.constrainadps:
|
|
367
|
+
for scatterer in scatterers:
|
|
368
|
+
if isosymbol:
|
|
369
|
+
par = scatterer.get(isosymbol)
|
|
370
|
+
if par is not None:
|
|
371
|
+
if scatterer.isConstrained(par):
|
|
372
|
+
scatterer.unconstrain(par)
|
|
373
|
+
par.setConst(False)
|
|
374
|
+
|
|
375
|
+
for pname in adpsymbols:
|
|
376
|
+
par = scatterer.get(pname)
|
|
377
|
+
if par is not None:
|
|
378
|
+
if scatterer.isConstrained(par):
|
|
379
|
+
scatterer.unconstrain(par)
|
|
380
|
+
par.setConst(False)
|
|
381
|
+
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
def _constrainLattice(self):
|
|
385
|
+
"""Constrain the lattice parameters."""
|
|
386
|
+
|
|
387
|
+
if not self.constrainlat:
|
|
388
|
+
return
|
|
389
|
+
|
|
390
|
+
phase = self.phase
|
|
391
|
+
sg = self.sg
|
|
392
|
+
|
|
393
|
+
lattice = phase.getLattice()
|
|
394
|
+
system = sg.crystal_system
|
|
395
|
+
if not system:
|
|
396
|
+
system = "Triclinic"
|
|
397
|
+
system = system.title()
|
|
398
|
+
# This makes the constraints
|
|
399
|
+
f = _constraintMap[system]
|
|
400
|
+
f(lattice)
|
|
401
|
+
|
|
402
|
+
# Now get the unconstrained, non-constant lattice pars and store them.
|
|
403
|
+
self._latpars = BaseSpaceGroupParameters("latpars")
|
|
404
|
+
latpars = [
|
|
405
|
+
lattice.a,
|
|
406
|
+
lattice.b,
|
|
407
|
+
lattice.c,
|
|
408
|
+
lattice.alpha,
|
|
409
|
+
lattice.beta,
|
|
410
|
+
lattice.gamma,
|
|
411
|
+
]
|
|
412
|
+
pars = [p for p in latpars if not p.const and not p.constrained]
|
|
413
|
+
for par in pars:
|
|
414
|
+
# FIXME - the original parameter will still appear as
|
|
415
|
+
# constrained.
|
|
416
|
+
newpar = self.__addPar(par.name, par)
|
|
417
|
+
self._latpars.addParameter(newpar)
|
|
418
|
+
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
def _constrainXYZs(self, positions):
|
|
422
|
+
"""Constrain the positions.
|
|
423
|
+
|
|
424
|
+
positions -- The coordinates of the scatterers.
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
from diffpy.structure.symmetryutilities import SymmetryConstraints
|
|
428
|
+
|
|
429
|
+
sg = self.sg
|
|
430
|
+
sgoffset = self.sgoffset
|
|
431
|
+
|
|
432
|
+
# We do this without ADPs here so we can skip much complication. See
|
|
433
|
+
# the _constrainADPs method for details.
|
|
434
|
+
g = SymmetryConstraints(sg, positions, sgoffset=sgoffset)
|
|
435
|
+
|
|
436
|
+
scatterers = self.scatterers
|
|
437
|
+
self._xyzpars = BaseSpaceGroupParameters("xyzpars")
|
|
438
|
+
|
|
439
|
+
# Make proxies to the free xyz parameters
|
|
440
|
+
xyznames = [name[:1] + "_" + name[1:] for name, val in g.pospars]
|
|
441
|
+
for pname in xyznames:
|
|
442
|
+
name, idx = pname.rsplit("_", 1)
|
|
443
|
+
idx = int(idx)
|
|
444
|
+
par = scatterers[idx].get(name)
|
|
445
|
+
newpar = self.__addPar(pname, par)
|
|
446
|
+
self._xyzpars.addParameter(newpar)
|
|
447
|
+
|
|
448
|
+
# Constrain non-free xyz parameters
|
|
449
|
+
fpos = g.positionFormulas(xyznames)
|
|
450
|
+
for idx, tmp in enumerate(zip(scatterers, fpos)):
|
|
451
|
+
scatterer, fp = tmp
|
|
452
|
+
|
|
453
|
+
# Extract the constraint equation from the formula
|
|
454
|
+
for parname, formula in fp.items():
|
|
455
|
+
_makeconstraint(
|
|
456
|
+
parname, formula, scatterer, idx, self._parameters
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
return
|
|
460
|
+
|
|
461
|
+
def _constrainADPs(self, positions):
|
|
462
|
+
"""Constrain the ADPs.
|
|
463
|
+
|
|
464
|
+
positions -- The coordinates of the scatterers.
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
from diffpy.structure.symmetryutilities import (
|
|
468
|
+
SymmetryConstraints,
|
|
469
|
+
stdUsymbols,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
if not self.constrainadps:
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
sg = self.sg
|
|
476
|
+
sgoffset = self.sgoffset
|
|
477
|
+
scatterers = self.scatterers
|
|
478
|
+
isosymbol = self.isosymbol
|
|
479
|
+
adpsymbols = self.adpsymbols
|
|
480
|
+
adpmap = dict(zip(stdUsymbols, adpsymbols))
|
|
481
|
+
self._adppars = BaseSpaceGroupParameters("adppars")
|
|
482
|
+
|
|
483
|
+
# Prepare ADPs. Note that not all scatterers have constrainable ADPs.
|
|
484
|
+
# For example, MoleculeParSet from objcryststructure does not. We
|
|
485
|
+
# discard those.
|
|
486
|
+
nonadps = []
|
|
487
|
+
Uijs = []
|
|
488
|
+
for sidx, scatterer in enumerate(scatterers):
|
|
489
|
+
|
|
490
|
+
pars = [scatterer.get(symb) for symb in adpsymbols]
|
|
491
|
+
|
|
492
|
+
if None in pars:
|
|
493
|
+
nonadps.append(sidx)
|
|
494
|
+
continue
|
|
495
|
+
|
|
496
|
+
Uij = numpy.zeros((3, 3), dtype=float)
|
|
497
|
+
for idx, par in enumerate(pars):
|
|
498
|
+
i, j = _idxtoij[idx]
|
|
499
|
+
Uij[i, j] = Uij[j, i] = par.getValue()
|
|
500
|
+
|
|
501
|
+
Uijs.append(Uij)
|
|
502
|
+
|
|
503
|
+
# Discard any positions for the nonadps
|
|
504
|
+
positions = list(positions)
|
|
505
|
+
nonadps.reverse()
|
|
506
|
+
[positions.pop(idx) for idx in nonadps]
|
|
507
|
+
|
|
508
|
+
# Now we can create symmetry constraints without having to worry about
|
|
509
|
+
# the nonadps
|
|
510
|
+
g = SymmetryConstraints(sg, positions, Uijs, sgoffset=sgoffset)
|
|
511
|
+
|
|
512
|
+
adpnames = [adpmap[name[:3]] + "_" + name[3:] for name, val in g.Upars]
|
|
513
|
+
|
|
514
|
+
# Make proxies to the free adp parameters. We start by filtering out
|
|
515
|
+
# the isotropic ones so we can use the isotropic parameter.
|
|
516
|
+
isoidx = []
|
|
517
|
+
isonames = []
|
|
518
|
+
for pname in adpnames:
|
|
519
|
+
name, idx = pname.rsplit("_", 1)
|
|
520
|
+
idx = int(idx)
|
|
521
|
+
# Check for isotropic ADPs
|
|
522
|
+
scatterer = scatterers[idx]
|
|
523
|
+
if isosymbol and g.Uisotropy[idx] and idx not in isoidx:
|
|
524
|
+
isoidx.append(idx)
|
|
525
|
+
par = scatterer.get(isosymbol)
|
|
526
|
+
if par is not None:
|
|
527
|
+
parname = "%s_%i" % (isosymbol, idx)
|
|
528
|
+
newpar = self.__addPar(parname, par)
|
|
529
|
+
self._adppars.addParameter(newpar)
|
|
530
|
+
isonames.append(newpar.name)
|
|
531
|
+
else:
|
|
532
|
+
par = scatterer.get(name)
|
|
533
|
+
if par is not None:
|
|
534
|
+
newpar = self.__addPar(pname, par)
|
|
535
|
+
self._adppars.addParameter(newpar)
|
|
536
|
+
|
|
537
|
+
# Constrain dependent isotropics
|
|
538
|
+
for idx, isoname in zip(isoidx[:], isonames):
|
|
539
|
+
for j in g.coremap[idx]:
|
|
540
|
+
if j == idx:
|
|
541
|
+
continue
|
|
542
|
+
isoidx.append(j)
|
|
543
|
+
scatterer = scatterers[j]
|
|
544
|
+
scatterer.constrain(isosymbol, isoname, ns=self._parameters)
|
|
545
|
+
|
|
546
|
+
fadp = g.UFormulas(adpnames)
|
|
547
|
+
|
|
548
|
+
# Constrain dependent anisotropics. We use the fact that an
|
|
549
|
+
# anisotropic cannot be dependent on an isotropic.
|
|
550
|
+
for idx, tmp in enumerate(zip(scatterers, fadp)):
|
|
551
|
+
if idx in isoidx:
|
|
552
|
+
continue
|
|
553
|
+
scatterer, fa = tmp
|
|
554
|
+
# Extract the constraint equation from the formula
|
|
555
|
+
for stdparname, formula in fa.items():
|
|
556
|
+
pname = adpmap[stdparname]
|
|
557
|
+
_makeconstraint(
|
|
558
|
+
pname, formula, scatterer, idx, self._parameters
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
def __addPar(self, parname, par):
|
|
562
|
+
"""Constrain a parameter via proxy with a specified name.
|
|
563
|
+
|
|
564
|
+
par -- Parameter to constrain
|
|
565
|
+
idx -- Index to identify scatterer from which par comes
|
|
566
|
+
"""
|
|
567
|
+
newpar = ParameterProxy(parname, par)
|
|
568
|
+
self.addParameter(newpar)
|
|
569
|
+
return newpar
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
# End class SpaceGroupParameters
|
|
573
|
+
|
|
574
|
+
# crystal system rules
|
|
575
|
+
# ref: Benjamin, W. A., Introduction to crystallography,
|
|
576
|
+
# New York (1969), p.60
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _constrainTriclinic(lattice):
|
|
580
|
+
"""Make constraints for Triclinic systems."""
|
|
581
|
+
return
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def _constrainMonoclinic(lattice):
|
|
585
|
+
"""Make constraints for Monoclinic systems.
|
|
586
|
+
|
|
587
|
+
alpha and beta are fixed to 90 unless alpha != beta and alpha ==
|
|
588
|
+
gamma, in which case alpha and gamma are constrained to 90.
|
|
589
|
+
"""
|
|
590
|
+
afactor = 1
|
|
591
|
+
if lattice.angunits == "rad":
|
|
592
|
+
afactor = deg2rad
|
|
593
|
+
ang90 = 90.0 * afactor
|
|
594
|
+
lattice.alpha.setConst(True, ang90)
|
|
595
|
+
beta = lattice.beta.getValue()
|
|
596
|
+
gamma = lattice.gamma.getValue()
|
|
597
|
+
|
|
598
|
+
if ang90 != beta and ang90 == gamma:
|
|
599
|
+
lattice.gamma.setConst(True, ang90)
|
|
600
|
+
else:
|
|
601
|
+
lattice.beta.setConst(True, ang90)
|
|
602
|
+
return
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _constrainOrthorhombic(lattice):
|
|
606
|
+
"""Make constraints for Orthorhombic systems.
|
|
607
|
+
|
|
608
|
+
alpha, beta and gamma are constrained to 90
|
|
609
|
+
"""
|
|
610
|
+
afactor = 1
|
|
611
|
+
if lattice.angunits == "rad":
|
|
612
|
+
afactor = deg2rad
|
|
613
|
+
ang90 = 90.0 * afactor
|
|
614
|
+
lattice.alpha.setConst(True, ang90)
|
|
615
|
+
lattice.beta.setConst(True, ang90)
|
|
616
|
+
lattice.gamma.setConst(True, ang90)
|
|
617
|
+
return
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _constrainTetragonal(lattice):
|
|
621
|
+
"""Make constraints for Tetragonal systems.
|
|
622
|
+
|
|
623
|
+
b is constrained to a and alpha, beta and gamma are constrained to
|
|
624
|
+
90.
|
|
625
|
+
"""
|
|
626
|
+
afactor = 1
|
|
627
|
+
if lattice.angunits == "rad":
|
|
628
|
+
afactor = deg2rad
|
|
629
|
+
ang90 = 90.0 * afactor
|
|
630
|
+
lattice.alpha.setConst(True, ang90)
|
|
631
|
+
lattice.beta.setConst(True, ang90)
|
|
632
|
+
lattice.gamma.setConst(True, ang90)
|
|
633
|
+
lattice.constrain(lattice.b, lattice.a)
|
|
634
|
+
return
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def _constrainTrigonal(lattice):
|
|
638
|
+
"""Make constraints for Trigonal systems.
|
|
639
|
+
|
|
640
|
+
If gamma == 120, then b is constrained to a, alpha and beta are
|
|
641
|
+
constrained to 90 and gamma is constrained to 120. Otherwise, b and
|
|
642
|
+
c are constrained to a, beta and gamma are constrained to alpha.
|
|
643
|
+
"""
|
|
644
|
+
afactor = 1
|
|
645
|
+
if lattice.angunits == "rad":
|
|
646
|
+
afactor = deg2rad
|
|
647
|
+
ang90 = 90.0 * afactor
|
|
648
|
+
ang120 = 120.0 * afactor
|
|
649
|
+
if lattice.gamma.getValue() == ang120:
|
|
650
|
+
lattice.constrain(lattice.b, lattice.a)
|
|
651
|
+
lattice.alpha.setConst(True, ang90)
|
|
652
|
+
lattice.beta.setConst(True, ang90)
|
|
653
|
+
lattice.gamma.setConst(True, ang120)
|
|
654
|
+
else:
|
|
655
|
+
lattice.constrain(lattice.b, lattice.a)
|
|
656
|
+
lattice.constrain(lattice.c, lattice.a)
|
|
657
|
+
lattice.constrain(lattice.beta, lattice.alpha)
|
|
658
|
+
lattice.constrain(lattice.gamma, lattice.alpha)
|
|
659
|
+
return
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _constrainHexagonal(lattice):
|
|
663
|
+
"""Make constraints for Hexagonal systems.
|
|
664
|
+
|
|
665
|
+
b is constrained to a, alpha and beta are constrained to 90 and
|
|
666
|
+
gamma is constrained to 120.
|
|
667
|
+
"""
|
|
668
|
+
afactor = 1
|
|
669
|
+
if lattice.angunits == "rad":
|
|
670
|
+
afactor = deg2rad
|
|
671
|
+
ang90 = 90.0 * afactor
|
|
672
|
+
ang120 = 120.0 * afactor
|
|
673
|
+
lattice.constrain(lattice.b, lattice.a)
|
|
674
|
+
lattice.alpha.setConst(True, ang90)
|
|
675
|
+
lattice.beta.setConst(True, ang90)
|
|
676
|
+
lattice.gamma.setConst(True, ang120)
|
|
677
|
+
return
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
def _constrainCubic(lattice):
|
|
681
|
+
"""Make constraints for Cubic systems.
|
|
682
|
+
|
|
683
|
+
b and c are constrained to a, alpha, beta and gamma are constrained
|
|
684
|
+
to 90.
|
|
685
|
+
"""
|
|
686
|
+
afactor = 1
|
|
687
|
+
if lattice.angunits == "rad":
|
|
688
|
+
afactor = deg2rad
|
|
689
|
+
ang90 = 90.0 * afactor
|
|
690
|
+
lattice.constrain(lattice.b, lattice.a)
|
|
691
|
+
lattice.constrain(lattice.c, lattice.a)
|
|
692
|
+
lattice.alpha.setConst(True, ang90)
|
|
693
|
+
lattice.beta.setConst(True, ang90)
|
|
694
|
+
lattice.gamma.setConst(True, ang90)
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
# This is used to map the correct crystal system to the proper constraint
|
|
699
|
+
# function.
|
|
700
|
+
_constraintMap = {
|
|
701
|
+
"Triclinic": _constrainTriclinic,
|
|
702
|
+
"Monoclinic": _constrainMonoclinic,
|
|
703
|
+
"Orthorhombic": _constrainOrthorhombic,
|
|
704
|
+
"Tetragonal": _constrainTetragonal,
|
|
705
|
+
"Trigonal": _constrainTrigonal,
|
|
706
|
+
"Hexagonal": _constrainHexagonal,
|
|
707
|
+
"Cubic": _constrainCubic,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def _makeconstraint(parname, formula, scatterer, idx, ns={}):
|
|
712
|
+
"""Constrain a parameter according to a formula.
|
|
713
|
+
|
|
714
|
+
parname -- Name of parameter
|
|
715
|
+
formula -- Constraint formula
|
|
716
|
+
scatterer -- scatterer containing par of parname
|
|
717
|
+
idx -- Index to identify scatterer from which par comes
|
|
718
|
+
ns -- namespace to draw extra names from (default {})
|
|
719
|
+
|
|
720
|
+
Returns the parameter if it is free.
|
|
721
|
+
"""
|
|
722
|
+
par = scatterer.get(parname)
|
|
723
|
+
|
|
724
|
+
if par is None:
|
|
725
|
+
return
|
|
726
|
+
|
|
727
|
+
compname = "%s_%i" % (parname, idx)
|
|
728
|
+
|
|
729
|
+
# Check to see if this parameter is free
|
|
730
|
+
pat = r"%s *([+-] *\d+)?$" % compname
|
|
731
|
+
if re.match(pat, formula):
|
|
732
|
+
return par
|
|
733
|
+
|
|
734
|
+
# Check to see if it is a constant
|
|
735
|
+
fval = _getFloat(formula)
|
|
736
|
+
if fval is not None:
|
|
737
|
+
par.setConst()
|
|
738
|
+
return
|
|
739
|
+
|
|
740
|
+
# If we got here, then we have a constraint equation
|
|
741
|
+
# Fix any division issues
|
|
742
|
+
formula = formula.replace("/", "*1.0/")
|
|
743
|
+
scatterer.constrain(par, formula, ns=ns)
|
|
744
|
+
return
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def _getFloat(formula):
|
|
748
|
+
"""Get a float from a formula string, or None if this is not possible."""
|
|
749
|
+
try:
|
|
750
|
+
return eval(formula)
|
|
751
|
+
except NameError:
|
|
752
|
+
return None
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
# Constants needed above
|
|
756
|
+
_idxtoij = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)]
|
|
757
|
+
deg2rad = numpy.pi / 180
|
|
758
|
+
rad2deg = 1.0 / deg2rad
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
# End of file
|