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.
Files changed (73) hide show
  1. diffpy/__init__.py +15 -0
  2. diffpy/srfit/__init__.py +41 -0
  3. diffpy/srfit/equation/__init__.py +31 -0
  4. diffpy/srfit/equation/builder.py +702 -0
  5. diffpy/srfit/equation/equationmod.py +213 -0
  6. diffpy/srfit/equation/literals/__init__.py +71 -0
  7. diffpy/srfit/equation/literals/abcs.py +81 -0
  8. diffpy/srfit/equation/literals/argument.py +76 -0
  9. diffpy/srfit/equation/literals/literal.py +69 -0
  10. diffpy/srfit/equation/literals/operators.py +380 -0
  11. diffpy/srfit/equation/visitors/__init__.py +94 -0
  12. diffpy/srfit/equation/visitors/argfinder.py +62 -0
  13. diffpy/srfit/equation/visitors/printer.py +120 -0
  14. diffpy/srfit/equation/visitors/swapper.py +142 -0
  15. diffpy/srfit/equation/visitors/validator.py +113 -0
  16. diffpy/srfit/equation/visitors/visitor.py +62 -0
  17. diffpy/srfit/exceptions.py +29 -0
  18. diffpy/srfit/fitbase/__init__.py +53 -0
  19. diffpy/srfit/fitbase/calculator.py +110 -0
  20. diffpy/srfit/fitbase/configurable.py +57 -0
  21. diffpy/srfit/fitbase/constraint.py +117 -0
  22. diffpy/srfit/fitbase/fitcontribution.py +351 -0
  23. diffpy/srfit/fitbase/fithook.py +227 -0
  24. diffpy/srfit/fitbase/fitrecipe.py +826 -0
  25. diffpy/srfit/fitbase/fitresults.py +674 -0
  26. diffpy/srfit/fitbase/parameter.py +314 -0
  27. diffpy/srfit/fitbase/parameterset.py +110 -0
  28. diffpy/srfit/fitbase/profile.py +405 -0
  29. diffpy/srfit/fitbase/profilegenerator.py +168 -0
  30. diffpy/srfit/fitbase/profileparser.py +179 -0
  31. diffpy/srfit/fitbase/recipeorganizer.py +1074 -0
  32. diffpy/srfit/fitbase/restraint.py +117 -0
  33. diffpy/srfit/fitbase/simplerecipe.py +230 -0
  34. diffpy/srfit/fitbase/validatable.py +59 -0
  35. diffpy/srfit/interface/__init__.py +33 -0
  36. diffpy/srfit/interface/interface.py +180 -0
  37. diffpy/srfit/pdf/__init__.py +24 -0
  38. diffpy/srfit/pdf/basepdfgenerator.py +296 -0
  39. diffpy/srfit/pdf/characteristicfunctions.py +432 -0
  40. diffpy/srfit/pdf/debyepdfgenerator.py +115 -0
  41. diffpy/srfit/pdf/pdfcontribution.py +311 -0
  42. diffpy/srfit/pdf/pdfgenerator.py +77 -0
  43. diffpy/srfit/pdf/pdfparser.py +226 -0
  44. diffpy/srfit/sas/__init__.py +30 -0
  45. diffpy/srfit/sas/prcalculator.py +139 -0
  46. diffpy/srfit/sas/sasgenerator.py +70 -0
  47. diffpy/srfit/sas/sasimport.py +66 -0
  48. diffpy/srfit/sas/sasparameter.py +70 -0
  49. diffpy/srfit/sas/sasparser.py +133 -0
  50. diffpy/srfit/sas/sasprofile.py +94 -0
  51. diffpy/srfit/structure/__init__.py +60 -0
  52. diffpy/srfit/structure/basestructureparset.py +62 -0
  53. diffpy/srfit/structure/bvsrestraint.py +101 -0
  54. diffpy/srfit/structure/cctbxparset.py +292 -0
  55. diffpy/srfit/structure/diffpyparset.py +303 -0
  56. diffpy/srfit/structure/objcrystparset.py +1552 -0
  57. diffpy/srfit/structure/sgconstraints.py +761 -0
  58. diffpy/srfit/structure/srrealparset.py +92 -0
  59. diffpy/srfit/util/__init__.py +50 -0
  60. diffpy/srfit/util/argbinders.py +29 -0
  61. diffpy/srfit/util/inpututils.py +50 -0
  62. diffpy/srfit/util/nameutils.py +45 -0
  63. diffpy/srfit/util/observable.py +93 -0
  64. diffpy/srfit/util/tagmanager.py +150 -0
  65. diffpy/srfit/util/weakrefcallable.py +170 -0
  66. diffpy/srfit/version.py +26 -0
  67. diffpy_srfit-3.1.0.dist-info/METADATA +216 -0
  68. diffpy_srfit-3.1.0.dist-info/RECORD +73 -0
  69. diffpy_srfit-3.1.0.dist-info/WHEEL +5 -0
  70. diffpy_srfit-3.1.0.dist-info/licenses/AUTHORS.rst +10 -0
  71. diffpy_srfit-3.1.0.dist-info/licenses/LICENSE.rst +138 -0
  72. diffpy_srfit-3.1.0.dist-info/licenses/LICENSE_DANSE.rst +34 -0
  73. 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