@@ -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
@@ -201,7 +204,7 @@ def parseLines(self, lines):
201204 elif line .find ("A =" ) == 0 :
202205 xcfg_A = float (line [3 :].split (None , 1 )[0 ])
203206 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 )
205208 xcfg_H0 [i ,j ] = float (line [10 :].split (None , 1 )[0 ])
206209 xcfg_H0_set [i ,j ] = True
207210 elif line .find (".NO_VELOCITY." ) == 0 :
@@ -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 )
@@ -304,7 +279,7 @@ def toLines(self, stru):
304279 emsg = "cannot convert empty structure to XCFG format"
305280 raise StructureFormatError (emsg )
306281 lines = []
307- lines .append ( "Number of particles = %i" % len (stru ) )
282+ lines .append ("Number of particles = %i" % len (stru ))
308283 # figure out length unit A
309284 allxyz = numpy .array ([a .xyz for a in stru ])
310285 lo_xyz = allxyz .min (axis = 0 )
@@ -318,7 +293,7 @@ def toLines(self, stru):
318293 hi_ucvect = max ([numpy .sqrt (numpy .dot (v ,v )) for v in stru .lattice .base ])
319294 if hi_ucvect * p_A < 3.5 :
320295 p_A = numpy .ceil (3.5 / hi_ucvect )
321- lines .append ( "A = %.8g Angstrom" % p_A )
296+ lines .append ("A = %.8g Angstrom" % p_A )
322297 # how much do we need to shift the coordinates?
323298 p_dxyz = numpy .zeros (3 , dtype = float )
324299 for i in range (3 ):
@@ -328,8 +303,8 @@ def toLines(self, stru):
328303 # H0 tensor
329304 for i in range (3 ):
330305 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 ]))
333308 # get out for empty structure
334309 if len (stru ) == 0 : return lines
335310 a_first = stru [0 ]
@@ -347,7 +322,7 @@ def toLines(self, stru):
347322 # add occupancy if any atom has nonunit occupancy
348323 for a in stru :
349324 if a .occupancy != 1.0 :
350- p_auxiliaries .append ( ('occupancy' , 'a.occupancy' ) )
325+ p_auxiliaries .append (('occupancy' , 'a.occupancy' ))
351326 break
352327 # add temperature factor with as many terms as needed
353328 # check whether all temperature factors are zero or isotropic
@@ -363,36 +338,31 @@ def toLines(self, stru):
363338 if p_allUzero :
364339 pass
365340 elif p_allUiso :
366- p_auxiliaries .append ( ('Uiso' , 'a.U[0,0 ]' ) )
341+ p_auxiliaries .append (('Uiso' , 'uflat[0 ]' ))
367342 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 ]' )])
371346 # check if there are off-diagonal elements
372347 allU = numpy .array ([a .U for a in stru ])
373348 if numpy .any (allU [:,0 ,1 ] != 0.0 ):
374- p_auxiliaries .append ( ('U12' , 'a.U[0, 1]' ) )
349+ p_auxiliaries .append (('U12' , 'uflat[ 1]' ))
375350 if numpy .any (allU [:,0 ,2 ] != 0.0 ):
376- p_auxiliaries .append ( ('U13' , 'a.U[0, 2]' ) )
351+ p_auxiliaries .append (('U13' , 'uflat[ 2]' ))
377352 if numpy .any (allU [:,1 ,2 ] != 0.0 ):
378- p_auxiliaries .append ( ('U23' , 'a.U[1,2 ]' ) )
353+ p_auxiliaries .append (('U23' , 'uflat[5 ]' ))
379354 # 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 )
381356 lines .append ("entry_count = %d" % p_entry_count )
382357 # add auxiliaries
383358 for i in range (len (p_auxiliaries )):
384359 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}" ]
388362 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 )
396366 # we are ready to output atoms:
397367 lines .append ("" )
398368 p_element = None
@@ -401,15 +371,58 @@ def toLines(self, stru):
401371 p_element = a .element
402372 lines .append ("%.4f" % AtomicMass .get (p_element , 0.0 ))
403373 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 )
405379 return lines
406380 # End of toLines
407381
408382# End of class P_xcfg
409383
410- # Routines
384+ # Routines -------------------------------------------------------------------
411385
412386def getParser ():
413387 return P_xcfg ()
414388
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+
415428# End of file
0 commit comments