Source code for exatomic.gaussian.inputs

# -*- coding: utf-8 -*-
# Copyright (c) 2015-2022, Exa Analytics Development Team
# Distributed under the terms of the Apache License 2.0
"""
Gaussian Input Generator
###########################
Editor class and helper function for writing input files.
"""
import os
import numpy as np
from .editor import Editor
from exatomic import __version__


_template = """\
{link0}
{route}

{title}

{charge} {mult}
{atom}

{basis}{ecp}{options}

"""


[docs]class Input(Editor):
[docs] @classmethod def from_universe(cls, uni, link0='', route='#P HF/6-31G(d)', title='', name='', charge=0, mult=1, basis='', ecp='', options='', writedir=None): """ Generate an input Editor from a universe. Arguments can either be strings or iterables of key, value pairs (dict, list, tuple) and/or just strings. """ kwargs = {} kwargs['mult'] = mult kwargs['charge'] = charge kwargs['atom'] = uni.atom.to_xyz()[:-1] kwargs['link0'] = _handle_args('link0', link0) kwargs['route'] = _handle_args('route', route) kwargs['basis'] = _handle_args('basis', basis) kwargs['ecp'] = _handle_args('ecp', ecp) kwargs['options'] = options if name and not title: title = name elif title and not name: name = title + '.g09' kwargs['title'] = '{} -- generated by exatomic.v{}'.format( title, __version__) fl = cls(_template) fl.name = name fl.format(inplace=True, **kwargs) if writedir: if (not name and not title): print('Must supply name or title to write input file.') else: if not writedir.endswith(os.sep): writedir += os.sep fl.write(writedir + fl.name) return fl
def __init__(self, *args, **kwargs): super(Input, self).__init__(*args, **kwargs)
def _handle_args(kwarg, args): if isinstance(args, str): return args if isinstance(args, dict): args = args.items() clean = [] for arg in args: try: k, v = arg clean.append((k, v)) except ValueError: clean.append((str(arg), '')) if kwarg == 'link0': return ''.join([''.join(['%', key, '=', str(arg), '\n']) for key, arg in clean])[:-1] elif kwarg == 'route': return ''.join(['#P '] + [''.join([key, '(', arg, ') ']) if arg else key + ' ' for key, arg in clean]) elif kwarg == 'basis': return ''.join([''.join([key, ' 0\n', arg, '\n****\n']) if arg else key + '\n\n' for key, arg in clean]) elif kwarg == 'ecp': return ''.join([''.join([key, ' 0\n', arg, '\n']) for key, arg in clean]) else: raise NotImplementedError('{} keyword is not currently ' 'supported'.format(key))
[docs]def tuning_inputs(uni, name, mult, charge, basis, gammas, alphas, route=None, link0=None, nproc=4, mem=4, field=None, writedir=None, deep=False): """ Provided a universe, generate input files for functional tuning. Includes input keywords for orbital visualization within exatomic. Assumes you will copy restart checkpoint files to have the same names as the input files. Args: uni (exatomic.container.Universe): molecular specification name (str): prefix for job names mult (int): spin multiplicity charge (int): charge of the system basis (list): tuples of atomic symbol, string of basis name gammas (iter): values of range separation parameter (omega) alphas (iter): fractions of Hartree-Fock in the short range route (list): strings or tuples of keyword, value pairs link0 (list): strings or tuples of keyword, value pairs nproc (int): number of processors mem (int): memory (in GB) writedir (str): directory path to write input files Returns: editors (list): input files as exatomic.exa.Editors """ if route is None: route = [('Pop', 'full')] rangedt = """ IOP(3/107={w}00000) IOP(3/108={w}00000) IOP(3/130={a}) IOP(3/131={a}) IOP(3/119={b}00000) IOP(3/120={b}00000)""" globalt = """ IOP(3/76={b}{a})""" # Make the obtuse strings that Gaussian requires gammas = [str(int(np.round(g * 10000, decimals=4))).zfill(5) for g in gammas] alphas = [str(int(np.round(a * 10000, decimals=4))).zfill(5) for a in alphas] betas = [str(10000 - int(a)).zfill(5) for a in alphas] # A systematic input file naming scheme chgnms = ['cat', 'neut', 'an'] # Auxiliary job information chgs = [charge + 1, charge, charge - 1] mults = [2, 1, 2] if mult == 1 else [mult - 1, mult, mult + 1] link_opts = [('NProc', nproc), ('Mem', str(mem) + 'gb')] ecp = False for bas in basis: try: #atom, bas = bas _, bas = bas ecp = 'ecp' in bas.lower() except ValueError: ecp = 'ecp' in bas.lower() gen = 'Genecp' if ecp else 'Gen' editors = [] for gamma in gammas: # Some recommended default keywords for Gaussian DFT jobs func = 'LC-PBEPBE/{}' if not np.isclose(int(gamma)/10000, 0) else 'PBEPBE/{}' if len(basis) == 1: route_opts = [(func.format(basis[0][1]), '')] dobasis = False else: route_opts = [(func.format(gen), '')] dobasis = True route_opts = route_opts + [ ('gfinput', ''), ('Guess', 'read'), ('IOP(3/75=5)', '')] for alpha, beta in zip(alphas, betas): for chgnm, chg, mult in zip(chgnms, chgs, mults): ndecgam = max(len(str(int(gamma) / 10000)) - 2, 2) ndecalp = max(len(str(int(alpha) / 10000)) - 2, 2) gam = ('{:.' + str(ndecgam) + 'f}').format(int(gamma) / 10000) alp = ('{:.' + str(ndecalp) + 'f}').format(int(alpha) / 10000) jobname = '-'.join([name, gam, alp, chgnm]) this_link = [('chk', jobname + '.chk')] if np.isclose(int(gamma), 0): this_route = [globalt.format(a=alpha, b=beta)] else: this_route = [rangedt.format(w=gamma, a=alpha, b=beta)] opts = {'charge': chg, 'mult': mult, 'title': jobname, 'writedir': writedir} if dobasis: opts['basis'] = basis if field is not None: opts['postatom'] = field this_route += ['Charge'] if isinstance(route, list): opts['route'] = route_opts + route + this_route else: opts['route'] = route_opts + this_route if isinstance(link0, list): opts['link0'] = link_opts + link0 + this_link else: opts['link0'] = link_opts + this_link editors.append(Input.from_universe(uni, **opts)) return editors
[docs]def functional_inputs(uni, name, mult, charge, basis, funcnames=None, nproc=4, mem=4, field=None, writedir=None): """ Provided a universe, generate input files to analyze the delocalization error inherent in functionals defined in funcnames. Includes input keywords for orbital visualization within exatomic. Assumes you will copy restart checkpoint files to have the same names as the input files. Args: uni (exatomic.container.Universe): molecular specification name (str): prefix for job names mult (int): spin multiplicity charge (int): charge of the system basis (list): tuples of atomic symbol, string of basis name funcnames (dict): Functional name aliases (default {"pbe": "PBEPBE"}) gammas (iter): values of range separation parameter (omega) alphas (iter): fractions of Hartree-Fock in the short range route (list): strings or tuples of keyword, value pairs link0 (list): strings or tuples of keyword, value pairs nproc (int): number of processors mem (int): memory (in GB) writedir (str): directory path to write input files Returns: editors (list): input files as exatomic.exa.Editors """ if funcnames is None: funcnames = {'pbe': 'PBEPBE'} editors = [] link_opts = [('NProc', nproc), ('Mem', mem)] chgnms = ['cat', 'neut', 'an'] chgs = [charge + 1, charge, charge - 1] mults = [2, 1, 2] if mult == 1 else [mult - 1, mult, mult + 1] for func in funcnames: route_opts = [funcnames[func] + '/Genecp', 'gfinput', ('Guess', 'read'), 'IOP(3/75=5)', ('Pop', 'full')] for chgnm, chg, mul in zip(chgnms, chgs, mults): jobname = '-'.join([chgnm, chg, mul]) #this_link = [('chk', jobname + '.chk')] field = field if field is not None else '' #this_route = ['Charge'] if field else [] # opts unused? #opts = {'title': jobname, 'mult': mul, 'charge': chg, # 'link0': link_opts + this_link, # 'route': route_opts + this_route, # 'writedir': writedir, 'postatom': field, # 'basis': basis} editors.append(Input.from_universe(uni, **args)) return editors