"""
ESjob clas - combines EShandler electronic structure file functionality + epsJob class functionality.
22/02/21 Basics working for file setting & epsJob functionatliy (including backend method dev)
19/02/21 Started dev.
"""
from pathlib import Path
import epsman as em # For base class
import epsman.elecStructure.ESclass as ESclass # Electronic structure methods
# With EShandler as base
# class ESjob(ESclass.EShandler, em.epsJob):
# Without EShandler - run, instead, on a per-host basis
[docs]class ESjob(em.epsJob):
"""
Class to wrap epsJob + EShandler functionality.
Note this currently assume a SINGLE electronic structure file as the base for a job.
For multiple jobs with different ES files (e.g. bond-scans), this should be wrapped or subclassed - TBD.
10/06/21: adding ES file handling & info functions following recent OCS run testing, first version of:
- setOrbInfoPD, orbInfoSummary (source _orbInfo.py) to pull orbital/molecule info from ES file (part of EShandler class).
- setChannel, setePSinputs, genSymList, convertSymList, writeInputConf (source _ePSsetup.py) for setting up ePS parameters based on ES file + additional inputs (part of EShandler class). Needs further work, may also move to ESjob class in future...?
- setJobInputConfig, symTest, setSymsFromFiles (source _ePSsetup), additional ePS job setup routines... need some work!
19/02/21: now init with epsman.epsJob class as parent, so can implement existing file IO methods.
"""
from ._ePSsetup import setJobInputConfig, symTest, setSymsFromFiles
def __init__(self, fileName = None, fileBase = None, outFile = None, master = 'localhost', overwriteFlag = True, **kwargs):
# Job creation init
em.epsJob.__init__(self, **kwargs)
# Set master file only
self.setMasterESfile(fileName = fileName, fileBase = fileBase, outFile = outFile, master = master, overwriteFlag = overwriteFlag)
# self.esData.
[docs] def buildePSjob(self, channel=None, mol='mol', batch='batch', note=None,
Ssym = None, Csym = None,
Estart=1.0, Estop = 1.0, dE = 1.0, EJob = None, precision = 2,
scrType = 'basicNoDefaults', writeInpLog = True,
overwriteFlag = False, confOnly = False):
"""
Master ePS job creation routine for electronic-structe case.
This tries to run all job creation steps, with useful output.
Parameters
----------
channel : int
Ionizing channel (orbital), for `self.esData.setChannel(channel)`
mol, batch : strings, optional, default = 'mol','batch'
Job labels, used for `self.setJob(mol = mol, orb = f'orb{job.esData.channel.name}', batch = batch)`
This defines the job paths & names.
note : string, optional, default = None
Used for additional job annotation.
`job.jobNote = f'{job.mol}, orb {job.esData.channel.name} ionization, batch {batch}, {note}.'`
This is propagated to generator files & outputs.
Ssym, Csym : lists, optional, default = None
Symmetry settings for Scattering and Continuum symmetries.
If None, defaults to all pairs.
(As set by self.esData.setePSinputs() and self.esData.genSymList().)
Estart, Estop, dE : floats, optional, default to 1.0
Energy settings for the job, passed to em.multiEChunck()
Ejob, precision : int, optional, default to None, 2
Additional energy settings for the job, passed to em.multiEChunck()
scrType : string, default = 'basicNoDefaults'
Template script for job generation.
Passed to self.writeInp().
writeInpLog : bool, default = True
Write log file on job creation if True.
Passed to self.writeInp().
overwriteFlag : bool, default = False
Overwrite any existing settings if True.
confOnly : bool, default = False
Skip ePS input file creation step.
This allows for local settings to be created without final deploy to ePS host.
"""
# Set channel
if channel is not None:
self.esData.setChannel(channel)
# TODO: set default case for HOMO here.
# Init job
print(f"\n*** Building job {mol}, orb{self.esData.channel.name} ({self.esData.channel.ePS}/{self.esData.PG}), batch: {batch}")
try:
# Set symmetries for job label (currently just sets ePS defined syms, may also want to propagate self.esData.channel.syms?)
orbLabel = f'orb{self.esData.channel.name}_{self.esData.channel.ePS}'
# Alternative method form - currently missing jobNote
self.setJob(mol = mol, orb = orbLabel, batch = batch, jobNote = note, overwriteFlag = overwriteFlag) #, jobNote = f'{job.mol}, orb {jobES.channel.name} ionization, sym testing run.')
if (note is None) and (self.jobNote is None):
self.jobNote = f'{self.mol}, orb {self.esData.channel.name} ({self.esData.channel.ePS}/{self.esData.PG}) ionization, batch {batch}, {note}.' # Additional notes, included at inp file head.
# else:
# self.jobNote = note
# except AttributeError as err:
# TODO: add more specific checks here!
# print("*** Failed to build job, no channel set - pass channel=int, or run self.esData.setChannel() to fix.")
# return False
except Exception as err:
print("\n*** Failed to build job, can't run self.setJob.")
print(err)
return False
# Build ePS generator file/configuration inputs
try:
# CURRENTLY NEED ALL OF THIS TO SET JOB FROM ES....
self.esData.host = self.host # 20/05/26 QUICK hack to fix paths in setePSinputs() UGLY.
self.esData.hostDefn = self.hostDefn
self.esData.setePSinputs(Ssym = Ssym, Csym = Csym) # Set self.ePSglobals and self.ePSrecords from inputs
# 09/05/23 - added this to fix broken electronic structure file paths on remote in ePS jobs...
# Hopefully won't break anything else...
if self.host != 'localhost':
# self.esData.ePSrecords['elecStructure']=self.hostDefn[self.host]['elecFile']
self.esData.ePSrecords['elecStructure']=self.hostDefn[self.host]['elecFile'].with_name(self.esData.moldenFile.name) # MOLDEN FILE!
self.esData.writeInputConf() # Dictionaries > strings for job template
self.setJobInputConfig() # Settings string > template file string
except Exception as err:
print("\n*** Failed to build job, can't set input configuration.")
print(err)
return False
# Set generators
try:
self.writeGenFile()
self.createJobDirTree()
except Exception as err:
print("\n*** Failed to build job, can't write gen file or create job tree - is the host set?")
print(err)
return False
# Push gen file
# Note - need pushFile even for localhost case, otherwise paths not set correctly.
# But - should be OK if .createJobDirTree() executed correctly?
# try:
# self.pushFile(self.genFile, self.hostDefn[self.host]['genDir'], overwritePrompt=False) # OK for file in wrkdir, force overwrite - currently not working!!!
#
# except Exception as err:
# print(f"\n*** Failed to build job, can't push gen file {self.genFile} to host at {self.hostDefn[self.host]['genDir']}.")
# print(err)
# return False
# Set energies & create ePS input files from generator
if confOnly:
print(f"\n*** Setting configuration only, skipping ePS file writers.")
print(f"Pass confOnly = True to run")
print(f"Or run `self.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = EJob, precision = precision)` and `self.writeInp(scrType = scrType, wLog = writeInpLog)` for manual control.")
else:
try:
# self.Elist = em.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = EJob, precision = precision)
self.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = EJob, precision = precision)
self.writeInp(scrType = scrType, wLog = writeInpLog)
except Exception as err:
print(f"\n*** Failed to build job at self.writeInp.")
print(err)
return False
# May also need to push electronic structure files...?
try:
self.checkLocalESfiles()
except Exception as err:
print(f"\n*** Failed to build job at self.checkLocalESfiles() - electronic structure files may be missing on host.")
print(err)
return False
[docs] def setMasterESfile(self, fileName = None, fileBase = None, outFile = None, master = 'localhost', overwriteFlag = True):
"""
Set electronic structure files for master, create object and check file.
NOTE: fileName cannot contain full path, since this is then set by fileBase/fileName.
TODO: add some checks/work-arounds here!
"""
# Set file for master host only, from passed args.
# This should work even for an uninitialised job (host settings not propagated).
self.setAttribute('elecStructure', fileName, overwriteFlag = overwriteFlag)
if fileBase is None:
try:
fileBase = self.hostDefn[master]['elecDir']
except KeyError:
fileBase = self.hostDefn[master]['wrkdir'] # Fallback to wrkdir if elecDir is not set.
if fileName is not None:
self.setHostDefns(elecDir = fileBase, elecFile = fileBase/self.elecStructure, host = master)
# self.setHostDefns(, host = master) #, elecFile = self.elecStructure)
# Set master file object.
# self.esData = ESclass.EShandler(self.hostDefn[master]['elecFile'], self.hostDefn[master]['elecDir'])
# Set master file object.
self.esData = ESclass.EShandler(fileName = fileName, fileBase = fileBase, outFile = outFile, verbose = self.verbose)
# else:
# print("Skipping")
[docs] def setESfiles(self, fileName = None, pushPrompt = True, overwriteFlag = False): # fileBase = None,
"""
Set electronic structure files for all self.hostDefn[host] and sync files.
NOTE: this sets self.elecStructure if passed, then propagates and syncs files.
This assumes self.hostDefn[host]['elecDir'] is already set on hosts.
Use setMasterESfile() to update & check local file first, and setJob(), initConenction() or setWrkDir() or setJobPaths() etc. first.
TODO: fix this!
"""
# Set values in epsJob class format (if not already set)
# self.setAttribute('elecDir', fileBase) # This will set self.elecDir, which is generally not used - need to pass/check to self.hostDefn[host]['elecDir'], or just ignore here.
self.setAttribute('elecStructure', fileName, overwriteFlag = overwriteFlag)
# If fileBase not passed, check for currently set paths
# TODO!
# Set in master host list
if self.elecStructure is not None:
# Set in hostDefn
# for host in self.hostDefn:
#
# # if hasattr(self.hostDefn[host])
# self.hostDefn[host]['elecFile'] = Path(self.hostDefn[host]['elecDir'], self.elecStructure)
# self.setHostDefns(elecDir = fileBase, elecFile = self.elecStructure) #FUCKING THIASSHIOEHD"FHK:ASDHGJK: DHAS:KG HASDGHKL:AK"JS
# THIS WON"T WORK FOR CASES WHERE FILEBASE IS PER HOST THIS IS SHIT.
# ALSO WON"T SET elecFile with correct dir
# Propagate elecStructure file, assuming 'elecDir' set for host already - ALSO SHIT
for host in self.hostDefn:
self.setHostDefns(elecFile = Path(self.hostDefn[host]['elecDir'], self.elecStructure), host = host)
# # Check file exists (local + host only)
# if self.verbose:
# print(f'Electronic structure file {self.elecStructure} host checks:')
#
# fCheckHost = {}
# for host in [self.host, 'localhost']:
# if host == 'localhost':
# fCheckHost[host] = self.checkLocalFiles(self.hostDefn[host]['elecFile']) # use local Path file test.
# else:
# fCheckHost[host] = self.checkFiles(self.hostDefn[host]['elecFile']) # Use remote Fabric file test.
#
# if self.verbose:
# print(f"\n\t{host}: \t{self.hostDefn[host]['elecFile']} \t{fCheckHost[host]}")
#
# pushFlag = 'n'
# if (not fCheckHost[self.host]) and (fCheckHost['localhost']):
# if pushPrompt:
# pushFlag = print(f'Push missing file to {self.host}? (y/n) ')
# else:
# pushFlag = 'y'
#
# if pushFlag == 'y':
# self.pushFileDict('elecFile')
# Sync files
self.syncFilesDict('elecFile', pushPrompt = pushPrompt)
[docs] def checkLocalESfiles(self, master = 'localhost', pushPrompt = True):
"""
Check master ES files, convert to Molden & sync with remote host.
Note that this will currently only work for a local machine as master, since there is no remote run set here.
Parameters
----------
master : str, default = 'localhost'
Set which host to use as master.
Note that Gamess > Molden conversion will currently only work for a local machine, since there is no remote run set here.
"""
# With EShandler as parent class
# EShandler init
# EShandler.__init__(self, fileName = fileName, fileBase = fileBase, outFile = outFile)
# Run base init routine for job settings
# super().__init__(**kwargs)
# With EShandler per host - CURRENTLY ASSUMED TO BE LOCAL HOST ONLY, set as master
# NOW SET as passed arg
# for master in ['localhost']:
# Set object & read file
# self.esData = ESclass.EShandler(self.hostDefn[master]['elecFile'], self.hostDefn[master]['elecDir'])
# If file is not a Molden file, read & convert
if self.esData.data is not None:
self.esData.writeMoldenFile2006()
# # Update master file
# self.elecStructure = self.esData.moldenFile.name
#
# # Update host paths (all hosts)
# for host in self.hostDefn:
# self.hostDefn[host]['elecFileGamess'] = self.hostDefn[host]['elecFile']
# self.hostDefn[host]['elecFile'] = self.hostDefn[host]['elecDir'] self.esData.moldenFile # self.hostDefn[host]['elecFile'].with_name
#
# # Sync files
# self.syncFilesDict('elecFile', pushPrompt = pushPrompt)
# Preserve Gamess file in new key
for host in self.hostDefn:
self.hostDefn[host]['elecFileGamess'] = self.hostDefn[host]['elecFile']
# Update with existing functionality.
self.setESfiles(fileName = self.esData.moldenFile.name, overwriteFlag = True, pushPrompt = pushPrompt)