Source code for exatomic.molcas.inputs
# -*- coding: utf-8 -*-
# Copyright (c) 2015-2022, Exa Analytics Development Team
# Distributed under the terms of the Apache License 2.0
"""
Input Generator and Parser
###############################
"""
from .editor import Editor
_template = """\
{seward}
{calc}
{postcalc}
"""
_seward = """\
&SEWARD &END
Title
{title}
{atoms_bases}
{seward_options}
End of input
"""
_scf = """\
&SCF &END
Title
{title}
Charge
{charge}
{mult}
End of input
"""
_rasscf = """\
{rasrestart}
&RASSCF &END
Spin
{mult}
Symmetry
{symmetry}
nActEl
{nactel} {ras1_out_of} {ras3_into}
Inactive
{inactive}
Ras1
{ras1}
Ras2
{ras2}
Ras3
{ras3}
Iter
{iterations}
{restart_key}
CIRoots
{ciroots} {ciroots} 1
ORBAppear
{orbappear}
Outorbital
{outorbital}
levshift
{levshift}{alter}
End of input
"""
_caspt2 = """\
&CASPT2 &END
End of input
"""
_rassi = """\
&RASSI &END
End of input
"""
[docs]class InputGenerator(Editor):
def __init__(self, *args, **kwargs):
super(InputGenerator, self).__init__(*args, **kwargs)
def _handle_basis(basis, atom_types):
if isinstance(basis, (str, dict)):
return {atom: basis for atom in atom_types}
if isinstance(basis, list):
return {atom: bas for atom, bas in basis}
raise TypeError("Cannot handle basis with type {}".format(type(basis)))
[docs]def write_molcas_input(universe, fp=None, seward=True, caspt2=False, rassi=False,
task='scf', title='', charge=0, seward_options=None,
basis='ANO-RCC-VDZP', mult=1, iterations=100, ras1=0,
inactive=0, alter='',
ras2=0, ras3=0, nactel=0, ras1_out_of=0, ras3_into=0,
ciroots=1, levshift=0, symmetry=1, restart='',
restart_key='', orbappear='compact', outorbital='canonical',):
"""
Molcas is not a black box. Do not use this function.
basis can be string, list of tuples or dictionary of symbol, basis pairs
seward_options is an iterable of string options to go in seward
if restart is specified and restart_key is not, restart is ignored
"""
kwargs = {'seward': '', 'calc': '', 'postcalc': ''}
if seward:
cols = ['tag', 'x', 'y', 'z']
atoms_bases = ''
if 'tag' in universe.atom:
pass
else:
tags = []
tagcount = {}
for atom in universe.atom['symbol']:
tagcount.setdefault(atom, 0)
tagcount[atom] += 1
tags.append(atom + str(tagcount[atom]))
universe.atom['tag'] = tags
atom_types = universe.atom['symbol'].unique()
basis = _handle_basis(basis, atom_types)
for i, atom in enumerate(atom_types):
atoms_bases += 'Basis set\n{}.{}\n'.format(atom, basis[atom])
subatom = universe.atom[universe.atom['symbol'] == atom]
atoms_bases += subatom[cols].to_string(index=None, header=None)
if i == len(atom_types) - 1:
atoms_bases += '\nEnd of basis'
else:
atoms_bases += '\nEnd of basis\n'
sew_opts = ''
if seward_options is not None:
for i, string in enumerate(seward_options):
if i == len(seward_options) - 1:
sew_opts += string
else:
sew_opts += string + '\n'
kwargs['seward'] = _seward.format(title=title, atoms_bases=atoms_bases,
seward_options=sew_opts)
totalz = universe.atom['Z'].sum()
# Calculation type
if task == 'scf':
nopen = int(mult) - 1
scfmult = 'UHF\nZSPIn\n{}'.format(nopen)
kwargs['calc'] = _scf.format(title=title, charge=charge, mult=scfmult)
# SCF check
print('Check :\n' \
'Total Z : {}\n' \
'Total e- : {}\n' \
'nopen orb: {}\n'.format(totalz, totalz - charge, nopen))
elif task == 'rasscf':
if restart_key == 'LUMORB':
rasrestart = '>> COPY {} INPORB'.format(restart)
elif restart_key == 'JOBIPH':
rasrestart = '>> COPY {} JOBIPH'.format(restart)
elif not restart_key:
rasrestart = ''
else:
raise Exception('restart_key must be LUMORB or JOBIPH')
if alter:
altfmt = '\nALTER\n'
if isinstance(alter, str):
altfmt += alter
elif isinstance(alter, list):
for i, alt in enumerate(alter):
if i == len(alter) - 1:
altfmt += alt
else:
altfmt += alt + '\n'
kwargs['calc'] = _rasscf.format(mult=mult, symmetry=symmetry, nactel=nactel,
ras1_out_of=ras1_out_of, ras3_into=ras3_into,
inactive=inactive, ras1=ras1, ras2=ras2,
ras3=ras3, iterations=iterations,
rasrestart=rasrestart, restart_key=restart_key,
ciroots=ciroots, orbappear=orbappear,
outorbital=outorbital, levshift=levshift,
alter=altfmt)
# RASSCF check
sumelec = inactive * 2 + nactel
print('Check :\n'\
'Spin : {}\n' \
'Total Z : {}\n' \
'Total e- : {}\n' \
'Inactives : {}\n' \
'Ras1 holes: {}\n' \
'Ras2 size : {}\n' \
'Ras3 holes: {}\n' \
'Sum of e- : {}'.format(mult, totalz, totalz - charge, inactive,
ras1, ras2, ras3, sumelec))
print('The specification of your ({}({},{}){}) (RAS1(NACT,RAS2)RAS3)' \
'active space corresponds to a molecular charge of {}'.format(
ras1, nactel, ras2, ras3, totalz - sumelec))
else:
raise Exception('task must be scf or rasscf')
if caspt2:
raise NotImplementedError("currently does not support caspt2")
if rassi:
raise NotImplementedError("currently does not support rassi")
fl = InputGenerator(_template)
if fp is not None:
fl.write(fp, **kwargs)
else:
return fl.format(**kwargs)