Skip to content

Commit 0933219

Browse files
committed
Merge remote-tracking branch 'brian/master'
2 parents e90348c + cc5c15d commit 0933219

13 files changed

+823
-758
lines changed

crystalmanager.py

+295
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
from manager_tools import resolve_status, update_attributes
2+
from crystal import CrystalReader
3+
from propertiesreader import PropertiesReader
4+
from autorunner import RunnerPBS
5+
import os
6+
import pickle as pkl
7+
import shutil as sh
8+
import crystal2qmc
9+
from autopaths import paths
10+
11+
class CrystalManager:
12+
""" Internal class managing process of running a DFT job though crystal.
13+
Has authority over file names associated with this task."""
14+
def __init__(self,writer,runner,creader=None,name='crystal_run',path=None,
15+
preader=None,prunner=None,
16+
trylev=False,bundle=False,max_restarts=5):
17+
''' CrystalManager manages the writing of a Crystal input file, it's running, and keeping track of the results.
18+
Args:
19+
writer (PySCFWriter): writer for input.
20+
reader (PySCFReader): to read PySCF output.
21+
runner (runner object): to run job.
22+
creader (CrystalReader): Reads the crystal results, (None implies use default reader).
23+
preader (PropertiesReader): Reads properties results, if any (None implies use default reader).
24+
prunner (runner object): run properties if needed (None implies use same runner as crystal).
25+
name (str): identifier for this job. This names the files associated with run.
26+
trylev (bool): When restarting use LEVSHIFT option to encourage convergence, then do a rerun without LEVSHIFT.
27+
bundle (bool): Whether you'll use a bundling tool to run these jobs.
28+
max_restarts (int): maximum number of times you'll allow restarting before giving up (and manually intervening).
29+
'''
30+
# Where to save self.
31+
self.name=name
32+
self.pickle="%s.pkl"%self.name
33+
34+
# Ensure path is set up correctly.
35+
if path is None:
36+
path=os.path.getcwd()
37+
if path[-1]!='/': path+='/'
38+
self.path=path
39+
40+
self.logname="%s@%s"%(self.__class__.__name__,self.path+self.name)
41+
42+
#print(self.logname,": initializing")
43+
44+
# Handle reader and runner defaults.
45+
self.writer=writer
46+
if creader is None: self.creader=CrystalReader()
47+
else: self.creader=creader
48+
if preader is None: self.preader=PropertiesReader()
49+
else: self.preader=preader
50+
if prunner is None: self.prunner=runner
51+
else: self.prunner=prunner
52+
if runner is None: self.runner=RunnerPBS()
53+
else: self.runner=runner
54+
55+
# Internal.
56+
self.crysinpfn=self.name
57+
self.propinpfn=self.name+'.prop'
58+
self.crysoutfn=self.crysinpfn+'.o'
59+
self.propoutfn=self.propinpfn+'.o'
60+
self.restarts=0
61+
self._runready=False
62+
self.scriptfile=None
63+
self.completed=False
64+
self.bundle=bundle
65+
self.qwfiles={
66+
'kpoints':[],
67+
'basis':'',
68+
'jastrow2':'',
69+
'orbplot':{},
70+
'orb':{},
71+
'sys':{},
72+
'slater':{}
73+
}
74+
75+
# Smart error detection.
76+
self.trylev=trylev
77+
self.max_restarts=max_restarts
78+
self.savebroy=[]
79+
self.lev=False
80+
81+
# Handle old results if present.
82+
if os.path.exists(self.path+self.pickle):
83+
#print(self.logname,": rebooting old manager.")
84+
old=pkl.load(open(self.path+self.pickle,'rb'))
85+
self.recover(old)
86+
87+
# Update the file.
88+
if not os.path.exists(self.path): os.mkdir(self.path)
89+
with open(self.path+self.pickle,'wb') as outf:
90+
pkl.dump(self,outf)
91+
92+
#------------------------------------------------
93+
def recover(self,other):
94+
''' Recover old class by copying over data. Retain variables from old that may change final answer.'''
95+
# Practically speaking, the run will preserve old `take_keys` and allow new changes to `skip_keys`.
96+
# This is because you are taking the attributes from the older instance, and copying into the new instance.
97+
98+
update_attributes(copyto=self,copyfrom=other,
99+
skip_keys=['writer','runner','creader','preader','prunner','lev','savebroy',
100+
'path','logname','name',
101+
'trylev','max_restarts','bundle'],
102+
take_keys=['restarts','completed','qwfiles'])
103+
104+
# Update queue settings, but save queue information.
105+
update_attributes(copyto=self.runner,copyfrom=other.runner,
106+
skip_keys=['queue','walltime','np','nn','jobname'],
107+
take_keys=['queueid'])
108+
update_attributes(copyto=self.prunner,copyfrom=other.prunner,
109+
skip_keys=['queue','walltime','np','nn','jobname'],
110+
take_keys=['queueid'])
111+
112+
update_attributes(copyto=self.creader,copyfrom=other.creader,
113+
skip_keys=[],
114+
take_keys=['completed','output'])
115+
116+
update_attributes(copyto=self.preader,copyfrom=other.preader,
117+
skip_keys=[],
118+
take_keys=['completed','output'])
119+
120+
updated=update_attributes(copyto=self.writer,copyfrom=other.writer,
121+
skip_keys=['maxcycle','edifftol'],
122+
take_keys=['completed','modisymm','restart','guess_fort','_elements'])
123+
if updated:
124+
self.writer.completed=False
125+
126+
#----------------------------------------
127+
def nextstep(self):
128+
''' Determine and perform the next step in the calculation.'''
129+
self.recover(pkl.load(open(self.path+self.pickle,'rb')))
130+
131+
print(self.logname,": next step.")
132+
cwd=os.getcwd()
133+
os.chdir(self.path)
134+
135+
# Generate input files.
136+
if not self.writer.completed:
137+
if self.writer.guess_fort is not None:
138+
sh.copy(self.writer.guess_fort,'fort.20')
139+
with open(self.crysinpfn,'w') as f:
140+
self.writer.write_crys_input(self.crysinpfn)
141+
with open(self.propinpfn,'w') as f:
142+
self.writer.write_prop_input(self.propinpfn)
143+
144+
# Check on the CRYSTAL run
145+
status=resolve_status(self.runner,self.creader,self.crysoutfn)
146+
print(self.logname,": status= %s"%(status))
147+
148+
if status=="not_started":
149+
self.runner.add_command("cp %s INPUT"%self.crysinpfn)
150+
self.runner.add_task("%s &> %s"%(paths['Pcrystal'],self.crysoutfn))
151+
152+
elif status=="ready_for_analysis":
153+
#This is where we (eventually) do error correction and resubmits
154+
status=self.creader.collect(self.crysoutfn)
155+
print(self.logname,": status %s"%status)
156+
if status=='killed':
157+
if self.restarts >= self.max_restarts:
158+
print(self.logname,": restarts exhausted (%d previous restarts). Human intervention required."%self.restarts)
159+
else:
160+
print(self.logname,": attempting restart (%d previous restarts)."%self.restarts)
161+
self.writer.restart=True
162+
if self.trylev:
163+
print(self.logname,": trying LEVSHIFT.")
164+
self.writer.levshift=[10,1] # No mercy.
165+
self.savebroy=deepcopy(self.writer.broyden)
166+
self.writer.broyden=[]
167+
self.lev=True
168+
sh.copy(self.crysinpfn,"%d.%s"%(self.restarts,self.crysinpfn))
169+
sh.copy(self.crysoutfn,"%d.%s"%(self.restarts,self.crysoutfn))
170+
sh.copy('fort.79',"%d.fort.79"%(self.restarts))
171+
self.writer.guess_fort='./fort.79'
172+
sh.copy(self.writer.guess_fort,'fort.20')
173+
self.writer.write_crys_input(self.crysinpfn)
174+
sh.copy(self.crysinpfn,'INPUT')
175+
self.runner.add_task("%s &> %s"%(paths['Pcrystal'],self.crysoutfn))
176+
self.restarts+=1
177+
elif status=='done' and self.lev:
178+
# We used levshift to converge. Now let's restart to be sure.
179+
print("Recovering from LEVSHIFTer.")
180+
self.writer.restart=True
181+
self.writer.levshift=[]
182+
self.creader.completed=False
183+
self.lev=False
184+
sh.copy(self.crysinpfn,"%d.%s"%(self.restarts,self.crysinpfn))
185+
sh.copy(self.crysoutfn,"%d.%s"%(self.restarts,self.crysoutfn))
186+
sh.copy('fort.79',"%d.fort.79"%(self.restarts))
187+
self.writer.guess_fort='./fort.79'
188+
sh.copy(self.writer.guess_fort,'fort.20')
189+
self.writer.write_crys_input(self.crysinpfn)
190+
sh.copy(self.crysinpfn,'INPUT')
191+
self.runner.add_task("%s &> %s"%(paths['Pcrystal'],self.crysoutfn))
192+
self.restarts+=1
193+
194+
# Ready for bundler or else just submit the jobs as needed.
195+
if self.bundle:
196+
self.scriptfile="%s.run"%self.name
197+
self.bundle_ready=self.runner.script(self.scriptfile)
198+
else:
199+
qsubfile=self.runner.submit(self.path.replace('/','-')+self.name)
200+
201+
self.completed=self.creader.completed
202+
203+
# Update the file.
204+
with open(self.pickle,'wb') as outf:
205+
pkl.dump(self,outf)
206+
os.chdir(cwd)
207+
208+
#----------------------------------------
209+
def collect(self):
210+
''' Call the collect routine for readers.'''
211+
print(self.logname,": collecting results.")
212+
self.creader.collect(self.path+self.crysoutfn)
213+
214+
# Update the file.
215+
with open(self.path+self.pickle,'wb') as outf:
216+
pkl.dump(self,outf)
217+
218+
#------------------------------------------------
219+
def script(self,jobname=None):
220+
''' Script execution lines for a bundler to pick up and run.'''
221+
if jobname is None: jobname=self.runner.jobname
222+
self.scriptfile="%s.run"%jobname
223+
self._runready=self.runner.script(self.scriptfile)
224+
225+
#------------------------------------------------
226+
def submit(self,jobname=None):
227+
''' Submit the runner's job to the queue. '''
228+
qsubfile=self.runner.submit(jobname)
229+
return qsubfile
230+
231+
#----------------------------------------
232+
def to_json(self):
233+
raise NotImplementedError
234+
235+
#----------------------------------------
236+
def write_summary(self):
237+
self.creader.write_summary()
238+
239+
#------------------------------------------------
240+
def export_qwalk(self):
241+
''' Export QWalk input files into current directory.'''
242+
self.recover(pkl.load(open(self.path+self.pickle,'rb')))
243+
244+
ready=False
245+
if len(self.qwfiles['slater'])==0:
246+
self.nextstep()
247+
248+
if not self.completed:
249+
return False
250+
251+
cwd=os.getcwd()
252+
os.chdir(self.path)
253+
254+
print(self.logname,": %s attempting to generate QWalk files."%self.name)
255+
256+
# Check on the properties run
257+
status=resolve_status(self.prunner,self.preader,self.propoutfn)
258+
print(self.logname,": properties status= %s"%(status))
259+
if status=='not_started':
260+
ready=False
261+
self.prunner.add_command("cp %s INPUT"%self.propinpfn)
262+
self.prunner.add_task("%s &> %s"%(paths['Pproperties'],self.propoutfn))
263+
264+
if self.bundle:
265+
self.scriptfile="%s.run"%self.name
266+
self.bundle_ready=self.prunner.script(self.scriptfile,self.driverfn)
267+
else:
268+
qsubfile=self.runner.submit(self.path.replace('/','-')+self.name)
269+
elif status=='ready_for_analysis':
270+
self.preader.collect(self.propoutfn)
271+
272+
if self.preader.completed:
273+
ready=True
274+
print(self.logname,": converting crystal to QWalk input now.")
275+
self.qwfiles=crystal2qmc.convert_crystal(base=self.name,propoutfn=self.propoutfn)
276+
else:
277+
ready=False
278+
print(self.logname,": conversion postponed because properties is not finished.")
279+
280+
os.chdir(cwd)
281+
else:
282+
ready=True
283+
284+
with open(self.path+self.pickle,'wb') as outf:
285+
pkl.dump(self,outf)
286+
287+
return ready
288+
289+
#----------------------------------------
290+
def status(self):
291+
if self.completed:
292+
return 'ok'
293+
else:
294+
return 'not_finished'
295+

intro/00-introduction.ipynb

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"from autorunner import PySCFRunnerLocal,RunnerLocal\n",
4848
"from trialfunc import SlaterJastrow\n",
4949
"from variance import VarianceWriter, VarianceReader\n",
50-
"from manager import PySCFManager, QWalkManager\n",
50+
"from pyscfmanager import PySCFManager\n",
51+
"from qwalkmanager import QWalkManager\n",
5152
"\n",
5253
"path='00-scratch'\n",
5354
"\n",

intro/00-introduction.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
from autorunner import PySCFRunnerLocal,RunnerLocal
3333
from trialfunc import SlaterJastrow
3434
from variance import VarianceWriter, VarianceReader
35-
from manager import PySCFManager, QWalkManager
35+
from pyscfmanager import PySCFManager
36+
from qwalkmanager import QWalkManager
3637

3738
path='00-scratch'
3839

intro/04-job_management.ipynb

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
"metadata": {},
8383
"outputs": [],
8484
"source": [
85-
"from manager import PySCFManager\n",
85+
"from pyscfmanager import PySCFManager\n",
8686
"pyscf_manager = PySCFManager(\n",
8787
" path='04-scratch',\n",
8888
" name='h2_pbe',\n",

intro/04-job_management.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
# In[ ]:
6060

6161

62-
from manager import PySCFManager
62+
from pyscfmanager import PySCFManager
6363
pyscf_manager = PySCFManager(
6464
path='04-scratch',
6565
name='h2_pbe',

intro/05-qwalk.ipynb

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"source": [
4040
"from autopyscf import PySCFWriter,PySCFReader\n",
4141
"from autorunner import PySCFRunnerLocal\n",
42-
"from manager import PySCFManager\n",
42+
"from pyscfmanager import PySCFManager\n",
4343
"\n",
4444
"h2='\\n'.join([\n",
4545
" 'H 0.0 0.0 0.0 ',\n",
@@ -95,7 +95,7 @@
9595
"source": [
9696
"from variance import VarianceWriter,VarianceReader\n",
9797
"from autorunner import RunnerLocal\n",
98-
"from manager import QWalkManager\n",
98+
"from qwalkmanager import QWalkManager\n",
9999
"from imp import reload\n",
100100
"import time\n",
101101
"\n",

intro/05-qwalk.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from autopyscf import PySCFWriter,PySCFReader
2626
from autorunner import PySCFRunnerLocal
27-
from manager import PySCFManager
27+
from pyscfmanager import PySCFManager
2828

2929
h2='\n'.join([
3030
'H 0.0 0.0 0.0 ',
@@ -64,7 +64,7 @@
6464

6565
from variance import VarianceWriter,VarianceReader
6666
from autorunner import RunnerLocal
67-
from manager import QWalkManager
67+
from qwalkmanager import QWalkManager
6868
from imp import reload
6969
import time
7070

0 commit comments

Comments
 (0)