def _setup_MD(dirname, deffnm='md', mdp=config.templates['md_OPLSAA.mdp'], struct=None, top='top/system.top', ndx=None, mainselection='"Protein"', qscript=config.qscript_template, qname=None, startdir=None, mdrun_opts="", budget=None, walltime=1/3., dt=0.002, runtime=1e3, multi=1, **mdp_kwargs): """Generic function to set up a ``mdrun`` MD simulation. See the user functions for usage. @param qname: name of the queing system, may be None. @param multi: setup multiple concurrent simulations. These are based upon deffnm being set, and a set of mdp / tpr are created named [deffnm]0.tpr. [deffnm]1.tpr, ... """ if struct is None: raise ValueError('struct must be set to a input structure') structure = realpath(struct) topology = realpath(top) try: index = realpath(ndx) except AttributeError: # (that's what realpath(None) throws...) index = None # None is handled fine below qname = mdp_kwargs.pop('sgename', qname) # compatibility for old scripts qscript = mdp_kwargs.pop('sge', qscript) # compatibility for old scripts qscript_template = config.get_template(qscript) mdp_template = config.get_template(mdp) nsteps = int(float(runtime)/float(dt)) mainindex = deffnm + '.ndx' final_structure = deffnm + '.pdb' # guess... really depends on templates,could also be DEFFNM.pdb # write the processed topology to the default output mdp_parameters = {'nsteps':nsteps, 'dt':dt} mdp_parameters.update(mdp_kwargs) add_mdp_includes(topology, mdp_parameters) # the basic result dictionary # depending on options, various bits might be added to this. result = {'struct': realpath(os.path.join(dirname, final_structure)), # guess 'top': topology, 'ndx': index, # possibly mainindex 'mainselection': mainselection, 'deffnm': deffnm, # return deffnm (tpr = deffnm.tpr!) } with in_dir(dirname): if not (mdp_parameters.get('Tcoupl','').lower() == 'no' or mainselection is None): logger.info("[%(dirname)s] Automatic adjustment of T-coupling groups" % vars()) # make index file in almost all cases; with mainselection == None the user # takes FULL control and also has to provide the template or index groups = make_main_index(structure, selection=mainselection, oldndx=index, ndx=mainindex) natoms = dict([(g['name'], float(g['natoms'])) for g in groups]) tc_group_names = ('__main__', '__environment__') # defined in make_main_index() try: x = natoms['__main__']/natoms['__environment__'] except KeyError: x = 0 # force using SYSTEM in code below wmsg = "Missing __main__ and/or __environment__ index group.\n" \ "This probably means that you have an atypical system. You can " \ "set mainselection=None and provide your own mdp and index files " \ "in order to set up temperature coupling.\n" \ "If no T-coupling is required then set Tcoupl='no'.\n" \ "For now we will just couple everything to 'System'." logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) if x < 0.1: # couple everything together tau_t = firstof(mdp_parameters.pop('tau_t', 0.1)) ref_t = firstof(mdp_parameters.pop('ref_t', 300)) # combine all in one T-coupling group mdp_parameters['tc-grps'] = 'System' mdp_parameters['tau_t'] = tau_t # this overrides the commandline! mdp_parameters['ref_t'] = ref_t # this overrides the commandline! mdp_parameters['gen-temp'] = mdp_parameters.pop('gen_temp', ref_t) wmsg = "Size of __main__ is only %.1f%% of __environment__ so " \ "we use 'System' for T-coupling and ref_t = %g K and " \ "tau_t = %g 1/ps (can be changed in mdp_parameters).\n" \ % (x * 100, ref_t, tau_t) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) else: # couple protein and bath separately n_tc_groups = len(tc_group_names) tau_t = asiterable(mdp_parameters.pop('tau_t', 0.1)) ref_t = asiterable(mdp_parameters.pop('ref_t', 300)) if len(tau_t) != n_tc_groups: tau_t = n_tc_groups * [tau_t[0]] wmsg = "%d coupling constants should have been supplied for tau_t. "\ "Using %f 1/ps for all of them." % (n_tc_groups, tau_t[0]) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) if len(ref_t) != n_tc_groups: ref_t = n_tc_groups * [ref_t[0]] wmsg = "%d temperatures should have been supplied for ref_t. "\ "Using %g K for all of them." % (n_tc_groups, ref_t[0]) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) mdp_parameters['tc-grps'] = tc_group_names mdp_parameters['tau_t'] = tau_t mdp_parameters['ref_t'] = ref_t mdp_parameters['gen-temp'] = mdp_parameters.pop('gen_temp', ref_t[0]) index = realpath(mainindex) if mdp_parameters.get('Tcoupl','').lower() == 'no': logger.info("Tcoupl == no: disabling all temperature coupling mdp options") mdp_parameters['tc-grps'] = "" mdp_parameters['tau_t'] = "" mdp_parameters['ref_t'] = "" mdp_parameters['gen-temp'] = "" if mdp_parameters.get('Pcoupl','').lower() == 'no': logger.info("Pcoupl == no: disabling all pressure coupling mdp options") mdp_parameters['tau_p'] = "" mdp_parameters['ref_p'] = "" mdp_parameters['compressibility'] = "" # do multiple concurrent simulations - ensemble sampling if multi > 1: for i in range(multi): new_mdp = deffnm + str(i) + ".mdp" mdout = deffnm + "out" + str(i) + ".mdp" pp = "processed" + str(i) + ".top" tpr = deffnm + str(i) + ".tpr" # doing ensemble sampling, so give differnt seeds for each one # if we are using 32 bit gromacs, make seeds are are 32 bit even on # 64 bit machine mdp_parameters["andersen_seed"] = random.randint(0,2**31) mdp_parameters["gen_seed"] = random.randint(0,2**31) mdp_parameters["ld_seed"] = random.randint(0,2**31) unprocessed = gromacs.cbook.edit_mdp(mdp_template, new_mdp=new_mdp, **mdp_parameters) check_mdpargs(unprocessed) gromacs.grompp(f=new_mdp, p=topology, c=structure, n=index, o=tpr, po=mdout, pp=pp, **unprocessed) # only add multi to result if we really are doing multiple runs result["multi"] = multi else: new_mdp = deffnm + '.mdp' tpr = deffnm + '.tpr' unprocessed = gromacs.cbook.edit_mdp(mdp_template, new_mdp=new_mdp, **mdp_parameters) check_mdpargs(unprocessed) gromacs.grompp(f=new_mdp, p=topology, c=structure, n=index, o=tpr, po="mdout.mdp", pp="processed.top", **unprocessed) # generate scripts for queing system if requested if qname is not None: runscripts = gromacs.qsub.generate_submit_scripts( qscript_template, deffnm=deffnm, jobname=qname, budget=budget, startdir=startdir, mdrun_opts=mdrun_opts, walltime=walltime) result["qscript"] =runscripts logger.info("[%(dirname)s] All files set up for a run time of %(runtime)g ps " "(dt=%(dt)g, nsteps=%(nsteps)g)" % vars()) result.update(mdp_kwargs) # return extra mdp args so that one can use them for prod run result.pop('define', None) # but make sure that -DPOSRES does not stay... return result
def solvate(struct='top/protein.pdb', top='top/system.top', distance=0.9, boxtype='dodecahedron', concentration=0, cation='NA', anion='CL', water='spc', solvent_name='SOL', with_membrane=False, ndx = 'main.ndx', mainselection = '"Protein"', dirname='solvate', **kwargs): """Put protein into box, add water, add counter-ions. Currently this really only supports solutes in water. If you need to embedd a protein in a membrane then you will require more sophisticated approaches. However, you *can* supply a protein already inserted in a bilayer. In this case you will probably want to set *distance* = ``None`` and also enable *with_membrane* = ``True`` (using extra big vdw radii for typical lipids). .. Note:: The defaults are suitable for solvating a globular protein in a fairly tight (increase *distance*!) dodecahedral box. :Arguments: *struct* : filename pdb or gro input structure *top* : filename Gromacs topology *distance* : float When solvating with water, make the box big enough so that at least *distance* nm water are between the solute *struct* and the box boundary. Set *boxtype* to ``None`` in order to use a box size in the input file (gro or pdb). *boxtype* or *bt*: string Any of the box types supported by :class:`~gromacs.tools.Editconf` (triclinic, cubic, dodecahedron, octahedron). Set the box dimensions either with *distance* or the *box* and *angle* keywords. If set to ``None`` it will ignore *distance* and use the box inside the *struct* file. *bt* overrides the value of *boxtype*. *box* List of three box lengths [A,B,C] that are used by :class:`~gromacs.tools.Editconf` in combination with *boxtype* (``bt`` in :program:`editconf`) and *angles*. Setting *box* overrides *distance*. *angles* List of three angles (only necessary for triclinic boxes). *concentration* : float Concentration of the free ions in mol/l. Note that counter ions are added in excess of this concentration. *cation* and *anion* : string Molecule names of the ions. This depends on the chosen force field. *water* : string Name of the water model; one of "spc", "spce", "tip3p", "tip4p". This should be appropriate for the chosen force field. If an alternative solvent is required, simply supply the path to a box with solvent molecules (used by :func:`~gromacs.genbox`'s *cs* argument) and also supply the molecule name via *solvent_name*. *solvent_name* Name of the molecules that make up the solvent (as set in the itp/top). Typically needs to be changed when using non-standard/non-water solvents. ["SOL"] *with_membrane* : bool ``True``: use special ``vdwradii.dat`` with 0.1 nm-increased radii on lipids. Default is ``False``. *ndx* : filename How to name the index file that is produced by this function. *mainselection* : string A string that is fed to :class:`~gromacs.tools.Make_ndx` and which should select the solute. *dirname* : directory name Name of the directory in which all files for the solvation stage are stored. *includes* List of additional directories to add to the mdp include path *kwargs* Additional arguments are passed on to :class:`~gromacs.tools.Editconf` or are interpreted as parameters to be changed in the mdp file. """ structure = realpath(struct) topology = realpath(top) # arguments for editconf that we honour editconf_keywords = ["box", "bt", "angles", "c", "center", "aligncenter", "align", "translate", "rotate", "princ"] editconf_kwargs = dict((k,kwargs.pop(k,None)) for k in editconf_keywords) editconf_boxtypes = ["triclinic", "cubic", "dodecahedron", "octahedron", None] # needed for topology scrubbing scrubber_kwargs = {'marker': kwargs.pop('marker',None)} # sanity checks and argument dependencies bt = editconf_kwargs.pop('bt') boxtype = bt if bt else boxtype # bt takes precedence over boxtype if not boxtype in editconf_boxtypes: msg = "Unsupported boxtype %(boxtype)r: Only %(boxtypes)r are possible." % vars() logger.error(msg) raise ValueError(msg) if editconf_kwargs['box']: distance = None # if box is set then user knows what she is doing... # handle additional include directories (kwargs are also modified!) mdp_kwargs = add_mdp_includes(topology, kwargs) if water.lower() in ('spc', 'spce'): water = 'spc216' elif water.lower() == 'tip3p': water = 'spc216' logger.warning("TIP3P water model selected: using SPC equilibrated box " "for initial solvation because it is a reasonable starting point " "for any 3-point model. EQUILIBRATE THOROUGHLY!") # By default, grompp should not choke on a few warnings because at # this stage the user cannot do much about it (can be set to any # value but is kept undocumented...) grompp_maxwarn = kwargs.pop('maxwarn',10) # clean topology (if user added the marker; the default marker is # ; Gromacs auto-generated entries follow: n_removed = gromacs.cbook.remove_molecules_from_topology(topology, **scrubber_kwargs) with in_dir(dirname): logger.info("[%(dirname)s] Solvating with water %(water)r..." % vars()) if boxtype is None: hasBox = False ext = os.path.splitext(structure)[1] if ext == '.gro': hasBox = True elif ext == '.pdb': with open(structure) as struct: for line in struct: if line.startswith('CRYST'): hasBox = True break if not hasBox: msg = "No box data in the input structure %(structure)r and boxtype is set to None" % vars() logger.exception(msg) raise MissingDataError(msg) distance = boxtype = None # ensures that editconf just converts editconf_kwargs.update({'f': structure, 'o': 'boxed.gro', 'bt': boxtype, 'd': distance}) gromacs.editconf(**editconf_kwargs) if with_membrane: vdwradii_dat = get_lipid_vdwradii() # need to clean up afterwards logger.info("Using special vdW radii for lipids %r" % vdw_lipid_resnames) try: gromacs.genbox(p=topology, cp='boxed.gro', cs=water, o='solvated.gro') except: if with_membrane: # remove so that it's not picked up accidentally gromacs.utilities.unlink_f(vdwradii_dat) raise logger.info("Solvated system with %s", water) with open('none.mdp','w') as mdp: mdp.write('; empty mdp file\ninclude = %(include)s\nrcoulomb = 1\nrvdw = 1\nrlist = 1\n' % mdp_kwargs) qtotgmx = gromacs.cbook.grompp_qtot(f='none.mdp', o='topol.tpr', c='solvated.gro', p=topology, stdout=False, maxwarn=grompp_maxwarn) qtot = round(qtotgmx) logger.info("[%(dirname)s] After solvation: total charge qtot = %(qtotgmx)r = %(qtot)r" % vars()) if concentration != 0: logger.info("[%(dirname)s] Adding ions for c = %(concentration)f M..." % vars()) # target concentration of free ions c ==> # N = N_water * c/c_water # add ions for concentration to the counter ions (counter ions are less free) # # get number of waters (count OW ... works for SPC*, TIP*P water models) rc,output,junk = gromacs.make_ndx(f='topol.tpr', o='ow.ndx', input=('keep 0', 'del 0', 'a OW*', 'name 0 OW', '', 'q'), stdout=False) groups = gromacs.cbook.parse_ndxlist(output) gdict = dict([(g['name'], g) for g in groups]) # overkill... N_water = gdict['OW']['natoms'] # ... but dict lookup is nice N_ions = int(N_water * concentration/CONC_WATER) # number of monovalents else: N_ions = 0 # neutralize (or try -neutral switch of genion???) n_cation = n_anion = 0 if qtot > 0: n_anion = int(abs(qtot)) elif qtot < 0: n_cation = int(abs(qtot)) n_cation += N_ions n_anion += N_ions if n_cation != 0 or n_anion != 0: # sanity check: assert qtot + n_cation - n_anion < 1e-6 logger.info("[%(dirname)s] Adding n_cation = %(n_cation)d and n_anion = %(n_anion)d ions..." % vars()) gromacs.genion(s='topol.tpr', o='ionized.gro', p=topology, pname=cation, nname=anion, np=n_cation, nn=n_anion, input=solvent_name) else: # fake ionized file ... makes it easier to continue without too much fuzz try: os.unlink('ionized.gro') except OSError, err: if err.errno != errno.ENOENT: raise os.symlink('solvated.gro', 'ionized.gro') qtot = gromacs.cbook.grompp_qtot(f='none.mdp', o='ionized.tpr', c='ionized.gro', p=topology, stdout=False, maxwarn=grompp_maxwarn) if abs(qtot) > 1e-4: wmsg = "System has non-zero total charge qtot = %(qtot)g e." % vars() warnings.warn(wmsg, category=BadParameterWarning) logger.warn(wmsg) # make main index try: make_main_index('ionized.tpr', selection=mainselection, ndx=ndx) except GromacsError, err: # or should I rather fail here? wmsg = "Failed to make main index file %r ... maybe set mainselection='...'.\n"\ "The error message was:\n%s\n" % (ndx, str(err)) logger.warn(wmsg) warnings.warn(wmsg, category=GromacsFailureWarning)
def energy_minimize(dirname='em', mdp=config.templates['em.mdp'], struct='solvate/ionized.pdb', top='top/system.top', output='em.pdb', deffnm="em", mdrunner=None, **kwargs): """Energy minimize the system. This sets up the system (creates run input files) and also runs ``mdrun_d``. Thus it can take a while. Additional itp files should be in the same directory as the top file. Many of the keyword arguments below already have sensible values. :Keywords: *dirname* set up under directory dirname [em] *struct* input structure (gro, pdb, ...) [solvate/ionized.pdb] *output* output structure (will be put under dirname) [em.pdb] *deffnm* default name for mdrun-related files [em] *top* topology file [top/system.top] *mdp* mdp file (or use the template) [templates/em.mdp] *includes* additional directories to search for itp files *mdrunner* :class:`gromacs.run.MDrunner` class; by defauly we just try :func:`gromacs.mdrun_d` and :func:`gromacs.mdrun` but a MDrunner class gives the user the ability to run mpi jobs etc. [None] *kwargs* remaining key/value pairs that should be changed in the template mdp file, eg ``nstxtcout=250, nstfout=250``. .. note:: If :func:`~gromacs.mdrun_d` is not found, the function falls back to :func:`~gromacs.mdrun` instead. """ structure = realpath(struct) topology = realpath(top) mdp_template = config.get_template(mdp) deffnm = deffnm.strip() # write the processed topology to the default output kwargs.setdefault('pp', 'processed.top') # filter some kwargs that might come through when feeding output # from previous stages such as solvate(); necessary because *all* # **kwargs must be *either* substitutions in the mdp file *or* valid # command line parameters for ``grompp``. kwargs.pop('ndx', None) # mainselection is not used but only passed through; right now we # set it to the default that is being used in all argument lists # but that is not pretty. TODO. mainselection = kwargs.pop('mainselection', '"Protein"') # only interesting when passed from solvate() qtot = kwargs.pop('qtot', 0) mdp = deffnm+'.mdp' tpr = deffnm+'.tpr' logger.info("[%(dirname)s] Energy minimization of struct=%(struct)r, top=%(top)r, mdp=%(mdp)r ..." % vars()) add_mdp_includes(topology, kwargs) if qtot != 0: # At the moment this is purely user-reported and really only here because # it might get fed into the function when using the keyword-expansion pipeline # usage paradigm. wmsg = "Total charge was reported as qtot = %(qtot)g <> 0; probably a problem." % vars() logger.warn(wmsg) warnings.warn(wmsg, category=BadParameterWarning) with in_dir(dirname): unprocessed = gromacs.cbook.edit_mdp(mdp_template, new_mdp=mdp, **kwargs) check_mdpargs(unprocessed) gromacs.grompp(f=mdp, o=tpr, c=structure, p=topology, **unprocessed) mdrun_args = dict(v=True, stepout=10, deffnm=deffnm, c=output) if mdrunner is None: try: gromacs.mdrun_d(**mdrun_args) except (AttributeError, OSError): # fall back to mdrun if no double precision binary wmsg = "No 'mdrun_d' binary found so trying 'mdrun' instead.\n"\ "(Note that energy minimization runs better with mdrun_d.)" logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) gromacs.mdrun(**mdrun_args) else: # user wants full control and provides simulation.MDrunner **class** # NO CHECKING --- in principle user can supply any callback they like mdrun = mdrunner(**mdrun_args) mdrun.run() # em.gro --> gives 'Bad box in file em.gro' warning --- why?? # --> use em.pdb instead. if not os.path.exists(output): errmsg = "Energy minimized system NOT produced." logger.error(errmsg) raise GromacsError(errmsg) final_struct = realpath(output) logger.info("[%(dirname)s] energy minimized structure %(final_struct)r" % vars()) return {'struct': final_struct, 'top': topology, 'mainselection': mainselection, }
def _setup_MD( dirname, deffnm="md", mdp=config.templates["md_OPLSAA.mdp"], struct=None, top="top/system.top", ndx=None, mainselection='"Protein"', qscript=config.qscript_template, qname=None, startdir=None, mdrun_opts="", budget=None, walltime=1 / 3.0, dt=0.002, runtime=1e3, **mdp_kwargs ): """Generic function to set up a ``mdrun`` MD simulation. See the user functions for usage. """ if struct is None: raise ValueError("struct must be set to a input structure") structure = realpath(struct) topology = realpath(top) try: index = realpath(ndx) except AttributeError: # (that's what realpath(None) throws...) index = None # None is handled fine below qname = mdp_kwargs.pop("sgename", qname) # compatibility for old scripts qscript = mdp_kwargs.pop("sge", qscript) # compatibility for old scripts qscript_template = config.get_template(qscript) mdp_template = config.get_template(mdp) nsteps = int(float(runtime) / float(dt)) mdp = deffnm + ".mdp" tpr = deffnm + ".tpr" mainindex = deffnm + ".ndx" final_structure = deffnm + ".gro" # guess... really depends on templates,could also be DEFFNM.pdb # write the processed topology to the default output mdp_parameters = {"nsteps": nsteps, "dt": dt, "pp": "processed.top"} mdp_parameters.update(mdp_kwargs) add_mdp_includes(topology, mdp_parameters) logger.info("[%(dirname)s] input mdp = %(mdp_template)r", vars()) with in_dir(dirname): if not (mdp_parameters.get("Tcoupl", "").lower() == "no" or mainselection is None): logger.info("[%(dirname)s] Automatic adjustment of T-coupling groups" % vars()) # make index file in almost all cases; with mainselection == None the user # takes FULL control and also has to provide the template or index groups = make_main_index(structure, selection=mainselection, oldndx=index, ndx=mainindex) natoms = dict([(g["name"], float(g["natoms"])) for g in groups]) tc_group_names = ("__main__", "__environment__") # defined in make_main_index() try: x = natoms["__main__"] / natoms["__environment__"] except KeyError: x = 0 # force using SYSTEM in code below wmsg = ( "Missing __main__ and/or __environment__ index group.\n" "This probably means that you have an atypical system. You can " "set mainselection=None and provide your own mdp and index files " "in order to set up temperature coupling.\n" "If no T-coupling is required then set Tcoupl='no'.\n" "For now we will just couple everything to 'System'." ) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) if x < 0.1: # couple everything together tau_t = firstof(mdp_parameters.pop("tau_t", 0.1)) ref_t = firstof(mdp_parameters.pop("ref_t", 300)) # combine all in one T-coupling group mdp_parameters["tc-grps"] = "System" mdp_parameters["tau_t"] = tau_t # this overrides the commandline! mdp_parameters["ref_t"] = ref_t # this overrides the commandline! mdp_parameters["gen-temp"] = mdp_parameters.pop("gen_temp", ref_t) wmsg = ( "Size of __main__ is only %.1f%% of __environment__ so " "we use 'System' for T-coupling and ref_t = %g K and " "tau_t = %g 1/ps (can be changed in mdp_parameters).\n" % (x * 100, ref_t, tau_t) ) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) else: # couple protein and bath separately n_tc_groups = len(tc_group_names) tau_t = asiterable(mdp_parameters.pop("tau_t", 0.1)) ref_t = asiterable(mdp_parameters.pop("ref_t", 300)) if len(tau_t) != n_tc_groups: tau_t = n_tc_groups * [tau_t[0]] wmsg = ( "%d coupling constants should have been supplied for tau_t. " "Using %f 1/ps for all of them." % (n_tc_groups, tau_t[0]) ) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) if len(ref_t) != n_tc_groups: ref_t = n_tc_groups * [ref_t[0]] wmsg = "%d temperatures should have been supplied for ref_t. " "Using %g K for all of them." % ( n_tc_groups, ref_t[0], ) logger.warn(wmsg) warnings.warn(wmsg, category=AutoCorrectionWarning) mdp_parameters["tc-grps"] = tc_group_names mdp_parameters["tau_t"] = tau_t mdp_parameters["ref_t"] = ref_t mdp_parameters["gen-temp"] = mdp_parameters.pop("gen_temp", ref_t[0]) index = realpath(mainindex) if mdp_parameters.get("Tcoupl", "").lower() == "no": logger.info("Tcoupl == no: disabling all temperature coupling mdp options") mdp_parameters["tc-grps"] = "" mdp_parameters["tau_t"] = "" mdp_parameters["ref_t"] = "" mdp_parameters["gen-temp"] = "" if mdp_parameters.get("Pcoupl", "").lower() == "no": logger.info("Pcoupl == no: disabling all pressure coupling mdp options") mdp_parameters["tau_p"] = "" mdp_parameters["ref_p"] = "" mdp_parameters["compressibility"] = "" unprocessed = gromacs.cbook.edit_mdp(mdp_template, new_mdp=mdp, **mdp_parameters) check_mdpargs(unprocessed) gromacs.grompp(f=mdp, p=topology, c=structure, n=index, o=tpr, **unprocessed) runscripts = gromacs.qsub.generate_submit_scripts( qscript_template, deffnm=deffnm, jobname=qname, budget=budget, startdir=startdir, mdrun_opts=mdrun_opts, walltime=walltime, ) logger.info("[%(dirname)s] output mdp = %(mdp)r", vars()) logger.info("[%(dirname)s] output ndx = %(ndx)r", vars()) logger.info("[%(dirname)s] output tpr = %(tpr)r", vars()) logger.info("[%(dirname)s] output runscripts = %(runscripts)r", vars()) logger.info( "[%(dirname)s] All files set up for a run time of %(runtime)g ps " "(dt=%(dt)g, nsteps=%(nsteps)g)" % vars() ) kwargs = { "struct": realpath(os.path.join(dirname, final_structure)), # guess "top": topology, "ndx": index, # possibly mainindex "qscript": runscripts, "mainselection": mainselection, "deffnm": deffnm, # return deffnm (tpr = deffnm.tpr!) } kwargs.update(mdp_kwargs) # return extra mdp args so that one can use them for prod run return kwargs