Skip to content

Commit ac70c72

Browse files
committed
finish work on Phase & HAP constraints from Phase/Transform for magnetic
1 parent 937f0a5 commit ac70c72

3 files changed

Lines changed: 147 additions & 28 deletions

File tree

GSASII/GSASIIconstrGUI.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,37 +1910,21 @@ def SetUniqAj(pId,iA,SGData):
19101910
'Constraint Gen. Problem',style=wx.ICON_INFORMATION).ShowModal()
19111911

19121912
# create constraints on lattice parameters between phases
1913-
opId = oldPhase['pId']
1914-
npId = newPhase['pId']
19151913
oRanId = oldPhase['ranId']
19161914
nRanId = newPhase['ranId']
1917-
debug = True
1918-
if debug: # debug
1919-
fmt6 = lambda x: ', '.join([f'{i:.6f}' for i in x])
1920-
fmt4 = lambda x: ', '.join([f'{i:.6f}' for i in x])
1921-
print('old A*',fmt6(G2lat.cell2A(oldPhase['General']['Cell'][1:7])))
1922-
print('new A*',fmt6(G2lat.cell2A(newPhase['General']['Cell'][1:7])))
1923-
print('old cell',fmt4(oldPhase['General']['Cell'][1:7]))
1924-
print('new cell',fmt4(newPhase['General']['Cell'][1:7]))
19251915
constraints['Phase'] += G2lat.GenCellConstraints(Trans,oRanId,nRanId,
19261916
G2lat.cell2A(oldPhase['General']['Cell'][1:7]),
19271917
oldPhase['General']['SGData'],
1928-
newPhase['General']['SGData'],debug)
1918+
newPhase['General']['SGData'],debug=False)
19291919

19301920
# create constraints on HAP Scale, Isotropic size and mustrain
19311921
# (anisotropic constraints would be more complex)
1932-
detTrans = np.abs(nl.det(Trans)) # volume ratio
19331922
UseList = newPhase['Histograms']
1934-
for hId,hist in enumerate(UseList): #HAP - seems OK
1935-
ohapkey = f'{opId}:{hId}:'
1936-
nhapkey = f'{npId}:{hId}:'
1937-
IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1938-
DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1939-
constraints['HAP'].append([DepCons,IndpCon,None,None,'e'])
1940-
for name in ['Size;i','Mustrain;i']:
1941-
IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1942-
DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1943-
constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1923+
Histograms, Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1924+
for hId,hist in enumerate(UseList):
1925+
hRanId = Histograms[hist]['ranId']
1926+
constrList = G2lat.GenHAPConstraints(Trans,oRanId,nRanId,hRanId)
1927+
constraints['HAP'] += constrList
19441928

19451929
#### Rigid bodies #############################################################
19461930
resRBsel = None

GSASII/GSASIIlattice.py

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,9 @@ def fmtCellConstraints(cellConstr):
670670
# cellXformRelations = fmtCellConstraints(GenerateCellConstraints())
671671

672672
def GenAtomConstraints(oldPhase,newPhase,atCodes,Trans):
673-
'''Generate the constraints on generated atoms between oldPhase and newPhase
674-
where atCodes links new atoms to the original atoms
673+
'''Generate the constraints on generated atoms between oldPhase
674+
and newPhase where atCodes links new atoms to the original atoms.
675+
uUsed for a magnetic phase transformed generated from oldPhase.
675676
676677
:param oldPhase: the phase object for the original phase
677678
:param newPhase: the phase object for the newly created phase
@@ -731,9 +732,104 @@ def GenAtomConstraints(oldPhase,newPhase,atCodes,Trans):
731732
constraints.append([DepCons,IndpCon,None,None,'e'])
732733
return constraints,message
733734

735+
def MatchGenAtomConstraints(oldPhase,newPhase,Trans,U,V):
736+
'''Generate the constraints on generated atoms in newPhase similar to
737+
:func:`GenAtomConstraints` but without pointers to relate the atoms.
738+
The atom positions are regenerated from the original phase and then
739+
are matched to the magnetic positions in newPhase. The positions
740+
generated here are what are then used for the new atom positions.
741+
742+
:param oldPhase: the phase object for the original phase
743+
:param newPhase: the phase object for the newly created phase
744+
:param np.array Trans: the transformation matrix
745+
:param np.array U: the offset vector, subtracted before transformation matrix
746+
:param np.array V: the offset vector, added after transformation matrix
747+
:returns: (constraints, message) where
748+
* constraints is a list of constraints that should be added to the
749+
Phase section of the constraints dict.
750+
* message is a message to display or None
751+
'''
752+
from . import GSASIIobj as G2obj
753+
constraints = []
754+
message = ''
755+
opRanId = oldPhase['ranId']
756+
oAtoms = oldPhase['Atoms']
757+
npRanId = newPhase['ranId']
758+
cx,ct,cs,cia = newPhase['General']['AtomPtrs']
759+
nAtoms = newPhase['Atoms']
760+
nSGData = newPhase['General']['SGData']
761+
xnames = ['dAx','dAy','dAz']
762+
atCodes = None
763+
# constraints on matching atom params between phases
764+
fmt4 = lambda x: ', '.join([f'{i:.6f}' for i in x])
765+
# regenerate and potentially relocate transformed atoms
766+
tmpPhase = copy.deepcopy(newPhase)
767+
tmpPhase,tmpCodes = TransformPhase(oldPhase,tmpPhase,Trans,
768+
np.array(U),np.array(V),True)
769+
Amat = cell2AB(newPhase['General']['Cell'][1:7])[0]
770+
atCodes = len(nAtoms)*[None]
771+
for iN,natom in enumerate(nAtoms):
772+
nxyz = np.array(natom[cx:cx+3])
773+
#print('searching to match new atom',fmt4(nxyz))
774+
for iT,tatom in enumerate(tmpPhase['Atoms']):
775+
if natom[ct] != tatom[ct]: continue
776+
txyz = np.array(tatom[cx:cx+3])
777+
#print('generated atom pos',fmt4(txyz))
778+
for oXYZ,osym,cell,spn in G2spc.GenAtom(txyz,
779+
tmpPhase['General']['SGData'],False,[],True):
780+
dist = np.sqrt(np.sum(np.inner(Amat,(oXYZ-nxyz))**2))
781+
#print(oXYZ, oXYZ-nxyz, np.allclose(oXYZ,nxyz),dist)
782+
if dist < 0.1: # allow a bit of distance (0.1A) between generated and CIF positions
783+
# found a matching atom, replace coordinates & save code
784+
atCodes[iN] = tmpCodes[iT]
785+
natom[cx:cx+3] = tatom[cx:cx+3]
786+
#print(' matched at',fmt4(tatom[cx:cx+3]))
787+
break
788+
if atCodes[iN]: break
789+
else:
790+
message += f'No matching atom found for {natom[ct]} at {fmt4(natom[cx:cx+3])}\n'
791+
792+
for ia,code in enumerate(atCodes): # iterate of matching pairs of atoms
793+
if not ia and nAtoms[ia][cia] == 'A' and 'ADP' not in message:
794+
message += 'Anisotropic ADP constraints not yet developed\n'
795+
if code is None: continue
796+
siteSym = G2spc.SytSym(nAtoms[ia][cx:cx+3],nSGData)[0]
797+
CSX = G2spc.GetCSxinel(siteSym)
798+
item = code.split('+')[0]
799+
iAtOrg,opr = item.split(':')
800+
iAtOrg = int(iAtOrg)
801+
Nop = abs(int(opr))%100-1
802+
if '-' in opr:
803+
Nop *= -1
804+
Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
805+
if Nop < 0: #inversion
806+
Opr *= -1
807+
XOpr = np.inner(Opr,Trans)
808+
invOpr = nl.inv(XOpr)
809+
for i,ix in enumerate(list(CSX[0])):
810+
if not ix:
811+
continue
812+
name = xnames[i]
813+
IndpCon = [1.0,G2obj.G2VarObj([npRanId,None,name,nAtoms[ia][-1]])]
814+
DepCons = []
815+
for iop,opval in enumerate(invOpr[i]):
816+
oldVar = G2obj.G2VarObj([opRanId,None,xnames[iop],oAtoms[iAtOrg][-1]])
817+
if abs(opval) > 1e-6:
818+
DepCons.append([float(opval),oldVar])
819+
if len(DepCons) == 1: # use equivalence if possible
820+
constraints.append([DepCons[0],IndpCon,None,None,'e'])
821+
elif len(DepCons) > 1:
822+
IndpCon[0] = -1.
823+
constraints.append([IndpCon]+DepCons+[0.0,None,'c'])
824+
for name in ['Afrac','AUiso']:
825+
IndpCon = [1.0,G2obj.G2VarObj([npRanId,None,name,nAtoms[ia][-1]])]
826+
DepCons = [1.0,G2obj.G2VarObj([opRanId,None,xnames[iop],oAtoms[iAtOrg][-1]])]
827+
constraints.append([DepCons,IndpCon,None,None,'e'])
828+
return constraints,message
829+
734830
def GenCellConstraints(Trans,oRanId,nRanId,origA,oSGLaue,nSGLaue,debug=False):
735-
'''Generate the constraints between two unit cells constants for a phase transformed
736-
by matrix Trans.
831+
'''Generate the constraints between two unit cells constants for
832+
a magnetic phase transformed by matrix Trans.
737833
738834
:param np.array Trans: a 3x3 direct cell transformation matrix where,
739835
Trans = np.array([ [2/3, 4/3, 1/3], [-1, 0, 0], [-1/3, -2/3, 1/3] ])
@@ -746,6 +842,7 @@ def GenCellConstraints(Trans,oRanId,nRanId,origA,oSGLaue,nSGLaue,debug=False):
746842
:param dict nSGLaue: space group info for transformed phase
747843
:param bool debug: If true, the constraint input is used to compute and print A*
748844
and from that the direct cell for the transformed phase.
845+
:returns: a list of generated constraints
749846
'''
750847
from . import GSASIIobj as G2obj
751848
Anew = []
@@ -781,6 +878,33 @@ def GenCellConstraints(Trans,oRanId,nRanId,origA,oSGLaue,nSGLaue,debug=False):
781878
print('xfm cell',fmt4(A2cell(Anew)))
782879
return constrList
783880

881+
def GenHAPConstraints(Trans,oRanId,nRanId,hRanId):
882+
'''Generate the HAP constraints between two phase constants
883+
for a phase transformed by matrix Trans.
884+
885+
:param np.array Trans: a 3x3 direct cell transformation matrix where,
886+
Trans = np.array([ [2/3, 4/3, 1/3], [-1, 0, 0], [-1/3, -2/3, 1/3] ])
887+
(for a' = 2/3a + 4/3b + 1/3c; b' = -a; c' = -1/3, -2/3, 1/3)
888+
:param int oRanId: Random id for the original phase
889+
:param int nRanId: Random id for the transformed phase to be constrained from
890+
original phase
891+
:param str hRanId: histogram Random Id
892+
:returns: a list of generated constraints
893+
'''
894+
from . import GSASIIobj as G2obj
895+
constrList = []
896+
detTrans = np.abs(nl.det(Trans)) # volume ratio
897+
# Phase fractions (note volume term)
898+
IndpCon = [1.0,G2obj.G2VarObj([oRanId,hRanId,'Scale',None])]
899+
DepCons = [detTrans,G2obj.G2VarObj([nRanId,hRanId,'Scale',None])]
900+
constrList.append([IndpCon,DepCons,None,None,'e'])
901+
# Isotropic size and mustrain (anisotropic constraints are messy)
902+
for name in ['Size;i','Mustrain;i']:
903+
IndpCon = [1.0,G2obj.G2VarObj([oRanId,hRanId,name,None])]
904+
DepCons = [1.0,G2obj.G2VarObj([nRanId,hRanId,name,None])]
905+
constrList.append([IndpCon,DepCons,None,None,'e'])
906+
return constrList
907+
784908
def cellUnique(SGData):
785909
'''Returns the indices for the unique A tensor terms
786910
based on the Laue class.
@@ -844,7 +968,17 @@ def TransformU6(U6,Trans):
844968
return UijtoU6(Uij)
845969

846970
def ExpandCell(Atoms,atCodes,cx,Trans):
847-
Unit = [int(max(abs(np.array(unit)))-1) for unit in Trans.T]
971+
'''Expand a list of atoms to an new unit cell.
972+
973+
:param list Atoms: atoms array as found in a Phase object
974+
:param list atCodes:list of codes that describe symmetry applied to
975+
generate atom from original position
976+
:param int cx: pointer to location of x coord in Atoms
977+
:param np.array Trans: 3x3 transformation matrix
978+
:returns: newAtoms,Codes where newAtoms is the expanded list of
979+
atoms and Codes is an expanded and updated version of atCodes
980+
'''
981+
Unit = [int(max(abs(np.array(unit)))-1) for unit in np.array(Trans).T]
848982
nUnit = (Unit[0]+1)*(Unit[1]+1)*(Unit[2]+1)
849983
Ugrid = np.mgrid[0:Unit[0]+1,0:Unit[1]+1,0:Unit[2]+1]
850984
Ugrid = np.reshape(Ugrid,(3,nUnit)).T
@@ -869,7 +1003,7 @@ def TransformPhase(oldPhase,newPhase,Trans,Uvec,Vvec,ifMag,Force=True):
8691003
:param oldPhase: dict G2 phase info for old phase
8701004
:param newPhase: dict G2 phase info for new phase; with new cell & space group
8711005
atoms are from oldPhase & will be transformed
872-
:param Trans: lattice transformation matrix M
1006+
:param np.array Trans: lattice transformation matrix M
8731007
:param Uvec: array parent coordinates transformation vector U
8741008
:param Vvec: array child coordinate transformation vector V
8751009
:param ifMag: bool True if convert to magnetic phase;

GSASII/GSASIIphsGUI.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ def OnTest(event):
423423

424424
def OnMag(event):
425425
self.ifMag = True
426+
self.ifConstr = True
426427
self.BNSlatt = self.SGData['SGLatt']
427428
G2spc.SetMagnetic(self.SGData)
428429
wx.CallAfter(self.Draw)

0 commit comments

Comments
 (0)