Coverage for /Users/Newville/Codes/xraylarch/larch/xafs/feffrunner.py: 12%
232 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1import sys
2import os
3from os.path import realpath, isdir, isfile, join, basename, dirname, abspath
4import glob
5from shutil import copy, move
6import subprocess
7import time
8import re
9from optparse import OptionParser
10from subprocess import Popen, PIPE
12from larch import Group, isNamedClass
13from larch.utils import isotime, bytes2str, uname, bindir, get_cwd
15def find_exe(exename):
16 if uname == 'win' and not exename.endswith('.exe'):
17 exename = "%s.exe" % exename
18 exefile = join(bindir, exename)
19 if os.path.exists(exefile) and os.access(exefile, os.X_OK):
20 return exefile
22class FeffRunner(Group):
23 """
24 A Larch plugin for managing calls to the feff85exafs stand-alone executables.
25 This plugin does not manage the output of Feff. See feffpath() and other tools.
27 Methods:
28 run -- run one or more parts of feff
30 feff = feffrunner(feffinp='path/to/feff.inp')
31 feff.run() to run feff monolithically
32 feff.run('rdinp')
33 feff.run('xsph')
34 and so on to run individual parts of feff
35 ('rdinp', 'pot', 'opconsat', 'xsph', 'pathfinder', 'genfmt', 'ff2x')
37 If the symbol _xafs._feff_executable is set to a Feff executable,
38 it can be run by doing
40 feff = feffrunner(feffinp='path/to/feff6.inp')
41 feff.run(None)
43 run returns None if feff ran successfully, otherwise it
44 returns an Exception with a useful message
46 Other versions of feff in the execution path can also be
47 handled, with the caveat that the executable begins with
48 'feff', i.e. 'feff6', 'feff7', etc.
50 feff = feffrunner(feffinp='path/to/feff6.inp')
51 feff.run('feff6')
53 If the value of the feffinp attribute is a file with a
54 basename other than 'feff.inp', that file will be renamed to
55 'feff.inp' and care will be taken to preserve an existing
56 file by that name.
58 Attributes:
59 folder -- the folder to run in, containing feff.inp file
60 feffinp -- the feff.inp file, absolute or relative to `folder`
61 resolved -- the fully resolved path to the most recently run executable
62 verbose -- write screen messages if True
63 mpse -- run opconsat after pot if True
65 """
67 Feff8l_modules = ('rdinp', 'pot', 'xsph', 'pathfinder', 'genfmt', 'ff2x')
69 def __init__(self, feffinp='feff.inp', folder='.', verbose=True, _larch=None,
70 message_writer=None, **kws):
71 kwargs = dict(name='Feff runner')
72 kwargs.update(kws)
73 Group.__init__(self, **kwargs)
74 self._larch = _larch
76 if folder is None:
77 folder = '.'
78 self.folder = folder
79 self.feffinp = feffinp
80 self.verbose = verbose
81 self.message_writer = message_writer
82 self.mpse = False
83 self.resolved = None
84 self.threshold = []
85 self.chargetransfer = []
87 def __repr__(self):
88 fullfile = os.path.join(self.folder, self.feffinp)
89 return '<External Feff Group: %s>' % fullfile
91 def run(self, feffinp=None, folder=None, exe='feff8l'):
92 """
93 Make system call to run one or more of the stand-alone executables,
94 writing a log file to the folder containing the input file.
96 """
97 if folder is not None:
98 self.folder = folder
100 if feffinp is not None:
101 self.feffinp = feffinp
103 if self.feffinp is None:
104 raise Exception("no feff.inp file was specified")
106 savefile = '.save_.inp'
107 here = abspath(get_cwd())
108 os.chdir(abspath(self.folder))
110 feffinp_dir, feffinp_file = os.path.split(self.feffinp)
111 feffinp_dir = dirname(self.feffinp)
112 if len(feffinp_dir) > 0:
113 os.chdir(feffinp_dir)
115 if not isfile(feffinp_file):
116 raise Exception("feff.inp file '%s' could not be found" % feffinp_file)
118 if exe in (None, 'feff8l'):
119 for module in self.Feff8l_modules:
120 os.chdir(here)
121 self.run(exe=module)
122 return
124 #
125 # exe is set, find the corresponding executable file
126 ## find program to run:
127 program = None
128 if exe in self.Feff8l_modules:
129 exe = "feff8l_%s" % exe
131 resolved_exe = find_exe(exe)
132 if resolved_exe is not None:
133 program = resolved_exe
135 else:
136 getsym = self._larch.symtable.get_symbol
137 try:
138 program = getsym('_xafs._feff8_executable')
139 except (NameError, AttributeError) as exc:
140 try:
141 program = getsym('_xafs._feff_executable')
142 except (NameError, AttributeError) as exc:
143 program = None
145 if program is not None:
146 if not os.access(program, os.X_OK):
147 program = None
149 if program is None: # Give up!
150 os.chdir(here)
151 raise Exception("'%s' executable cannot be found" % exe)
153 ## preserve an existing feff.inp file if this is not called feff.inp
154 if feffinp_file != 'feff.inp':
155 if isfile('feff.inp'):
156 copy('feff.inp', savefile)
157 copy(feffinp_file, 'feff.inp')
159 _, logname = os.path.split(program)
160 if logname.endswith('.exe'):
161 logname = logname[:4]
163 log = 'feffrun_%s.log' % logname
165 if isfile(log):
166 os.unlink(log)
168 f = open(log, 'a')
169 header = "\n======== running Feff module %s ========\n" % exe
171 def write(msg):
172 msg = bytes2str(msg)
173 msg = " : {:s}\n".format(msg.strip().rstrip())
174 if self._larch is not None:
175 self._larch.writer.write(msg)
176 else:
177 sys.stdout.write(msg)
179 if self.verbose:
180 write(header)
181 f.write(header)
182 process=subprocess.Popen(program, shell=False,
183 stdout=subprocess.PIPE,
184 stderr=subprocess.STDOUT)
185 flag = False
186 thislist = []
187 while True:
188 if process.returncode is None:
189 process.poll()
190 time.sleep(0.01)
191 line = bytes2str(process.stdout.readline())
192 if not line:
193 break
194 if self.verbose:
195 write(line)
196 if callable(self.message_writer):
197 self.message_writer(line)
199 ## snarf threshold energy
200 pattern = re.compile('mu_(new|old)=\s+(-?\d\.\d+)')
201 match = pattern.search(line)
202 if match is not None:
203 self.threshold.append(match.group(2))
204 ## snarf charge transfer
205 if line.strip().startswith('Charge transfer'):
206 thislist = []
207 flag = True
208 elif line.strip().startswith('SCF ITERATION'):
209 self.chargetransfer.append(list(thislist))
210 flag = False
211 elif line.strip().startswith('Done with module 1'):
212 self.chargetransfer.append(list(thislist))
213 flag = False
214 elif flag:
215 this = line.split()
216 thislist.append(this[1])
217 f.write(line)
218 f.close
220 if isfile(savefile):
221 move(savefile, 'feff.inp')
222 os.chdir(here)
223 return None
225######################################################################
226def feffrunner(folder=None, feffinp=None, verbose=True, _larch=None, **kws):
227 """
228 Make a FeffRunner group given a folder containing a baseline calculation
229 """
230 return FeffRunner(folder=folder, feffinp=feffinp, verbose=verbose,
231 _larch=_larch, **kws)
233def feff6l(feffinp='feff.inp', folder='.', verbose=True, _larch=None, **kws):
234 """
235 run a Feff6l calculation for a feff.inp file in a folder
237 Arguments:
238 ----------
239 feffinp (str): name of feff.inp file to use ['feff.inp']
240 folder (str): folder for calculation, containing 'feff.inp' file ['.']
241 verbose (bool): whether to print out extra messages [False]
243 Returns:
244 --------
245 instance of FeffRunner
247 Notes:
248 ------
249 many results data files are generated in the Feff working folder
250 """
251 feffrunner = FeffRunner(folder=folder, feffinp=feffinp, verbose=verbose,
252 _larch=_larch, **kws)
253 exe = find_exe('feff6l')
254 feffrunner.run(exe=exe)
255 return feffrunner
257def feff6l_cli():
258 """run feff6l as command line program
259 """
261 usage = """usage: %prog [options] folder(s)
263run feff6l on one or more folders containing feff.inp files
264or on an input file in the current folder
266Examples:
267 feff6l Structure1 Structure2
269 feff6l feff_Cu2O.inp
271"""
273 parser = OptionParser(usage=usage, prog="feff6l",
274 version="Feff6L version 6L.02")
276 FEFFINP = 'feff.inp'
277 (options, args) = parser.parse_args()
278 if len(args) == 0:
279 args = ['.']
281 curdir = abspath(get_cwd())
282 for arg in args:
283 if os.path.isfile(arg):
284 feff6l(feffinp=arg)
285 elif os.path.isdir(arg):
286 feffinp = os.path.join(arg, 'feff.inp')
287 if os.path.exists(feffinp):
288 os.chdir(abspath(arg))
289 feff6l(folder=arg)
290 else:
291 msg = "Could not find feff.inp file in folder '{:s}'"
292 sys.stdout.write(msg.format(abspath(os.curdir)))
293 os.chdir(curdir)
296def feff8l(feffinp='feff.inp', folder='.', module=None, verbose=True, _larch=None, **kws):
297 """
298 run a Feff8l calculation for a feff.inp file in a folder
300 Arguments:
301 ----------
302 feffinp (str): name of feff.inp file to use ['feff.inp']
303 folder (str): folder for calculation, containing 'feff.inp' file ['.']
304 module (None or str): module of Feff8l to run [None -- run all]
305 verbose (bool): whether to print out extra messages [False]
307 Returns:
308 --------
309 instance of FeffRunner
311 Notes:
312 ------
313 many results data files are generated in the Feff working folder
314 """
315 feffrunner = FeffRunner(folder=folder, feffinp=feffinp, verbose=verbose,
316 _larch=_larch, **kws)
317 feffrunner.run(exe='feff8l')
318 return feffrunner
321def feff8l_cli():
322 """run feff8l as a command line program to run all or some of
323 feff8l_rdinp
324 feff8l_pot
325 feff8l_xsph
326 feff8l_pathfinder
327 feff8l_genfmt
328 feff8l_ff2x
329 """
331 usage = """usage: %prog [options] folder(s)
333run feff8l (or selected modules) on one
334or more folders containing feff.inp files.
336Example:
337 feff8l --no-ff2chi Structure1 Structure2
338"""
340 parser = OptionParser(usage=usage, prog="feff8l",
341 version="Feff85L for EXAFS version 8.5L, build 001")
342 parser.add_option("-q", "--quiet", dest="quiet", action="store_true",
343 default=False, help="set quiet mode, default=False")
344 parser.add_option("--no-pot", dest="no_pot", action="store_true",
345 default=False, help="do not run POT module")
346 parser.add_option("--no-phases", dest="no_phases", action="store_true",
347 default=False, help="do not run XSPH module")
348 parser.add_option("--no-paths", dest="no_paths", action="store_true",
349 default=False, help="do not run PATHFINDER module")
350 parser.add_option("--no-genfmt", dest="no_genfmt", action="store_true",
351 default=False, help="do not run GENFMT module")
352 parser.add_option("--no-ff2chi", dest="no_ff2chi", action="store_true",
353 default=False, help="do not run FF2CHI module")
356 FEFFINP = 'feff.inp'
357 (options, args) = parser.parse_args()
359 verbose = not options.quiet
360 modules = ['rdinp', 'pot', 'xsph', 'pathfinder', 'genfmt', 'ff2x']
361 if options.no_pot:
362 modules.remove('pot')
363 if options.no_phases:
364 modules.remove('xsph')
365 if options.no_paths:
366 modules.remove('pathfinder')
367 if options.no_genfmt:
368 modules.remove('genfmt')
369 if options.no_ff2chi:
370 modules.remove('ff2x')
372 if len(args) == 0:
373 args = ['.']
376 def run_feff8l(modules):
377 """ run selected modules of Feff85L """
378 try:
379 logfile = open('feff8l.log', 'w+')
380 except:
381 logfile = tempfile.NamedTemporaryFile(prefix='feff8l')
383 def write(msg):
384 msg = bytes2str(msg)
385 sys.stdout.write(msg)
386 logfile.write(msg)
388 write("#= Feff85l %s\n" % isotime())
389 for mod in modules:
390 write("#= Feff85l %s module\n" % mod)
391 exe = find_exe('feff8l_%s' % mod)
392 proc = Popen(exe, stdout=PIPE, stderr=PIPE)
393 while True:
394 msg = bytes2str(proc.stdout.read())
395 if msg == '':
396 break
397 write(msg)
398 while True:
399 msg = bytes2str(proc.stderr.read())
400 if msg == '':
401 break
402 write("#ERROR %s" % msg)
403 logfile.flush()
404 for fname in glob.glob('log*.dat'):
405 try:
406 os.unlink(fname)
407 except IOError:
408 pass
409 write("#= Feff85l done %s\n" % isotime())
411 for dirname in args:
412 if os.path.exists(dirname) and os.path.isdir(dirname):
413 thisdir = abspath(os.curdir)
414 os.chdir(dirname)
415 if os.path.exists(FEFFINP) and os.path.isfile(FEFFINP):
416 run_feff8l(modules)
417 else:
418 msg = "Could not find feff.inp file in folder '{:s}'"
419 sys.stdout.write(msg.format(abspath(os.curdir)))
420 os.chdir(thisdir)
421 else:
422 print("Could not find folder '{:s}'".format(dirname))