#
#@BEGIN LICENSE
#
# PSI4: an ab initio quantum chemistry software package
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#@END LICENSE
#
from __future__ import print_function
import math
import re
import psi4
import qcdb
import p4util
from p4util.exceptions import *
from procedures import *
def _method_exists(ptype, method_name):
r"""
Quick check to see if this method exists, if it does not exist we raise a convenient flag.
"""
if method_name not in procedures[ptype].keys():
alternatives = ""
alt_method_name = p4util.text.find_approximate_string_matches(method_name,
procedures[ptype].keys(), 2)
if len(alt_method_name) > 0:
alternatives = " Did you mean? %s" % (" ".join(alt_method_name))
Cptype = ptype[0].upper() + ptype[1:]
raise ValidationError('%s method "%s" is not available.%s' % (Cptype, method_name, alternatives))
def _set_convergence_criterion(ptype, method_name, scf_Ec, pscf_Ec, scf_Dc, pscf_Dc, gen_Ec, verbose=1):
r"""
This function will set local SCF and global energy convergence criterion
to the defaults listed at:
http://www.psicode.org/psi4manual/master/scf.html#convergence-and-
algorithm-defaults. SCF will be converged more tightly if a post-SCF
method is select (pscf_Ec, and pscf_Dc) else the looser (scf_Ec, and
scf_Dc convergence criterion will be used).
ptype - Procedure type (energy, gradient, etc). Nearly always test on
procedures['energy'] since that's guaranteed to exist for a method.
method_name - Name of the method
scf_Ec - E convergence criterion for scf target method
pscf_Ec - E convergence criterion for scf of post-scf target method
scf_Dc - D convergence criterion for scf target method
pscf_Dc - D convergence criterion for scf of post-scf target method
gen_Ec - E convergence criterion for post-scf target method
"""
optstash = p4util.OptionsState(
['SCF', 'E_CONVERGENCE'],
['SCF', 'D_CONVERGENCE'],
['E_CONVERGENCE'])
# Kind of want to move this out of here
_method_exists(ptype, method_name)
if verbose >= 2:
print(' Setting convergence', end=' ')
# Set method-dependent scf convergence criteria, check against energy routines
if not psi4.has_option_changed('SCF', 'E_CONVERGENCE'):
if procedures['energy'][method_name] in [proc.run_scf, proc.run_dft]:
psi4.set_local_option('SCF', 'E_CONVERGENCE', scf_Ec)
if verbose >= 2:
print(scf_Ec, end=' ')
else:
psi4.set_local_option('SCF', 'E_CONVERGENCE', pscf_Ec)
if verbose >= 2:
print(pscf_Ec, end=' ')
else:
if verbose >= 2:
print('CUSTOM', psi4.get_option('SCF', 'E_CONVERGENCE'), end=' ')
if not psi4.has_option_changed('SCF', 'D_CONVERGENCE'):
if procedures['energy'][method_name] in [proc.run_scf, proc.run_dft]:
psi4.set_local_option('SCF', 'D_CONVERGENCE', scf_Dc)
if verbose >= 2:
print(scf_Dc, end=' ')
else:
psi4.set_local_option('SCF', 'D_CONVERGENCE', pscf_Dc)
if verbose >= 2:
print(pscf_Dc, end=' ')
else:
if verbose >= 2:
print('CUSTOM', psi4.get_option('SCF', 'D_CONVERGENCE'), end=' ')
# Set post-scf convergence criteria (global will cover all correlated modules)
if not psi4.has_global_option_changed('E_CONVERGENCE'):
if procedures['energy'][method_name] not in [proc.run_scf, proc.run_dft]:
psi4.set_global_option('E_CONVERGENCE', gen_Ec)
if verbose >= 2:
print(gen_Ec, end=' ')
else:
if procedures['energy'][method_name] not in [proc.run_scf, proc.run_dft]:
if verbose >= 2:
print('CUSTOM', psi4.get_global_option('E_CONVERGENCE'), end=' ')
if verbose >= 2:
print('')
return optstash
[docs]def parse_arbitrary_order(name):
r"""Function to parse name string into a method family like CI or MRCC and specific
level information like 4 for CISDTQ or MRCCSDTQ.
"""
name = name.lower()
# matches 'mrccsdt(q)'
if name.startswith('mrcc'):
# avoid undoing fn's good work when called twice
if name == 'mrcc':
return name, None
# grabs 'sdt(q)'
ccfullname = name[4:]
# A negative order indicates perturbative method
methods = {
'sd' : { 'method': 1, 'order': 2, 'fullname': 'CCSD' },
'sdt' : { 'method': 1, 'order': 3, 'fullname': 'CCSDT' },
'sdtq' : { 'method': 1, 'order': 4, 'fullname': 'CCSDTQ' },
'sdtqp' : { 'method': 1, 'order': 5, 'fullname': 'CCSDTQP' },
'sdtqph' : { 'method': 1, 'order': 6, 'fullname': 'CCSDTQPH' },
'sd(t)' : { 'method': 3, 'order': -3, 'fullname': 'CCSD(T)' },
'sdt(q)' : { 'method': 3, 'order': -4, 'fullname': 'CCSDT(Q)' },
'sdtq(p)' : { 'method': 3, 'order': -5, 'fullname': 'CCSDTQ(P)' },
'sdtqp(h)' : { 'method': 3, 'order': -6, 'fullname': 'CCSDTQP(H)' },
'sd(t)_l' : { 'method': 4, 'order': -3, 'fullname': 'CCSD(T)_L' },
'sdt(q)_l' : { 'method': 4, 'order': -4, 'fullname': 'CCSDT(Q)_L' },
'sdtq(p)_l' : { 'method': 4, 'order': -5, 'fullname': 'CCSDTQ(P)_L' },
'sdtqp(h)_l' : { 'method': 4, 'order': -6, 'fullname': 'CCSDTQP(H)_L' },
'sdt-1a' : { 'method': 5, 'order': 3, 'fullname': 'CCSDT-1a' },
'sdtq-1a' : { 'method': 5, 'order': 4, 'fullname': 'CCSDTQ-1a' },
'sdtqp-1a' : { 'method': 5, 'order': 5, 'fullname': 'CCSDTQP-1a' },
'sdtqph-1a' : { 'method': 5, 'order': 6, 'fullname': 'CCSDTQPH-1a' },
'sdt-1b' : { 'method': 6, 'order': 3, 'fullname': 'CCSDT-1b' },
'sdtq-1b' : { 'method': 6, 'order': 4, 'fullname': 'CCSDTQ-1b' },
'sdtqp-1b' : { 'method': 6, 'order': 5, 'fullname': 'CCSDTQP-1b' },
'sdtqph-1b' : { 'method': 6, 'order': 6, 'fullname': 'CCSDTQPH-1b' },
'2' : { 'method': 7, 'order': 2, 'fullname': 'CC2' },
'3' : { 'method': 7, 'order': 3, 'fullname': 'CC3' },
'4' : { 'method': 7, 'order': 4, 'fullname': 'CC4' },
'5' : { 'method': 7, 'order': 5, 'fullname': 'CC5' },
'6' : { 'method': 7, 'order': 6, 'fullname': 'CC6' },
'sdt-3' : { 'method': 8, 'order': 3, 'fullname': 'CCSDT-3' },
'sdtq-3' : { 'method': 8, 'order': 4, 'fullname': 'CCSDTQ-3' },
'sdtqp-3' : { 'method': 8, 'order': 5, 'fullname': 'CCSDTQP-3' },
'sdtqph-3' : { 'method': 8, 'order': 6, 'fullname': 'CCSDTQPH-3' }
}
# looks for 'sdt(q)' in dictionary
if ccfullname in methods:
return 'mrcc', methods[ccfullname]
else:
raise ValidationError('MRCC method \'%s\' invalid.' % (name))
elif re.match(r'^[a-z]+\d+$', name):
decompose = re.compile(r'^([a-z]+)(\d+)$').match(name)
namestump = decompose.group(1)
namelevel = int(decompose.group(2))
if namestump in ['mp', 'zapt', 'ci']:
# Let mp2, mp3, mp4 pass through to select functions
if namestump == 'mp' and namelevel in [2, 3, 4]:
return name, None
# Otherwise return method and order
else:
return namestump, namelevel
else:
return name, None
else:
return name, None
[docs]def parse_cotton_irreps(irrep, point_group):
r"""Function to return validated Cotton ordering index for molecular
*point_group* from string or integer irreducible representation *irrep*.
"""
cotton = {
'c1': {
'a': 1,
'1': 1
},
'ci': {
'ag': 1,
'au': 2,
'1': 1,
'2': 2
},
'c2': {
'a': 1,
'b': 2,
'1': 1,
'2': 2
},
'cs': {
'ap': 1,
'app': 2,
'1': 1,
'2': 2
},
'd2': {
'a': 1,
'b1': 2,
'b2': 3,
'b3': 4,
'1': 1,
'2': 2,
'3': 3,
'4': 4
},
'c2v': {
'a1': 1,
'a2': 2,
'b1': 3,
'b2': 4,
'1': 1,
'2': 2,
'3': 3,
'4': 4
},
'c2h': {
'ag': 1,
'bg': 2,
'au': 3,
'bu': 4,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
},
'd2h': {
'ag': 1,
'b1g': 2,
'b2g': 3,
'b3g': 4,
'au': 5,
'b1u': 6,
'b2u': 7,
'b3u': 8,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8
}
}
try:
return cotton[point_group.lower()][str(irrep).lower()]
except KeyError:
raise ValidationError("""Irrep '%s' not valid for point group '%s'.""" % (str(irrep), point_group))