@@ -177,13 +177,16 @@ def parseLines(self, lines):
177
177
p_nl = 0
178
178
p_auxiliary_re = re .compile (r"^auxiliary\[(\d+)\] =" )
179
179
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 ])
180
189
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 ])
187
190
# read XCFG header
188
191
for line in ilines :
189
192
p_nl += 1
@@ -201,7 +204,7 @@ def parseLines(self, lines):
201
204
elif line .find ("A =" ) == 0 :
202
205
xcfg_A = float (line [3 :].split (None , 1 )[0 ])
203
206
elif line .find ("H0(" ) == 0 :
204
- i , j = ( int (line [3 ])- 1 , int (line [5 ])- 1 )
207
+ i , j = (int (line [3 ]) - 1 , int (line [5 ]) - 1 )
205
208
xcfg_H0 [i ,j ] = float (line [10 :].split (None , 1 )[0 ])
206
209
xcfg_H0_set [i ,j ] = True
207
210
elif line .find (".NO_VELOCITY." ) == 0 :
@@ -222,51 +225,22 @@ def parseLines(self, lines):
222
225
for i in range (p_auxnum ):
223
226
if not i in p_auxiliary :
224
227
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 ())
227
229
if p_auxnum != 0 :
228
230
stru .xcfg = {
229
231
'auxiliaries' : [ p_auxiliary [k ]
230
232
for k in sorted_aux_keys ]
231
233
}
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 "
234
237
"not consistent with entry_count" ) % p_nl
235
238
raise StructureFormatError (emsg )
236
239
# define proper lattice
237
240
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
267
242
p_element = None
268
- p_nl -= 1
269
- for line in lines [p_nl :stop ]:
243
+ for line in ilines :
270
244
p_nl += 1
271
245
words = line .split ()
272
246
# ignore atom mass
@@ -277,11 +251,12 @@ def parseLines(self, lines):
277
251
w = line .strip ()
278
252
p_element = w [:1 ].upper () + w [1 :].lower ()
279
253
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 )
285
260
else :
286
261
emsg = "%d: invalid record" % p_nl
287
262
raise StructureFormatError (emsg )
@@ -304,7 +279,7 @@ def toLines(self, stru):
304
279
emsg = "cannot convert empty structure to XCFG format"
305
280
raise StructureFormatError (emsg )
306
281
lines = []
307
- lines .append ( "Number of particles = %i" % len (stru ) )
282
+ lines .append ("Number of particles = %i" % len (stru ))
308
283
# figure out length unit A
309
284
allxyz = numpy .array ([a .xyz for a in stru ])
310
285
lo_xyz = allxyz .min (axis = 0 )
@@ -318,7 +293,7 @@ def toLines(self, stru):
318
293
hi_ucvect = max ([numpy .sqrt (numpy .dot (v ,v )) for v in stru .lattice .base ])
319
294
if hi_ucvect * p_A < 3.5 :
320
295
p_A = numpy .ceil (3.5 / hi_ucvect )
321
- lines .append ( "A = %.8g Angstrom" % p_A )
296
+ lines .append ("A = %.8g Angstrom" % p_A )
322
297
# how much do we need to shift the coordinates?
323
298
p_dxyz = numpy .zeros (3 , dtype = float )
324
299
for i in range (3 ):
@@ -328,8 +303,8 @@ def toLines(self, stru):
328
303
# H0 tensor
329
304
for i in range (3 ):
330
305
for j in range (3 ):
331
- lines .append ( "H0(%i,%i) = %.8g A" % \
332
- ( i + 1 , j + 1 , stru .lattice .base [i ,j ]) )
306
+ lines .append ("H0(%i,%i) = %.8g A" %
307
+ ( i + 1 , j + 1 , stru .lattice .base [i , j ]))
333
308
# get out for empty structure
334
309
if len (stru ) == 0 : return lines
335
310
a_first = stru [0 ]
@@ -347,7 +322,7 @@ def toLines(self, stru):
347
322
# add occupancy if any atom has nonunit occupancy
348
323
for a in stru :
349
324
if a .occupancy != 1.0 :
350
- p_auxiliaries .append ( ('occupancy' , 'a.occupancy' ) )
325
+ p_auxiliaries .append (('occupancy' , 'a.occupancy' ))
351
326
break
352
327
# add temperature factor with as many terms as needed
353
328
# check whether all temperature factors are zero or isotropic
@@ -363,36 +338,31 @@ def toLines(self, stru):
363
338
if p_allUzero :
364
339
pass
365
340
elif p_allUiso :
366
- p_auxiliaries .append ( ('Uiso' , 'a.U[0,0 ]' ) )
341
+ p_auxiliaries .append (('Uiso' , 'uflat[0 ]' ))
367
342
else :
368
- p_auxiliaries .extend ([ ('U11' , 'a.U[0, 0]' ),
369
- ('U22' , 'a.U[1,1 ]' ),
370
- ('U33' , 'a.U[2,2 ]' ) ])
343
+ p_auxiliaries .extend ([('U11' , 'uflat[ 0]' ),
344
+ ('U22' , 'uflat[4 ]' ),
345
+ ('U33' , 'uflat[8 ]' )])
371
346
# check if there are off-diagonal elements
372
347
allU = numpy .array ([a .U for a in stru ])
373
348
if numpy .any (allU [:,0 ,1 ] != 0.0 ):
374
- p_auxiliaries .append ( ('U12' , 'a.U[0, 1]' ) )
349
+ p_auxiliaries .append (('U12' , 'uflat[ 1]' ))
375
350
if numpy .any (allU [:,0 ,2 ] != 0.0 ):
376
- p_auxiliaries .append ( ('U13' , 'a.U[0, 2]' ) )
351
+ p_auxiliaries .append (('U13' , 'uflat[ 2]' ))
377
352
if numpy .any (allU [:,1 ,2 ] != 0.0 ):
378
- p_auxiliaries .append ( ('U23' , 'a.U[1,2 ]' ) )
353
+ p_auxiliaries .append (('U23' , 'uflat[5 ]' ))
379
354
# count entries
380
- p_entry_count = 6 - 3 * p_NO_VELOCITY + len (p_auxiliaries )
355
+ p_entry_count = ( 3 if p_NO_VELOCITY else 6 ) + len (p_auxiliaries )
381
356
lines .append ("entry_count = %d" % p_entry_count )
382
357
# add auxiliaries
383
358
for i in range (len (p_auxiliaries )):
384
359
lines .append ("auxiliary[%d] = %s [au]" % (i , p_auxiliaries [i ][0 ]))
385
- # now define p_entry_line function for representing atom properties
386
- p_exprs = [ "def p_entry_line(a, p_A, p_dxyz):" ,
387
- " fields = list( a.xyz/p_A+p_dxyz )" ]
360
+ # now define entry format efmt for representing atom properties
361
+ fmwords = ["{pos[0]:.8g}" , "{pos[1]:.8g}" , "{pos[2]:.8g}" ]
388
362
if not p_NO_VELOCITY :
389
- p_exprs .append ( \
390
- " fields += [ a.v[0], a.v[1], a.v[2] ]" )
391
- p_exprs += [" fields += [ " +
392
- "," .join ([e for p ,e in p_auxiliaries ]) + " ]" ,
393
- " line = ' '.join([ '%.8g' % x for x in fields ])" ,
394
- " return line" ]
395
- exec "\n " .join (p_exprs )
363
+ fmwords += ["{v[0]:.8g}" , "{v[1]:.8g}" , "{v[2]:.8g}" ]
364
+ fmwords += (('{' + e + ':.8g}' ) for p , e in p_auxiliaries )
365
+ efmt = ' ' .join (fmwords )
396
366
# we are ready to output atoms:
397
367
lines .append ("" )
398
368
p_element = None
@@ -401,15 +371,58 @@ def toLines(self, stru):
401
371
p_element = a .element
402
372
lines .append ("%.4f" % AtomicMass .get (p_element , 0.0 ))
403
373
lines .append (p_element )
404
- lines .append (p_entry_line (a , p_A , p_dxyz ))
374
+ pos = a .xyz / p_A + p_dxyz
375
+ v = None if p_NO_VELOCITY else a .v
376
+ uflat = numpy .ravel (a .U )
377
+ entry = efmt .format (pos = pos , v = v , uflat = uflat , a = a )
378
+ lines .append (entry )
405
379
return lines
406
380
# End of toLines
407
381
408
382
# End of class P_xcfg
409
383
410
- # Routines
384
+ # Routines -------------------------------------------------------------------
411
385
412
386
def getParser ():
413
387
return P_xcfg ()
414
388
389
+ # Local Helpers --------------------------------------------------------------
390
+
391
+ def _assign_auxiliaries (a , fields , auxiliaries , no_velocity ):
392
+ """\
393
+ Assing auxiliary properties for Atom object when reading CFG format.
394
+
395
+ Parameters
396
+ ----------
397
+ a : Atom
398
+ The Atom instance for which the auxiliary properties need to be set.
399
+ fields : list
400
+ Floating point values for the current row of the processed CFG file.
401
+ auxiliaries : dict
402
+ Dictionary of zero-based indices and names of auxiliary properties
403
+ defined in the CFG format.
404
+ no_velocity : bool
405
+ When `False` set atom velocity `a.v` to `fields[3:6]`.
406
+ Use `fields[3:6]` for auxiliary values otherwise.
407
+
408
+ No return value.
409
+ """
410
+ if not no_velocity :
411
+ a .v = numpy .asarray (fields [3 :6 ], dtype = float )
412
+ auxfirst = 3 if no_velocity else 6
413
+ for i , prop in auxiliaries .items ():
414
+ value = fields [auxfirst + i ]
415
+ if prop == "Uiso" :
416
+ a .Uisoequiv = value
417
+ elif prop == "Biso" :
418
+ a .Bisoequiv = value
419
+ elif prop [0 ] in 'BU' and all (d in '123' for d in prop [1 :]):
420
+ nm = (prop if prop [1 ] <= prop [2 ]
421
+ else prop [0 ] + prop [2 ] + prop [1 ])
422
+ a .anisotropy = True
423
+ setattr (a , nm , value )
424
+ else :
425
+ setattr (a , prop , value )
426
+ return
427
+
415
428
# End of file
0 commit comments