Source code for pyscf.gto.basis.parse_nwchem_ecp
#!/usr/bin/env python
# Copyright 2023 The PySCF Developers. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Qiming Sun <osirpt.sun@gmail.com>
#
'''
Parsers for basis set in the NWChem format
'''
__all__ = ['parse', 'load', 'convert_ecp_to_nwchem']
import numpy
import numpy as np
import re
from pyscf.data.elements import _std_symbol
from pyscf.lib.exceptions import BasisNotFoundError
from pyscf import __config__
DISABLE_EVAL = getattr(__config__, 'DISABLE_EVAL', False)
MAXL = 15
SPDF = 'SPDFGHIKLMNORTU'
MAPSPDF = {key: l for l, key in enumerate(SPDF)}
ECP_DELIMITER = re.compile('\n *ECP *\n')
[docs]
def parse(string, symb=None):
'''Parse ECP text which is in NWChem format. Return an internal
basis format which can be assigned to attribute :attr:`Mole.ecp`
Empty lines, or the lines started with #, or the lines of "BASIS SET" and
"END" will be ignored are ignored.
'''
if symb is not None:
symb = _std_symbol(symb)
raw_data = string.splitlines()
for i, dat in enumerate(raw_data):
dat0 = dat.split(None, 1)
if dat0 and dat0[0] == symb:
break
if i+1 == len(raw_data):
raise BasisNotFoundError('ECP not found for %s' % symb)
seg = []
for dat in raw_data[i:]:
dat = dat.strip()
if dat: # remove empty lines
if ((dat[0].isalpha() and dat.split(None, 1)[0].upper() != symb.upper())):
break
else:
seg.append(dat)
else:
seg = string.splitlines()
ecptxt = []
for dat in seg:
dat = dat.split('#')[0].strip()
dat_upper = dat.upper()
if (dat and not dat_upper.startswith('END') and not dat_upper.startswith('ECP')):
ecptxt.append(dat)
return _parse_ecp(ecptxt)
def _parse_ecp(raw_ecp):
ecp_add = []
nelec = None
for line in raw_ecp:
dat = line.strip()
if not dat or dat.startswith('#'): # comment line
continue
elif dat[0].isalpha():
keys = dat.split()
if len(keys) == 1:
key = keys[0].upper()
else:
key = keys[1].upper()
if key == 'NELEC':
nelec = int(dat.split()[2])
continue
elif key == 'UL':
ecp_add.append([-1])
elif key in MAPSPDF:
ecp_add.append([MAPSPDF[key]])
else:
raise BasisNotFoundError('Not basis data')
# up to r^6
by_ang = [[] for i in range(7)]
ecp_add[-1].append(by_ang)
else:
line = dat.replace('D','e').split()
l = int(line[0])
try:
coef = [float(x) for x in line[1:]]
except ValueError:
if DISABLE_EVAL:
raise ValueError('Failed to parse ecp %s' % line)
else:
coef = list(eval(','.join(line[1:])))
except Exception:
raise BasisNotFoundError('Not basis data')
if any(x != 0 for x in coef[1:]):
by_ang[l].append(coef)
if nelec is None:
return []
bsort = []
for l in range(-1, MAXL):
bsort.extend([b for b in ecp_add if b[0] == l])
if not bsort:
raise BasisNotFoundError(f'ECP data not found in "{raw_ecp}"')
return [nelec, bsort]
[docs]
def load(basisfile, symb):
'''Load ECP for atom of symb from file'''
return _parse_ecp(_search_ecp(basisfile, symb))
def _search_ecp(basisfile, symb):
symb = _std_symbol(symb)
with open(basisfile, 'r') as fin:
fdata = re.split(ECP_DELIMITER, fin.read())
if len(fdata) <= 1:
return []
fdata = fdata[1].splitlines()
for i, dat in enumerate(fdata):
dat0 = dat.split(None, 1)
if dat0 and dat0[0] == symb:
break
seg = []
for dat in fdata[i:]:
dat = dat.strip()
if dat: # remove empty lines
if ((dat[0].isalpha() and dat.split(None, 1)[0].upper() != symb.upper())):
return seg
else:
seg.append(dat)
return []
[docs]
def convert_ecp_to_nwchem(symb, ecp):
'''Convert the internal ecp format to NWChem format string'''
symb = _std_symbol(symb)
res = ['%-2s nelec %d' % (symb, ecp[0])]
for ecp_block in ecp[1]:
l = ecp_block[0]
if l == -1:
res.append('%-2s ul' % symb)
else:
res.append('%-2s %s' % (symb, SPDF[l].lower()))
for r_order, dat in enumerate(ecp_block[1]):
for e,c in dat:
res.append('%d %15.9f %15.9f' % (r_order, e, c))
return '\n'.join(res)