mirror of https://github.com/QMCPACK/qmcpack.git
655 lines
19 KiB
Python
655 lines
19 KiB
Python
##################################################################
|
|
## (c) Copyright 2015- by Jaron T. Krogel ##
|
|
##################################################################
|
|
|
|
|
|
import os
|
|
from generic import obj
|
|
from developer import DevBase
|
|
from simulation import Simulation,SimulationInput,SimulationAnalyzer
|
|
|
|
|
|
# PLEASE READ THIS
|
|
#
|
|
# depending on what you want to do with a simulation
|
|
# you will have to implement different functions below
|
|
# here are a few use cases and the functions required
|
|
# details about each function are given within the classes
|
|
#
|
|
# use cases
|
|
# 1) standalone simulation
|
|
# nexus drives this simulation in isolation of others
|
|
# i.e., one performs parameter scans to drive several independent opium runs
|
|
# in this setting, a opium simulation does not provide information to
|
|
# other simulations (e.g. pseudopotentials to qmcpack)
|
|
#
|
|
# the input file will be read from a template file
|
|
# and modified to obtain the desired inputs
|
|
# one could also provide the input longhand in python
|
|
# in a form OpiumInput understands (this depends on your implementation)
|
|
#
|
|
# required functions to be implemented:
|
|
# OpiumInput: read_contents, write_contents
|
|
# Opium: app_command, check_sim_status
|
|
#
|
|
# 2) generated standalone simulation
|
|
# as above, but with fully generated input files
|
|
# generate functions provide a short-hand of minimal vars for input
|
|
#
|
|
# required functions to be implemented:
|
|
# OpiumInput: read_contents, write_contents
|
|
# Opium: app_command, check_sim_status
|
|
# generate_opium_input
|
|
#
|
|
# 3) simulation that provides information to subsequent chained simulations
|
|
# as above (with or without #2)
|
|
# other simulations can request and get information about
|
|
# results produced by this simulation
|
|
# (e.g. relaxed structure data, location of orbital files, etc.)
|
|
# this information is used by the others to populate input files
|
|
#
|
|
# required functions to be implemented:
|
|
# OpiumInput: read_contents, write_contents
|
|
# Opium: app_command,check_sim_status,
|
|
# check_result, get_result
|
|
#
|
|
# if required to get needed output information:
|
|
# OpiumAnalyzer: analyze
|
|
|
|
|
|
def readval(s):
|
|
try:
|
|
val = int(s)
|
|
except:
|
|
try:
|
|
val = float(s)
|
|
except:
|
|
val = s
|
|
#end try
|
|
#end try
|
|
return val
|
|
#end def readval
|
|
|
|
|
|
class Section(DevBase):
|
|
types = {int:'int',float:'float',str:'str',list:'list',obj:'obj'}
|
|
variables = None
|
|
def __init__(self,list_rep=None,**kwargs):
|
|
if isinstance(list_rep,list):
|
|
self.from_list_rep(list_rep)
|
|
else:
|
|
self.set(**kwargs)
|
|
#end if
|
|
#self.validate()
|
|
#for name,type in self.variables.iteritems():
|
|
# if type in ('str','int','float'):
|
|
# val = None
|
|
# elif type=='list':
|
|
# val = []
|
|
# elif type=='obj':
|
|
# val = obj()
|
|
# #end if
|
|
# self[name] = val
|
|
##end for
|
|
#end def __init__
|
|
|
|
def validate(self):
|
|
allowed = set(self.variables.keys())
|
|
if len(allowed)>0: # some have numeric rather than named entries
|
|
present = set(self.keys())
|
|
missing = allowed-present
|
|
invalid = present-allowed
|
|
if len(missing)>0:
|
|
self.error('the following variables are missing: '+str(sorted(missing)))
|
|
#end if
|
|
if len(invalid)>0:
|
|
self.error('invalid variable names encountered\ninvalid variables: {0}\nallowed variables: {1}'.format(sorted(invalid),sorted(allowed)))
|
|
#end if
|
|
for name in sorted(present):
|
|
type = self.variables[name]
|
|
if not isinstance(self[name],type):
|
|
self.error('type of variable {0} is incorrect\ntype required: {1}\ncontents: {2}'.format(name,self.types[type]),str(self[name]))
|
|
#end if
|
|
#end for
|
|
#end if
|
|
#end def validate
|
|
|
|
def read(self,text):
|
|
list_rep = []
|
|
lines = text.splitlines()
|
|
for line in lines:
|
|
tokens = line.split()
|
|
vals = []
|
|
for token in tokens:
|
|
vals.append(readval(token))
|
|
#end for
|
|
list_rep.append(vals)
|
|
#end for
|
|
self.from_list_rep(list_rep)
|
|
#end def read
|
|
|
|
def write(self):
|
|
s = '['+self.__class__.__name__+']\n'
|
|
for line_list in self.list_rep():
|
|
for val in line_list:
|
|
s+=' '+str(val)
|
|
#end for
|
|
s+='\n'
|
|
#end for
|
|
s+='\n'
|
|
return s
|
|
#end def write
|
|
|
|
def list_rep(self):
|
|
self.not_implemented()
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.not_implemented()
|
|
#end def from_list_rep
|
|
#end class Section
|
|
|
|
|
|
|
|
|
|
class Atom(Section):
|
|
variables = obj(symbol=str,rorbs=int,ref=obj)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.symbol],[self.rorbs]]
|
|
for index in sorted(self.ref.keys()):
|
|
v = self.ref[index]
|
|
list_rep.append([v.nlm,v.occ,v.eig])
|
|
#end for
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.symbol = list_rep[0][0]
|
|
self.rorbs = list_rep[1][0]
|
|
ref = obj()
|
|
n = 0
|
|
for nlm,occ,eig in list_rep[2:]:
|
|
ref[n] = obj(nlm=nlm,occ=occ,eig=eig)
|
|
n+=1
|
|
#end for
|
|
self.ref = ref
|
|
#end def from_list_rep
|
|
#end class Atom
|
|
|
|
|
|
class Pseudo(Section):
|
|
variables = obj(porbs=int,rcuts=list,method=str)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.porbs]+list(self.rcuts),[self.method]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.porbs = list_rep[0][0]
|
|
self.rcuts = list_rep[0][1:]
|
|
self.method = list_rep[1][0]
|
|
#end def from_list_rep
|
|
#end class Pseudo
|
|
|
|
|
|
class Optinfo(Section):
|
|
variables = obj(qcuts=list,bessels=list)
|
|
|
|
def list_rep(self):
|
|
list_rep = zip(self.qcuts,self.bessels)
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
qc = []
|
|
bs = []
|
|
for q,b in list_rep:
|
|
qc.append(q)
|
|
bs.append(b)
|
|
#end for
|
|
self.qcuts = qc
|
|
self.bessels = bs
|
|
#end def from_list_rep
|
|
#end class Optinfo
|
|
|
|
|
|
class XC(Section):
|
|
variables = obj(functional=str)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.functional]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.functional = list_rep[0][0]
|
|
#end def from_list_rep
|
|
#end class XC
|
|
|
|
|
|
class Pcc(Section):
|
|
variables = obj(radius=float,method=str)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.radius],[self.method]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.radius = list_rep[0][0]
|
|
self.method = list_rep[1][0]
|
|
#end def from_list_rep
|
|
#end class Pcc
|
|
|
|
|
|
class Relativity(Section):
|
|
variables = obj(rl=str)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.rl]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.rl = list_rep[0][0]
|
|
#end def from_list_rep
|
|
#end class Relativity
|
|
|
|
|
|
class Grid(Section):
|
|
variables = obj(np=int,a=float,b=float)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.np,self.a,self.b]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
np,a,b = list_rep[0]
|
|
self.np = np
|
|
self.a = a
|
|
self.b = b
|
|
#end def from_list_rep
|
|
#end class Grid
|
|
|
|
|
|
class Tol(Section):
|
|
variables = obj(aetol=float,nltol=float)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.aetol,self.nltol]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
ae,nl = list_rep[0]
|
|
self.aetol = ae
|
|
self.nltol = nl
|
|
#end def from_list_rep
|
|
#end class Tol
|
|
|
|
|
|
class Configs(Section):
|
|
variables = obj()
|
|
|
|
def list_rep(self):
|
|
list_rep = [[len(self)]]
|
|
for index in sorted(self.keys()):
|
|
v = self[index]
|
|
list_rep.append([v.nlm,v.occ,v.eig])
|
|
#end for
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
nconfigs = list_rep[0][0]
|
|
orbs = list_rep[1:]
|
|
orbs_per_config = len(orbs)/nconfigs
|
|
n=1
|
|
for nlm,occ,eig in orbs:
|
|
if n%orbs_per_config==0:
|
|
self.append(obj(nlm=nlm,occ=occ,eig=eig))
|
|
#end if
|
|
n+=1
|
|
#end for
|
|
#end def from_list_rep
|
|
#end class Configs
|
|
|
|
|
|
class KBdesign(Section):
|
|
variables = obj(local=str,boxes=obj)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.local]]
|
|
if 'boxes' in self:
|
|
list_rep.append([len(self.boxes)])
|
|
for index in sorted(self.boxes.keys()):
|
|
v = self.boxes[index]
|
|
list_rep.append([v.units,v.rmin,v.rmax,v.depth])
|
|
#end for
|
|
#end if
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.local = list_rep[0][0]
|
|
if len(list_rep)>1:
|
|
boxes = obj()
|
|
for units,rmin,rmax,depth in list_rep[2:]:
|
|
boxes.append(obj(units=units,rmin=rmin,rmax=rmax,depth=depth))
|
|
#end for
|
|
self.boxes = boxes
|
|
#end if
|
|
#end def from_list_rep
|
|
#end class KBdesign
|
|
|
|
|
|
class Loginfo(Section):
|
|
variables = obj(config=int,radius=float,Emin=float,Emax=float)
|
|
|
|
def list_rep(self):
|
|
list_rep = [[self.config],[self.radius,self.Emin,self.Emax]]
|
|
return list_rep
|
|
#end def list_rep
|
|
|
|
def from_list_rep(self,list_rep):
|
|
self.config = list_rep[0][0]
|
|
radius,Emin,Emax = list_rep[1]
|
|
self.radius = radius
|
|
self.Emin = Emin
|
|
self.Emax = Emax
|
|
#end def from_list_rep
|
|
#end class Loginfo
|
|
|
|
|
|
|
|
class OpiumInput(SimulationInput):
|
|
|
|
method_map = obj(o='optimized',k='kerker',t='tm')
|
|
section_map = obj(atom=Atom,pseudo=Pseudo,optinfo=Optinfo,xc=XC,pcc=Pcc,relativity=Relativity,grid=Grid,tol=Tol,configs=Configs,kbdesign=KBdesign,loginfo=Loginfo)
|
|
|
|
section_order = 'Atom Pseudo Optinfo XC Pcc Relativity Grid Tol Configs KBdesign Loginfo'.split()
|
|
|
|
def __init__(self,filepath=None,
|
|
atom = None,
|
|
pseudo = None,
|
|
optinfo = None,
|
|
xc = None,
|
|
pcc = None,
|
|
relativity = None,
|
|
grid = None,
|
|
tol = None,
|
|
configs = None,
|
|
kbdesign = None,
|
|
loginfo = None
|
|
):
|
|
if filepath!=None:
|
|
self.read(filepath)
|
|
else:
|
|
inputs = obj(atom=atom,pseudo=pseudo,optinfo=optinfo,xc=xc,pcc=pcc,relativity=relativity,grid=grid,tol=tol,configs=configs,kbdesign=kbdesign,loginfo=loginfo)
|
|
for secname,input in inputs.iteritems():
|
|
section_type = self.section_map[secname]
|
|
if isinstance(input,section_type):
|
|
self[secname]=input
|
|
elif isinstance(input,(dict,obj)):
|
|
self[secname] = section_type(**input)
|
|
elif isinstance(input,(list,tuple)):
|
|
self[secname] = section_type(list(input))
|
|
elif input!=None:
|
|
self.error('invalid type encountered for {0} input\nexpected types are dict, obj, list, or tuple\nvalue provided: {1}'.format(secname,input))
|
|
#end if
|
|
#end for
|
|
#end if
|
|
#end def __init__
|
|
|
|
|
|
def read_contents(self,contents):
|
|
lines = contents.splitlines()
|
|
sections = obj()
|
|
sec=None
|
|
secname=None
|
|
for line in lines:
|
|
ls = line.strip()
|
|
if len(ls)>0 and not ls.startswith('#'):
|
|
if ls.startswith('[') and ls.endswith(']'):
|
|
prevsecname = secname
|
|
secname = ls.strip('[]').lower()
|
|
if not secname in self.section_map:
|
|
self.error('cannot read file\n{0} is not a valid section name\nvalid options are: {1}'.format(secname,self.section_order))
|
|
#end if
|
|
if sec!=None:
|
|
sections[prevsecname]=sec
|
|
#end if
|
|
sec=''
|
|
elif sec is None:
|
|
self.error('invalid text encountered: '+line)
|
|
else:
|
|
sec+=ls+'\n'
|
|
#end if
|
|
#end if
|
|
#end for
|
|
if sec!=None and secname!=None and not secname in sections:
|
|
sections[secname]=sec
|
|
#end if
|
|
for secname,sectext in sections.iteritems():
|
|
section = self.section_map[secname]()
|
|
section.read(sectext)
|
|
self[secname] = section
|
|
#end for
|
|
#end def read_contents
|
|
|
|
|
|
def write_contents(self):
|
|
contents = ''
|
|
for secname in self.section_order:
|
|
secname = secname.lower()
|
|
if secname in self:
|
|
contents += self[secname].write()
|
|
#end if
|
|
#end for
|
|
return contents
|
|
#end def write_contents
|
|
#end class OpiumInput
|
|
|
|
|
|
|
|
def generate_opium_input(selector,**kwargs):
|
|
|
|
if selector=='basic':
|
|
return generate_basic_opium_input(**kwargs)
|
|
elif selector=='full':
|
|
return generate_full_opium_input(**kwargs)
|
|
else:
|
|
OpiumInput.class_error('selection '+str(selector)+' has not been implemented for opium input generation')
|
|
#end if
|
|
#end def generate_opium_input
|
|
|
|
|
|
def generate_basic_opium_input(
|
|
):
|
|
oi = None
|
|
return oi
|
|
#end def generate_basic_opium_input
|
|
|
|
|
|
def generate_full_opium_input(
|
|
atom = None,
|
|
pseudo = None,
|
|
optinfo = None,
|
|
xc = None,
|
|
pcc = None,
|
|
relativity = None,
|
|
grid = None,
|
|
tol = None,
|
|
configs = None,
|
|
kbdesign = None,
|
|
loginfo = None
|
|
):
|
|
|
|
oi = OpiumInput(
|
|
atom = atom ,
|
|
pseudo = pseudo ,
|
|
optinfo = optinfo ,
|
|
xc = xc ,
|
|
pcc = pcc ,
|
|
relativity = relativity,
|
|
grid = grid ,
|
|
tol = tol ,
|
|
configs = configs ,
|
|
kbdesign = kbdesign ,
|
|
loginfo = loginfo
|
|
)
|
|
|
|
return oi
|
|
#end def generate_full_opium_input
|
|
|
|
|
|
|
|
class OpiumAnalyzer(SimulationAnalyzer):
|
|
def __init__(self,arg0=None):
|
|
# optional
|
|
# only necessary if you want to use results from output files
|
|
# to inform the inputs of subsequent simulations
|
|
# or if you want to have a general purpose class to scrape
|
|
# and process simulation data
|
|
# below is a reasonable default implementation
|
|
# if you don't want to implement it, just uncomment the following line
|
|
#return
|
|
|
|
self.path = None
|
|
self.input = None
|
|
infile = None
|
|
if isinstance(arg0,Simulation):
|
|
sim = arg0
|
|
infile = os.path.join(sim.locdir,sim.infile)
|
|
else:
|
|
infile = arg0
|
|
#end if
|
|
if infile!=None:
|
|
self.path = os.path.dirname(infile)
|
|
self.input = OpiumInput(infile)
|
|
#end if
|
|
#end def __init__
|
|
|
|
|
|
def analyze(self):
|
|
# optional
|
|
# only necessary if you want to use results from output files
|
|
# to inform the inputs of subsequent simulations
|
|
# or if you want to have a general purpose class to scrape
|
|
# and process simulation data
|
|
# if you don't want to implement it, no action is required
|
|
None
|
|
#end def analyze
|
|
#end class OpiumAnalyzer
|
|
|
|
|
|
|
|
class Opium(Simulation):
|
|
input_type = OpiumInput
|
|
analyzer_type = OpiumAnalyzer
|
|
generic_identifier = 'opium'
|
|
application = 'opium_exe' #replace with default name of opium executable
|
|
application_properties = set(['serial','mpi'])
|
|
application_results = set(['orbitals']) #what opium produces that other simulations can use
|
|
|
|
def check_result(self,result_name,sim):
|
|
# optional
|
|
# only necessary if another simulation depends on this one
|
|
# e.g.
|
|
# other_sim.depends(opium_sim,'pseudopotential') or similar
|
|
# if you don't want to implement it, uncomment the line below
|
|
#return False
|
|
calculating_result = False
|
|
input = self.input # a OpiumInput object
|
|
# check the input to see if result is being calculated
|
|
# (e.g. result_name='pseudopotential')
|
|
return calculating_result
|
|
#end def check_result
|
|
|
|
|
|
def get_result(self,result_name,sim):
|
|
# optional
|
|
# only necessary if another simulation depends on this one
|
|
# e.g.
|
|
# other_sim.depends(opium_sim,'pseudopotential') or similar
|
|
# if you don't want to implement it, uncomment the line below
|
|
#self.not_implemented()
|
|
|
|
result = obj()
|
|
input = self.input
|
|
#analyzer = self.load_analyzer_image()
|
|
|
|
# package information about a result/product in the result object
|
|
# for example, if pseudopotentials are requested,
|
|
# the path to the pseudopotential file might be provided:
|
|
# result.pseudopotential_file = '/path/to/pseudopotential/file'
|
|
return result
|
|
#end def get_result
|
|
|
|
|
|
def app_command(self):
|
|
# required
|
|
# specify command line arguments to the executable, such as the input file
|
|
# e.g. command_line_args = ' '+self.infile
|
|
command_line_args = ''
|
|
return self.app_name + command_line_args
|
|
#end def app_command
|
|
|
|
|
|
def check_sim_status(self):
|
|
# required
|
|
# read output/error files to check whether simulation has
|
|
# completed successfully
|
|
# one could also check whether all output files exist
|
|
output = open(os.path.join(self.locdir,self.outfile),'r').read()
|
|
errors = open(os.path.join(self.locdir,self.errfile),'r').read()
|
|
|
|
success = False
|
|
# check output and errors
|
|
# set success=True if run completed successfully
|
|
|
|
self.finished = success and self.job.finished
|
|
#end def check_sim_status
|
|
|
|
|
|
def get_output_files(self):
|
|
# optional
|
|
# if provided, the listed output files will be copied to the results directory
|
|
# if you don't want to implement it, no action is required
|
|
output_files = []
|
|
return output_files
|
|
#end def get_output_files
|
|
#end class Opium
|
|
|
|
|
|
|
|
def generate_opium(**kwargs):
|
|
has_input = 'input_type' in kwargs
|
|
if has_input:
|
|
input_type = kwargs['input_type']
|
|
del kwargs['input_type']
|
|
else:
|
|
input_type = 'basic'
|
|
#end if
|
|
kw = set(kwargs.keys())
|
|
sim_kw = kw & Simulation.allowed_inputs
|
|
inp_kw = (kw - sim_kw)
|
|
sim_args = dict()
|
|
inp_args = dict()
|
|
for kw in sim_kw:
|
|
sim_args[kw] = kwargs[kw]
|
|
#end for
|
|
for kw in inp_kw:
|
|
inp_args[kw] = kwargs[kw]
|
|
#end for
|
|
if len(inp_args)>0:
|
|
sim_args['input'] = generate_opium_input(input_type,**inp_args)
|
|
#end if
|
|
opium = Opium(**sim_args)
|
|
|
|
return opium
|
|
#end def generate_opium
|