Skip to content

Commit 3626d52

Browse files
committed
Fix parsing of AtomEye XCFG format.
Do not create dummy setter code with exec. Use local helper function instead.
1 parent d5966e1 commit 3626d52

File tree

1 file changed

+61
-47
lines changed

1 file changed

+61
-47
lines changed

src/diffpy/Structure/Parsers/P_xcfg.py

Lines changed: 61 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,16 @@ def parseLines(self, lines):
177177
p_nl = 0
178178
p_auxiliary_re = re.compile(r"^auxiliary\[(\d+)\] =")
179179
p_auxiliary = {}
180+
stru = Structure()
181+
# ignore trailing blank lines
182+
stop = len(lines)
183+
for line in reversed(lines):
184+
if line.strip():
185+
break
186+
stop -= 1
187+
# iterator over the valid data lines
188+
ilines = iter(lines[:stop])
180189
try:
181-
stru = Structure()
182-
# ignore trailing blank lines
183-
stop = len(lines)
184-
while stop>0 and lines[stop-1].strip() == "":
185-
stop -= 1
186-
ilines = iter(lines[:stop])
187190
# read XCFG header
188191
for line in ilines:
189192
p_nl += 1
@@ -222,51 +225,22 @@ def parseLines(self, lines):
222225
for i in range(p_auxnum):
223226
if not i in p_auxiliary:
224227
p_auxiliary[i] = "aux%d" % i
225-
sorted_aux_keys = p_auxiliary.keys()
226-
sorted_aux_keys.sort()
228+
sorted_aux_keys = sorted(p_auxiliary.keys())
227229
if p_auxnum != 0:
228230
stru.xcfg = {
229231
'auxiliaries' : [ p_auxiliary[k]
230232
for k in sorted_aux_keys ]
231233
}
232-
if 6-3*xcfg_NO_VELOCITY+len(p_auxiliary) != xcfg_entry_count:
233-
emsg = ("%d: auxiliary fields " +
234+
ecnt = len(p_auxiliary) + (3 if xcfg_NO_VELOCITY else 6)
235+
if ecnt != xcfg_entry_count:
236+
emsg = ("%d: auxiliary fields are "
234237
"not consistent with entry_count") % p_nl
235238
raise StructureFormatError(emsg)
236239
# define proper lattice
237240
stru.lattice.setLatBase(xcfg_H0)
238-
# build p_assign_atom function to assign entries to proper fields
239-
p_exprs = [ "a.xyz[0]=fields[0]",
240-
"a.xyz[1]=fields[1]",
241-
"a.xyz[2]=fields[2]" ]
242-
if not xcfg_NO_VELOCITY:
243-
p_exprs += [ "a.v=numpy.zeros(3, dtype=float)",
244-
"a.v[0]=fields[3]",
245-
"a.v[1]=fields[4]",
246-
"a.v[2]=fields[5]" ]
247-
for idx in sorted_aux_keys:
248-
prop = p_auxiliary[idx]
249-
col = idx + 6 - 3*xcfg_NO_VELOCITY
250-
if prop == "Uiso":
251-
p_exprs.append("a.Uisoequiv=fields[%d]" % col)
252-
elif re.match(r"^U\d\d$", prop) \
253-
and 1<=int(prop[1])<=3 and 1<=int(prop[2])<=3 :
254-
p_exprs.append("a.anisotropy=True")
255-
i, j = int(prop[1])-1, int(prop[2])-1
256-
if i==j:
257-
p_exprs.append("a.U[%i,%i]=fields[%d]" % (i, j, col) )
258-
else:
259-
p_exprs.append("a.U[%i,%i]=a.U[%i,%i]=fields[%d]" % \
260-
(i, j, j, i, col) )
261-
else:
262-
p_exprs.append( "a.__dict__[%r]=fields[%d]" % \
263-
(prop, col) )
264-
p_assign_expr = "pass; " + "; ".join(p_exprs[3:])
265-
exec "def p_assign_atom(a, fields) : %s" % p_assign_expr
266-
# here we are inside data
241+
# here we are inside the data block
267242
p_element = None
268-
p_nl -= 1
269-
for line in lines[p_nl:stop]:
243+
for line in ilines:
270244
p_nl += 1
271245
words = line.split()
272246
# ignore atom mass
@@ -277,11 +251,12 @@ def parseLines(self, lines):
277251
w = line.strip()
278252
p_element = w[:1].upper() + w[1:].lower()
279253
elif len(words) == xcfg_entry_count and p_element is not None:
280-
fields = [ float(w) for w in words ]
281-
stru.addNewAtom(p_element, fields[:3])
282-
a = stru.getLastAtom()
283-
a.xyz *= xcfg_A
284-
p_assign_atom(a, fields)
254+
fields = [float(w) for w in words]
255+
xyz = [xcfg_A * xi for xi in fields[:3]]
256+
stru.addNewAtom(p_element, xyz=xyz)
257+
a = stru[-1]
258+
_assign_auxiliaries(a, fields, auxiliaries=p_auxiliary,
259+
no_velocity=xcfg_NO_VELOCITY)
285260
else:
286261
emsg = "%d: invalid record" % p_nl
287262
raise StructureFormatError(emsg)
@@ -407,9 +382,48 @@ def toLines(self, stru):
407382

408383
# End of class P_xcfg
409384

410-
# Routines
385+
# Routines -------------------------------------------------------------------
411386

412387
def getParser():
413388
return P_xcfg()
414389

390+
# Local Helpers --------------------------------------------------------------
391+
392+
def _assign_auxiliaries(a, fields, auxiliaries, no_velocity):
393+
"""\
394+
Assing auxiliary properties for Atom object when reading CFG format.
395+
396+
Parameters
397+
----------
398+
a : Atom
399+
The Atom instance for which the auxiliary properties need to be set.
400+
fields : list
401+
Floating point values for the current row of the processed CFG file.
402+
auxiliaries : dict
403+
Dictionary of zero-based indices and names of auxiliary properties
404+
defined in the CFG format.
405+
no_velocity : bool
406+
When `False` set atom velocity `a.v` to `fields[3:6]`.
407+
Use `fields[3:6]` for auxiliary values otherwise.
408+
409+
No return value.
410+
"""
411+
if not no_velocity:
412+
a.v = numpy.asarray(fields[3:6], dtype=float)
413+
auxfirst = 3 if no_velocity else 6
414+
for i, prop in auxiliaries.items():
415+
value = fields[auxfirst + i]
416+
if prop == "Uiso":
417+
a.Uisoequiv = value
418+
elif prop == "Biso":
419+
a.Bisoequiv = value
420+
elif prop[0] in 'BU' and all(d in '123' for d in prop[1:]):
421+
nm = (prop if prop[1] <= prop[2]
422+
else prop[0] + prop[2] + prop[1])
423+
a.anisotropy = True
424+
setattr(a, nm, value)
425+
else:
426+
setattr(a, prop, value)
427+
return
428+
415429
# End of file

0 commit comments

Comments
 (0)