diff --git a/news/deprecate-symmetryutilities-5.rst b/news/deprecate-symmetryutilities-5.rst new file mode 100644 index 0000000..8e57245 --- /dev/null +++ b/news/deprecate-symmetryutilities-5.rst @@ -0,0 +1,29 @@ +**Added:** + +* Added ``position_formula`` method in ``GeneratorSite`` class +* Added ``u_formula`` method in ``GeneratorSite`` class +* Added ``eq_index`` method in ``GeneratorSite`` class +* Added ``prune_formula_dictionary`` method in ``symmetryutilities.py`` + +**Changed:** + +* + +**Deprecated:** + +* Deprecated ``positionFormula`` method in ``GeneratorSite`` class for removal in version 4.0.0 +* Deprecated ``UFormula`` method in ``GeneratorSite`` class for removal in version 4.0.0 +* Deprecated ``eqIndex`` method in ``GeneratorSite`` class for removal in version 4.0.0 +* Deprecated ``pruneFormulaDictionary`` method in ``symmetryutilities.py`` for removal in version 4.0.0 + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/structure/symmetryutilities.py b/src/diffpy/structure/symmetryutilities.py index b4a215d..8c605e5 100644 --- a/src/diffpy/structure/symmetryutilities.py +++ b/src/diffpy/structure/symmetryutilities.py @@ -476,6 +476,24 @@ def _find_invariants(symops): "convert_fp_num_to_signed_rational", removal_version, ) +positionFormula_deprecation_msg = build_deprecation_message( + generator_site, + "positionFormula", + "position_formula", + removal_version, +) +UFormula_deprecation_msg = build_deprecation_message( + generator_site, + "UFormula", + "u_formula", + removal_version, +) +eqIndex_deprecation_msg = build_deprecation_message( + generator_site, + "eqIndex", + "eq_index", + removal_version, +) class GeneratorSite(object): @@ -759,7 +777,17 @@ def _find_eq_uij(self): self.eqUij.append(numpy.dot(R, numpy.dot(self.Uij, Rt))) return + @deprecated(positionFormula_deprecation_msg) def positionFormula(self, pos, xyzsymbols=("x", "y", "z")): + """'diffpy.structure.GeneratorSite.positionFormula' is + deprecated and will be removed in version 4.0.0. + + Please use 'diffpy.structure.GeneratorSite.position_formula' + instead. + """ + return self.position_formula(pos, xyzsymbols) + + def position_formula(self, pos, xyzsymbols=("x", "y", "z")): """Formula of equivalent position with respect to generator site. @@ -811,7 +839,16 @@ def positionFormula(self, pos, xyzsymbols=("x", "y", "z")): xyzformula = [re.sub("^[+]1[*]|(?<=[+-])1[*]", "", f).strip() for f in xyzformula] return dict(zip(("x", "y", "z"), xyzformula)) + @deprecated(UFormula_deprecation_msg) def UFormula(self, pos, Usymbols=stdUsymbols): + """'diffpy.structure.GeneratorSite.UFormula' is deprecated and + will be removed in version 4.0.0. + + Please use 'diffpy.structure.GeneratorSite.u_formula' instead. + """ + return self.u_formula(pos, Usymbols) + + def u_formula(self, pos, Usymbols=stdUsymbols): """List of atom displacement formulas with custom parameter symbols. @@ -857,7 +894,16 @@ def UFormula(self, pos, Usymbols=stdUsymbols): Uformula[smbl] = f return Uformula + @deprecated(eqIndex_deprecation_msg) def eqIndex(self, pos): + """'diffpy.structure.GeneratorSite.eqIndex' is deprecated and + will be removed in version 4.0.0. + + Please use 'diffpy.structure.GeneratorSite.eq_index' instead. + """ + return self.eq_index(pos) + + def eq_index(self, pos): """Index of the nearest generator equivalent site. Parameters @@ -954,8 +1000,29 @@ def __init__(self, spacegroup, corepos, coreUijs=None, sgoffset=[0, 0, 0], eps=N # Helper function for SymmetryConstraints class. It may be useful # elsewhere therefore its name does not start with underscore. +pruneFormulaDictionary_deprecation_msg = build_deprecation_message( + base, + "pruneFormulaDictionary", + "prune_formula_dictionary", + removal_version, +) + +@deprecated(pruneFormulaDictionary_deprecation_msg) def pruneFormulaDictionary(eqdict): + """'diffpy.structure.pruneFormulaDictionary' is deprecated and will + be removed in version 4.0.0. + + Please use 'diffpy.structure.prune_formula_dictionary' instead. + """ + pruned = {} + for smb, eq in eqdict.items(): + if not is_constant_formula(eq): + pruned[smb] = eq + return pruned + + +def prune_formula_dictionary(eqdict): """Remove constant items from formula dictionary. Parameters @@ -1097,7 +1164,7 @@ def _findConstraints(self): indies = sorted(independent) for indidx in indies: indpos = self.positions[indidx] - formula = gen.positionFormula(indpos, gxyzsymbols) + formula = gen.position_formula(indpos, gxyzsymbols) # formula is empty when indidx is independent if not formula: continue @@ -1105,9 +1172,9 @@ def _findConstraints(self): independent.remove(indidx) self.coremap[genidx].append(indidx) self.poseqns[indidx] = formula - self.Ueqns[indidx] = gen.UFormula(indpos, gUsymbols) + self.Ueqns[indidx] = gen.u_formula(indpos, gUsymbols) # make sure positions and Uijs are consistent with spacegroup - eqidx = gen.eqIndex(indpos) + eqidx = gen.eq_index(indpos) dxyz = gen.eqxyz[eqidx] - indpos self.positions[indidx] += dxyz - dxyz.round() self.Uijs[indidx] = gen.eqUij[eqidx] @@ -1179,7 +1246,7 @@ def positionFormulasPruned(self, xyzsymbols=None): list List of coordinate formula dictionaries. """ - rv = [pruneFormulaDictionary(eqns) for eqns in self.positionFormulas(xyzsymbols)] + rv = [prune_formula_dictionary(eqns) for eqns in self.positionFormulas(xyzsymbols)] return rv def UparSymbols(self): @@ -1248,7 +1315,7 @@ def UFormulasPruned(self, Usymbols=None): List of atom displacement formulas in tuples of ``(U11, U22, U33, U12, U13, U23)``. """ - rv = [pruneFormulaDictionary(eqns) for eqns in self.UFormulas(Usymbols)] + rv = [prune_formula_dictionary(eqns) for eqns in self.UFormulas(Usymbols)] return rv @@ -1263,9 +1330,9 @@ def UFormulasPruned(self, Usymbols=None): site = [0.125, 0.625, 0.13] Uij = [[1, 2, 3], [2, 4, 5], [3, 5, 6]] g = GeneratorSite(sg100, site, Uij=Uij) - fm100 = g.positionFormula(site) + fm100 = g.position_formula(site) print("g = GeneratorSite(sg100, %r)" % site) print("g.positionFormula(%r) = %s" % (site, fm100)) print("g.pparameters =", g.pparameters) print("g.Uparameters =", g.Uparameters) - print("g.UFormula(%r) =" % site, g.UFormula(site)) + print("g.UFormula(%r) =" % site, g.u_formula(site)) diff --git a/tests/test_symmetryutilities.py b/tests/test_symmetryutilities.py index 2bded26..f4294b6 100644 --- a/tests/test_symmetryutilities.py +++ b/tests/test_symmetryutilities.py @@ -41,6 +41,7 @@ nullSpace, position_difference, positionDifference, + prune_formula_dictionary, pruneFormulaDictionary, ) @@ -150,6 +151,13 @@ def test_pruneFormulaDictionary(self): self.assertEqual({"x": "3*y-0.17"}, pruned) return + def test_prune_formula_dictionary(self): + """Check prune_formula_dictionary()""" + fmdict = {"x": "3*y-0.17", "y": "0", "z": "0.13"} + pruned = prune_formula_dictionary(fmdict) + self.assertEqual({"x": "3*y-0.17"}, pruned) + return + def test_isconstantFormula(self): """Check isconstantFormula()""" self.assertFalse(isconstantFormula("x-y+z")) @@ -333,6 +341,30 @@ def test_positionFormula(self): self.assertEqual([], self.g227oc.pparameters) return + def test_position_formula(self): + """Check GeneratorSite.positionFormula()""" + # 117c + self.assertEqual([], self.g117c.pparameters) + self.assertEqual([("x", self.x)], self.g117h.pparameters) + # 143c + pfm143c = self.g143c.position_formula(self.g143c.xyz) + self.assertEqual("+2/3", pfm143c["x"]) + self.assertEqual("+1/3", pfm143c["y"]) + self.assertEqual("z", pfm143c["z"]) + # 143d + x, y, z = self.x, self.y, self.z + pfm143d = self.g143d.position_formula([-x + y, -x, z]) + self.assertEqual("-x+y", pfm143d["x"].replace(" ", "")) + self.assertEqual("-x+1", pfm143d["y"].replace(" ", "")) + self.assertTrue(re.match("[+]?z", pfm143d["z"].strip())) + # 227a + self.assertEqual([], self.g227a.pparameters) + self.assertEqual([], self.g227oa.pparameters) + # 227c + self.assertEqual([], self.g227c.pparameters) + self.assertEqual([], self.g227oc.pparameters) + return + def test_positionFormula_sg209(self): "check positionFormula at [x, 1-x, -x] site of the F432 space group." sg209 = GetSpaceGroup("F 4 3 2") @@ -344,6 +376,17 @@ def test_positionFormula_sg209(self): self.assertEqual("-x+1", pfm["z"].replace(" ", "")) return + def test_position_formula_sg209(self): + "check positionFormula at [x, 1-x, -x] site of the F432 space group." + sg209 = GetSpaceGroup("F 4 3 2") + xyz = [0.05198, 0.94802, -0.05198] + g209e = GeneratorSite(sg209, xyz) + pfm = g209e.position_formula(xyz) + self.assertEqual("x", pfm["x"]) + self.assertEqual("-x+1", pfm["y"].replace(" ", "")) + self.assertEqual("-x+1", pfm["z"].replace(" ", "")) + return + def test_UFormula(self): """Check GeneratorSite.UFormula()""" # Ref: Willis and Pryor, Thermal Vibrations in Crystallography, @@ -450,6 +493,112 @@ def test_UFormula(self): self.assertEqual(rule06, ufm) return + def test_u_formula(self): + """Check GeneratorSite.UFormula()""" + # Ref: Willis and Pryor, Thermal Vibrations in Crystallography, + # Cambridge University Press 1975, p. 104-110 + smbl = ("A", "B", "C", "D", "E", "F") + norule = { + "U11": "A", + "U22": "B", + "U33": "C", + "U12": "D", + "U13": "E", + "U23": "F", + } + rule05 = { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "D", + "U13": "0", + "U23": "0", + } + rule06 = { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "D", + "U13": "E", + "U23": "E", + } + rule07 = { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "D", + "U13": "E", + "U23": "-E", + } + rule15 = { + "U11": "A", + "U22": "B", + "U33": "C", + "U12": "0.5*B", + "U13": "0.5*F", + "U23": "F", + } + rule16 = { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "0.5*A", + "U13": "0", + "U23": "0", + } + rule17 = { + "U11": "A", + "U22": "A", + "U33": "A", + "U12": "0", + "U13": "0", + "U23": "0", + } + rule18 = { + "U11": "A", + "U22": "A", + "U33": "A", + "U12": "D", + "U13": "D", + "U23": "D", + } + ufm = self.g117c.u_formula(self.g117c.xyz, smbl) + self.assertEqual(rule05, ufm) + ufm = self.g117h.u_formula(self.g117h.xyz, smbl) + self.assertEqual(rule07, ufm) + ufm = self.g143a.u_formula(self.g143a.xyz, smbl) + self.assertEqual(rule16, ufm) + ufm = self.g143b.u_formula(self.g143b.xyz, smbl) + self.assertEqual(rule16, ufm) + ufm = self.g143c.u_formula(self.g143c.xyz, smbl) + self.assertEqual(rule16, ufm) + ufm = self.g143d.u_formula(self.g143d.xyz, smbl) + self.assertEqual(norule, ufm) + ufm = self.g164e.u_formula(self.g164e.xyz, smbl) + self.assertEqual(rule15, ufm) + ufm = self.g164f.u_formula(self.g164f.xyz, smbl) + self.assertEqual(rule15, ufm) + ufm = self.g164g.u_formula(self.g164g.xyz, smbl) + self.assertEqual(rule15, ufm) + ufm = self.g164h.u_formula(self.g164h.xyz, smbl) + self.assertEqual(rule15, ufm) + ufm = self.g186c.u_formula(self.g186c.xyz, smbl) + self.assertEqual(rule07, ufm) + ufm = self.g227a.u_formula(self.g227a.xyz, smbl) + self.assertEqual(rule17, ufm) + ufm = self.g227c.u_formula(self.g227c.xyz, smbl) + self.assertEqual(rule18, ufm) + ufm = self.g227oa.u_formula(self.g227oa.xyz, smbl) + self.assertEqual(rule17, ufm) + ufm = self.g227oc.u_formula(self.g227oc.xyz, smbl) + self.assertEqual(rule18, ufm) + # SG 167 in hexagonal and rhombohedral setting + ufm = self.gh167e.u_formula(self.gh167e.xyz, smbl) + self.assertEqual(rule15, ufm) + ufm = self.gr167e.u_formula(self.gr167e.xyz, smbl) + self.assertEqual(rule06, ufm) + return + def test_UFormula_g186c_eqxyz(self): """Check rotated U formulas at the symmetry positions of c-site in 186.""" @@ -532,6 +681,88 @@ def test_UFormula_g186c_eqxyz(self): self.assertEqual(uisod[n], eval(fm, upd)) return + def test_u_formula_g186c_eqxyz(self): + """Check rotated U formulas at the symmetry positions of c-site + in 186.""" + sg186 = GetSpaceGroup(186) + crules = [ + { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "D", + "U13": "E", + "U23": "-E", + }, + { + "U11": "A", + "U22": "2*A-2*D", + "U33": "C", + "U12": "A-D", + "U13": "E", + "U23": "2*E", + }, + { + "U11": "2*A-2*D", + "U22": "A", + "U33": "C", + "U12": "A-D", + "U13": "-2*E", + "U23": "-E", + }, + { + "U11": "A", + "U22": "A", + "U33": "C", + "U12": "D", + "U13": "-E", + "U23": "E", + }, + { + "U11": "A", + "U22": "2*A-2*D", + "U33": "C", + "U12": "A-D", + "U13": "-E", + "U23": "-2*E", + }, + { + "U11": "2*A-2*D", + "U22": "A", + "U33": "C", + "U12": "A-D", + "U13": "2*E", + "U23": "E", + }, + ] + self.assertEqual(6, len(self.g186c.eqxyz)) + gc = self.g186c + for idx in range(6): + self.assertEqual(crules[idx], gc.u_formula(gc.eqxyz[idx], "ABCDEF")) + uiso = numpy.array([[2, 1, 0], [1, 2, 0], [0, 0, 2]]) + eau = ExpandAsymmetricUnit(sg186, [gc.xyz], [uiso]) + for u in eau.expandedUijs: + du = numpy.linalg.norm((uiso - u).flatten()) + self.assertAlmostEqual(0.0, du, 8) + symcon = SymmetryConstraints(sg186, sum(eau.expandedpos, []), sum(eau.expandedUijs, [])) + upd = dict(symcon.Upars) + self.assertEqual(2.0, upd["U110"]) + self.assertEqual(2.0, upd["U330"]) + self.assertEqual(1.0, upd["U120"]) + self.assertEqual(0.0, upd["U130"]) + uisod = { + "U11": 2.0, + "U22": 2.0, + "U33": 2.0, + "U12": 1.0, + "U13": 0.0, + "U23": 0.0, + } + for ufms in symcon.UFormulas(): + for n, fm in ufms.items(): + self.assertEqual(uisod[n], eval(fm, upd)) + return + def test_UFormula_self_reference(self): "Ensure U formulas have no self reference such as U13=0.5*U13." for g in self.generators.values(): @@ -539,6 +770,13 @@ def test_UFormula_self_reference(self): self.assertEqual([], badformulas) return + def test_u_formula_self_reference(self): + "Ensure U formulas have no self reference such as U13=0.5*U13." + for g in self.generators.values(): + badformulas = [(n, fm) for n, fm in g.u_formula(g.xyz).items() if n in fm and n != fm] + self.assertEqual([], badformulas) + return + def test__findUParameters(self): """Check GeneratorSite._findUParameters()""" # by default all Uparameters equal zero, this would fail for NaNs @@ -561,6 +799,11 @@ def test_eqIndex(self): self.assertEqual(13, self.g227oc.eqIndex(self.g227oc.eqxyz[13])) return + def test_eq_index(self): + """Check GeneratorSite.eqIndex()""" + self.assertEqual(13, self.g227oc.eq_index(self.g227oc.eqxyz[13])) + return + # End of class TestGeneratorSite