Adding Methods to Driver¶
proc.py
¶
This is concerned at present with normal methods added first to the procedures table in driver.py that associates method names with functions to run them located in proc.py .
The function should start with a declaration, as below. methodname
is
never seen by users, so it’s good to be specific; if there’s lots of
modules that can run mp2, call methodname modulenamemethodname, perhaps.
The function must always take as arguments (name, **kwargs)
.
1 2 3 4 5 | # energy method
def run_methodname(name, **kwargs):
# gradient method
def run_methodname_gradient(name, **kwargs):
|
If the function needs to test the identity of name
several times, it
can be convenient to predefine the lowercase version of the variable. The
case of all other py-side options (in kwargs) has already been handled by
energy()
, etc. in driver.py and need not be repeated here.
1 2 3 4 5 | # include if convenient
lowername = name.lower()
# never include
kwargs = kwargs_lower(kwargs)
|
It’s often necessary to The function often needs to set options for the
c-side modules it calls. In order that the state of the options set by the
user remains when control is returned to the user, an
OptionsState
object is set up. See
LibOptions: globals, locals, has_changed and all that for details. All options set by the
function need to be included here, and only options set by the function
should be included. Most options should be associated with a particular
module, but a few (see below) are given without module.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # include if any options set
optstash = OptionsState(
# these and other basis options should have no associated module
['BASIS'],
['DF_BASIS_SCF'],
['DF_BASIS_MP2'],
['PUREAM'],
['FREEZE_CORE'],
# all others should have an associated module
['SCF', 'SCF_TYPE'],
['SCF', 'GUESS'],
['DFMP2', 'MP2_OS_SCALE'],
)
|
If options need to be set, set them anywhere here. Options should be set
locally to a module, except for those without a module in
OptionsState
.
1 2 3 4 5 6 7 8 | # include if necessary as globals
psi4.set_global_option('BASIS', guessbasis)
psi4.set_global_option('DF_BASIS_SCF', guessbasisdf)
# include if necessary as locals
psi4.set_local_option('TRANSQT2', 'WFN', 'MP2')
psi4.set_local_option('CCSORT', 'WFN', 'MP2')
psi4.set_local_option('MP2', 'WFN', 'MP2')
|
If the regular scf module is to be run, run it through
scf_helper()
so that cast-up can be used. Also, add
the option to pass the reference wavefunction by pre-running scf,
then running the module with the ref_wfn
kwarg. Also, if the full
two-electron integrals are necessary for the post-scf, compute them if
only the df integrals were run previously.
1 2 3 4 5 6 7 8 9 | # Bypass the scf call if a reference wavefunction is given
ref_wfn = kwargs.get('ref_wfn', None)
if ref_wfn is None:
ref_wfn = scf_helper(name, **kwargs)
# If the scf type is DF/CD, then the AO integrals were never written to disk
if psi4.get_option('SCF', 'SCF_TYPE') in ['DF', 'CD']:
psi4.MintsHelper(ref_wfn.basisset()).integrals()
|
Direct any post-scf modules to be run.
1 2 3 4 | # include if further post-scf modules are needed
psi4.transqt2()
psi4.ccsort()
psi4.mp2()
|
If an OptionsState
object was set up, those options
need to be returned to the original user state with the following.
1 2 | # include if optstash = OptionsState( was set up previously
optstash.restore()
|
No function should return anything. CURRENT ENERGY
will be set by
energy()
, etc.
1 2 | # never include
return returnvalue
|
Managed Methods¶
When functionality overlaps between modules, a pattern is needed to (1)
access each route through the code without contrivances like ccsd2
,
_ccsd
, sdci
and (2) apportion defaulting among the modules, taking
into account reference (RHF/UHF/ROHF) and calc type (CONV, DF, CD).
Managed methods handle both these cases through the addition of a new
keyword QC_MODULE and a set of type keywords analogous to
MP2_TYPE: MP_TYPE,
CI_TYPE, CC_TYPE, which can have values CONV
,
DF
, and CD
. These are all global keywords, as their values are
shared among modules rather than (or in addition to) being used internally
by the module). We’re sticking with SCF_TYPE and
MP2_TYPE defaulting to DF
, while everything higher defaults
to CONV
. In psi4/share/python/driver.py, a managed method calls a
“select” function rather than a “run” function.
1 2 3 4 5 | procedures = {
'energy': {
'scf' : run_scf,
'mp3' : select_mp3,
'dcft' : run_dcft,
|
Then in psi4/share/python/proc.py, the select function runs through
reference (always outer loop) and type (inner loop) to specify the proc
function to call for any able, non-default module (e.g., mtd_type ==
'DETCI'
) or able, default module (e.g., mtd_typd == ['', 'FNOCC']
).
Don’t worry about ‘else’ statements as anything that falls through will be
caught and a readable error generated.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | def select_mp3(name, **kwargs):
"""Function selecting the algorithm for a MP3 energy call
and directing to specified or best-performance default modules.
"""
reference = psi4.get_option('SCF', 'REFERENCE')
mtd_type = psi4.get_global_option('MP_TYPE')
module = psi4.get_global_option('QC_MODULE')
# Considering only [df]occ/fnocc/detci
func = None
if reference == 'RHF':
if mtd_type == 'CONV':
if module == 'DETCI':
func = run_detci
elif module == 'FNOCC':
func = run_fnocc
elif module in ['', 'OCC']:
func = run_occ
elif mtd_type == 'DF':
if module in ['', 'OCC']:
func = run_dfocc
elif mtd_type == 'CD':
if module in ['', 'OCC']:
func = run_dfocc
elif reference == 'UHF':
if mtd_type == 'CONV':
if module in ['', 'OCC']:
func = run_occ
elif mtd_type == 'DF':
if module in ['', 'OCC']:
func = run_dfocc
elif mtd_type == 'CD':
if module in ['', 'OCC']:
func = run_dfocc
elif reference == 'ROHF':
if mtd_type == 'CONV':
if module in ['DETCI']:
func = run_detci
if func is None:
raise ManagedMethodError(['select_mp3', name, 'MP_TYPE', mtd_type, reference, module])
return func(name, **kwargs)
|
Naturally, in the run function, you must either use the type keyword for
type switching or translate it into whatever DO_CD
-like keywords your
module uses. At run time with a closed-shell molecule,
1 | energy('mp3')
|
will run OCC, while
1 2 | set qc_module fnocc
energy('mp3')
|
will run FNOCC mp3.
A special case is DETCI that can run mp3, but oughtn’t to be used for such. So above, ROHF CONV mp3 has no default, but can still access the detci code with
1 2 3 | set reference rohf
set qc_module detci
energy('mp3')
|
While the below gives an error
1 2 | set reference rohf
energy('mp3')
|
Again, whenever a single method name needs to call multiple proc.py run functions, it should be managed. In Overlapping capabilities of . “Y” is available; “D” is default. “Y” means method available in module, “D” means module is default for that method, “” mean method not available.