If you do NPT simulations with namd2, you may get the following message:
FATAL ERROR: Periodic cell has become too small for original patch grid!
Unfortunately, namd2 does not support automatic restarts in this case. Here is a small wrapper script, that automates this process. Just prepare your namd.conf as usual but omit the DCDfile and restartname keywords. Then call the wrapper
namd_keep_running.py template/namd.conf template/ /storage/somewhere/ --scratchdir='/local_scratch/somewhere/'
The configuration file should be suitable for simulations working only on a copy of the template directory. The simulation run will be split into a minimization and MD part. After completion, the data will be copied from the scratch directory to the storage directory given as third parameter. The results are collected in a folder named after the PBS job id or after the current process id (if invoked outside of the queueing system).
#!/usr/bin/env python # system modules import argparse import os import shutil import subprocess import sys # argument parsing parser = argparse.ArgumentParser(description='Automatically restarts namd2 on patch grid changes.') parser.add_argument('configfile', nargs=1, help='namd config file') parser.add_argument('templatedir', nargs=1, help='namd template folder according to relative filenames in configfile. Should be prepared to contain the configuration file.') parser.add_argument('outputdir', nargs=1, help='Folder to save the results to.') parser.add_argument('--scratchdir', nargs='?', default='/tmp/', help='Scratch directory.') args = parser.parse_args() # sanitize input def checkfolder(value): if value[-1] != '/': value += '/' if value[0] != '/': value = os.getcwd() + '/' + value return value args.scratchdir = checkfolder(args.scratchdir) args.outputdir = checkfolder(args.outputdir[0]) args.templatedir = args.templatedir[0] args.configfile = args.configfile[0] # config file input def get_key_val(line): parts = line.split('#') line = parts[0].strip() parts = line.split('=') if len(parts) > 2: raise ValueError('Malformatted config line') if len(parts) == 2: return tuple(parts) parts = parts[0].split() if len(parts) < 2: return (None, None) return (parts[0], ' '.join(parts[1:])) try: fh = open(args.configfile) except: print 'Unable to read config file %s.' % args.configfile exit(1) lines = fh.readlines() # parse config file firstline = len(lines) basiclines = [] first_run_lines = [] due = dict([('minimize', 0), ('run', 0)]) for no, line in enumerate(lines): try: key, value = get_key_val(line) except ValueError: print 'Syntax error on line %d. Omitting line.' % (no+1) if key is None: continue basicline = True if key.lower() == 'binaryrestart': if value.lower() != 'yes': print 'Please use binary restart files. namd2 default is yes.' exit(1) if key.lower() == 'dcdfile': print 'Please remove the DCDfile keyword. It will be activated automatically.' exit(1) if key.lower() == 'restartname': print 'Please remove the restartname keyword. It will be activated automatically.' exit(1) if key.lower() in ('minimize', 'run'): if due[key.lower()] != 0: print 'Ignoring former %s step count. Taking the one from line %d.' % (key.lower(), no+1) due[key.lower()] = int(value) firstline = min(firstline, no) basicline = False if key.lower() == 'temperature': basicline = False first_run_lines.append(line) if basicline: basiclines.append(line) # prepare folder structure try: folder = os.environ['PBS_JOBID'] except: folder = str(os.getpid()) try: os.mkdir(args.scratchdir + folder) except OSError: print 'Tempdir already exists.' exit(1) initial_cwd = os.getcwd() try: shutil.copytree(args.templatedir, args.scratchdir + folder + '/minimizedir') shutil.copytree(args.templatedir, args.scratchdir + folder + '/rundir') except: pass # prepare namd runs def start_section(mode, minlastbasename=None): mode_due = due[mode] run = 0 steps = 0 while steps < mode_due: print 'mode: %s, run %d' % (mode, run) # build namd config basename = '%s-%03d' % (mode, run) prevbasename = '%s-%03d' % (mode, run-1) wh = open(basename + '.conf', 'w') for line in basiclines: wh.write(line) if run == 0: if mode == 'run': wh.write('bincoordinates ../minimizedir/%s.coor\n' % minlastbasename) wh.write('binvelocities ../minimizedir/%s.vel\n' % minlastbasename) wh.write('extendedSystem ../minimizedir/%s.xsc\n' % minlastbasename) else: for line in first_run_lines: wh.write(line) else: wh.write('bincoordinates %s.coor\n' % prevbasename) wh.write('binvelocities %s.vel\n' % prevbasename) wh.write('extendedSystem %s.xsc\n' % prevbasename) wh.write('DCDfile %s.dcd\n' % basename) wh.write('restartname %s\n' % basename) wh.write('%s %d\n' % (mode, mode_due-steps)) wh.close() # start namd run wlog = open(basename + '.log', 'w') retval = subprocess.call(['namd2', basename + '.conf'], stdout=wlog, stderr=subprocess.STDOUT) wlog.close() if retval == 0: return basename # check whether the patch grid was reason for exit rlog = open(basename + '.log', 'r') patch_grid_is_reason = False last_restart_step = 0 for line in rlog.readlines(): if line.startswith('FATAL ERROR: Periodic cell has become too small for original patch grid!'): patch_grid_is_reason = True if line.startswith('WRITING EXTENDED SYSTEM TO RESTART FILE AT STEP'): last_restart_step = int(line[48:]) if not patch_grid_is_reason: print 'Unable to complete %s.' % mode print 'See %s/%sdir/%s.log for details.' % (args.scratchdir + folder, mode, basename) exit(1) # update counters run += 1 steps += last_restart_step return basename # perform calculations os.chdir(args.scratchdir + folder + '/minimizedir') minlastbasename = start_section('minimize') os.chdir(args.scratchdir + folder + '/rundir') start_section('run', minlastbasename) # retain data try: shutil.copytree(args.scratchdir + folder, args.outputdir + folder) except: print 'Error on copying results.' pass