├── .gitignore ├── CHANGES.txt ├── LICENSE ├── MANIFEST.in ├── README.rst ├── SCFpy ├── __init__.py ├── iterator.py ├── main.py ├── parser.py └── scf.py ├── examples └── water.xyz └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | 0.1 2 | ------ 3 | 4 | - first version of SCFpy!!!! 5 | 6 | 7 | 0.2 8 | ------ 9 | 10 | - fixed python2 and 3 compatible problems. 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CHANGES.txt 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | SCFpy 2 | ==================================================== 3 | 4 | .. image:: https://img.shields.io/pypi/v/scfpy.svg 5 | :target: https://pypi.org/project/SCFpy/ 6 | 7 | .. image:: https://img.shields.io/pypi/l/scfpy.svg 8 | :target: https://pypi.org/project/SCFpy/ 9 | 10 | .. image:: https://img.shields.io/pypi/pyversions/scfpy.svg 11 | :target: https://pypi.org/project/SCFpy/ 12 | 13 | SCFpy is a simple restricted Hartree-Fock code for small molecules 14 | and small basis sets. 15 | The purpose of this program is for computational chemistry beginner students 16 | to understand the concept of RHF and SCF procedure and how they works in the 17 | code. 18 | 19 | You can use SCFpy to calculate small molecule energy: 20 | 21 | :: 22 | 23 | $SCFpy -c 0 -b sto-3g h2.xyz 24 | >Total SCF energy = -1.06609574024 25 | 26 | Installation 27 | ------------ 28 | :: 29 | 30 | pip install SCFpy 31 | 32 | or 33 | 34 | :: 35 | 36 | git clone https://github.com/ipudu/SCFpy.git 37 | python setup.py install 38 | 39 | Usage 40 | ----- 41 | 42 | :: 43 | 44 | usage: scfpy [-h] [-c CHARGE] [-b BASIS] [-v] [input] 45 | 46 | SCFpy: simple restricted Hartree-Fock code 47 | 48 | positional arguments: 49 | input xyz file of molecule 50 | 51 | optional arguments: 52 | -h, --help show this help message and exit 53 | -c CHARGE, --charge CHARGE 54 | specify total charge of the molecule (default: 0) 55 | -b BASIS, --basis BASIS 56 | specify basis set (default: sto-3g) 57 | -v, --version displays the current version of SCFpy 58 | 59 | Author 60 | ------ 61 | 62 | - Pu Du (`@pudu.io `_) 63 | 64 | Notes 65 | ----- 66 | 67 | - You have to have NWChem package installed on your machine. 68 | - SCFpy get the total number of electrons, kinetic, potential, overlap, two electrons integrals from NWChem output. 69 | - Tested small molecules like H2, CH4, H2O..... 70 | - It may very slow for large molecules and large basis sets since SCFpy is single-core based program. 71 | 72 | Acknowledgement 73 | ---------------- 74 | 75 | This little program got inspired by Pyquante 2 and Joshua Goings's HeH+ SCF code. 76 | I also got a lot of help from Dr. Kenneth Lopata. 77 | -------------------------------------------------------------------------------- /SCFpy/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.2' 2 | -------------------------------------------------------------------------------- /SCFpy/iterator.py: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # 3 | # SCFpy: A Simple restricted Hartree-Fock code 4 | # written by Pu Du (pudugg@gmail.com) 5 | # 6 | ###################################################### 7 | 8 | from __future__ import print_function, division 9 | import numpy as np 10 | 11 | class SCFIterator(object): 12 | def __init__(self, rhf, tol=1e-5, maxiters=1000): 13 | self.rhf = rhf 14 | self.Eold = 0 15 | self.tol = tol 16 | self.maxiters = maxiters 17 | self.converged = False 18 | self.iterations = 0 19 | print('{0:5s} {1:25s} {2:25s} {3:25s}'.format('Iter', 20 | 'Energy', 'deltaE', 'deltaP')) 21 | return 22 | def __iter__(self): return self 23 | def next(self): return self.__next__() 24 | def __next__(self): 25 | self.iterations += 1 26 | if self.iterations > self.maxiters: 27 | print('Reached the maximun iterations, SCF iterations converged = ', self.converged) 28 | raise StopIteration 29 | deltaP, self.rhf.P = self.rhf.update() 30 | E = self.rhf.energy 31 | deltaE = E - self.Eold 32 | self.printInfo(E, deltaE, deltaP) 33 | if abs(deltaP) < self.tol: 34 | self.converged = True 35 | print('\n') 36 | print('SCF iterations converged = ', self.converged) 37 | print('\n') 38 | raise StopIteration 39 | self.Eold = E 40 | return E 41 | def printInfo(self, E, deltaE, deltaP): 42 | print(str(self.iterations).ljust(5) + str(E).ljust(25) + \ 43 | str(deltaE).ljust(25) + str(deltaP).ljust(25)) 44 | -------------------------------------------------------------------------------- /SCFpy/main.py: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # 3 | # SCFpy: A Simple restricted Hartree-Fock code 4 | # written by Pu Du (pudugg@gmail.com) 5 | # 6 | ###################################################### 7 | 8 | from __future__ import print_function, division 9 | 10 | import argparse 11 | import os 12 | import subprocess 13 | from .scf import rhf 14 | from .iterator import SCFIterator 15 | from .parser import parser as read 16 | from .__init__ import __version__ 17 | 18 | def welcome(): 19 | print('#' * 44) 20 | print('#SCFpy: Simple restricted Hartree-Fock code#') 21 | print('#' * 44) 22 | print('\n') 23 | 24 | def enable_cache(): 25 | if not os.path.exists('perm'): 26 | os.makedirs('perm') 27 | if not os.path.exists('scratch'): 28 | os.makedirs('scratch') 29 | 30 | def nwchem(args): 31 | name = os.path.splitext(args['input'])[0] 32 | with open(name + '.nw', 'w') as f: 33 | f.write('echo\n') 34 | f.write('start ' + os.path.splitext(args['input'])[0] + '\n\n') 35 | f.write('permanent_dir ./perm\n') 36 | f.write('scratch_dir ./scratch\n\n') 37 | f.write('charge ' + str(args['charge']) + '\n') 38 | f.write('geometry units angstroms noautosym\n') 39 | with open(args['input'],'r') as i: 40 | for line in i: 41 | lineInfo = line.split() 42 | if len(lineInfo) == 4: 43 | f.write(line) 44 | f.write('end\n\n') 45 | f.write('basis\n') 46 | f.write(' * library ' + args['basis'] + '\n') 47 | f.write('end\n\n') 48 | f.write('scf\nprint overlap kinetic potential ao2eints debug\nend\n') 49 | f.write('task scf\n') 50 | return name 51 | 52 | def get_parser(): 53 | parser = argparse.ArgumentParser(description='SCFpy: simple restricted Hartree-Fock code') 54 | parser.add_argument('input', type=str, nargs='?',help='xyz file of molecule') 55 | parser.add_argument('-c', '--charge', default=0, type=int, 56 | help='specify total charge of the molecule (default: 0)') 57 | parser.add_argument('-b','--basis', default='sto-3g', type=str, 58 | help='specify basis set (default: sto-3g)') 59 | parser.add_argument('-v', '--version', action='store_true', 60 | help='displays the current version of SCFpy') 61 | return parser 62 | 63 | def command_line_runner(): 64 | parser = get_parser() 65 | args = vars(parser.parse_args()) 66 | if args['version']: 67 | print(__version__) 68 | return 69 | if not args['input']: 70 | parser.print_help() 71 | return 72 | else: 73 | welcome() 74 | enable_cache() 75 | print('Preparing the input file for NWChem ............') 76 | name = nwchem(args) 77 | print('NWChem is running...............................') 78 | os.system('nwchem '+ name +'.nw' + '>' + name +'.nwo') 79 | print('Getting following infomation from NWChem output:') 80 | print('Number of total electrons') 81 | print('Nuclear replusion energy') 82 | print('Kinetic integral') 83 | print('Potential integral') 84 | print('Overlap integral') 85 | print('two electrons integral\n') 86 | 87 | p = read(name+'.nwo') 88 | mol = rhf(p.Nelec,'enuc.dat','s.dat','t.dat','v.dat','e2.dat') 89 | print('=====> Begin SCF Iterations <====\n\n') 90 | ens = mol.converge(SCFIterator) 91 | print("* Total SCF energy = " +str(mol.energy)) 92 | 93 | if __name__ == '__main__': 94 | command_line_runner() 95 | -------------------------------------------------------------------------------- /SCFpy/parser.py: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # 3 | # SCFpy: A Simple restricted Hartree-Fock code 4 | # written by Pu Du (pudugg@gmail.com) 5 | # 6 | ###################################################### 7 | 8 | from __future__ import print_function, division 9 | 10 | import os 11 | import re 12 | import subprocess 13 | import numpy as np 14 | 15 | class parser(object): 16 | def __init__(self,nwo): 17 | self.file = nwo 18 | self.Nelec = 0 19 | self.grabInfo() 20 | 21 | def grabInfo(self): 22 | """ 23 | grab kinetic 1-e integrals 24 | potential 1-e integrals 25 | overlap 1-e integrals 26 | all 2-e integrals 27 | guess e density matrix 28 | nuclear replusion energy from nwchem output file 29 | """ 30 | #for kinetic integrals 31 | t_start = r'^\s*Begin\skinetic\s1-e\sintegrals$' 32 | t_end = r'^\s*End\skinetic\s1-e\sintegrals$' 33 | t_info =r'^.*\d+$' 34 | t_file = open('t.dat', 'w') 35 | t_flag = False 36 | 37 | #for potential integrals 38 | v_start = r'^\s*Begin\spotential\s1-e\sintegrals$' 39 | v_end = r'^\s*End\spotential\s1-e\sintegrals$' 40 | v_info =r'^.*\d+$' 41 | v_file = open('v.dat', 'w') 42 | v_flag = False 43 | 44 | #for overlap integrals 45 | s_start = r'^\s*Begin\soverlap\s1-e\sintegrals$' 46 | s_end = r'^\s*End\soverlap\s1-e\sintegrals$' 47 | s_info =r'^.*\d+$' 48 | s_file = open('s.dat', 'w') 49 | s_flag = False 50 | 51 | #for two electron integrals 52 | e2_start = r'^\s*Begin\sall\s2-e\sintegrals$' 53 | e2_end = r'^\s*End\sall\s2-e\sintegrals$' 54 | e2_info =r'^.*\d+$' 55 | e2_file = open('e2.dat', 'w') 56 | e2_flag = False 57 | 58 | #for nulear replustion energy 59 | enuc_info = r'^.*Nuclear\srepulsion\senergy\s=.+$' 60 | enuc_file = open('enuc.dat','w') 61 | 62 | 63 | with open(self.file, 'r') as f: 64 | for line in f: 65 | if re.match(r'^\s*closed\sshells\s*=', line): 66 | lineInfo = line.split() 67 | self.Nelec = int(lineInfo[3]) * 2 68 | if re.match(t_start, line): 69 | t_flag = True 70 | if re.match(t_end, line): 71 | t_flag = False 72 | if t_flag is True: 73 | if re.match(t_info, line): 74 | lineInfo = line.split() 75 | t_file.write(lineInfo[1] + ' ' + 76 | lineInfo[4] + ' ' + 77 | lineInfo[7]+'\n') 78 | if re.match(v_start, line): 79 | v_flag = True 80 | if re.match(v_end, line): 81 | v_flag = False 82 | if v_flag is True: 83 | if re.match(v_info, line): 84 | lineInfo = line.split() 85 | v_file.write(lineInfo[1] + ' ' + 86 | lineInfo[4] + ' ' + 87 | lineInfo[7]+'\n') 88 | if re.match(s_start, line): 89 | s_flag = True 90 | if re.match(s_end, line): 91 | s_flag = False 92 | if s_flag is True: 93 | if re.match(s_info, line): 94 | lineInfo = line.split() 95 | s_file.write(lineInfo[1] + ' ' + 96 | lineInfo[4] + ' ' + 97 | lineInfo[7]+'\n') 98 | if re.match(e2_start, line): 99 | e2_flag = True 100 | if re.match(e2_end, line): 101 | e2_flag = False 102 | if e2_flag is True: 103 | if re.match(e2_info, line): 104 | lineInfo = line.split() 105 | e2_file.write(lineInfo[1] + ' ' + 106 | lineInfo[2] + ' ' + 107 | lineInfo[3] + ' ' + 108 | lineInfo[4] + ' ' + 109 | lineInfo[5]+'\n') 110 | if re.match(enuc_info, line): 111 | lineInfo = line.split() 112 | enuc_file.write(lineInfo[-1]+'\n') 113 | t_file.close() 114 | v_file.close() 115 | s_file.close() 116 | e2_file.close() 117 | enuc_file.close() 118 | -------------------------------------------------------------------------------- /SCFpy/scf.py: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # 3 | # SCFpy: A Simple restricted Hartree-Fock code 4 | # written by Pu Du (pudugg@gmail.com) 5 | # 6 | ###################################################### 7 | 8 | from __future__ import print_function, division 9 | 10 | import numpy as np 11 | from .iterator import SCFIterator 12 | 13 | class rhf(object): 14 | """ 15 | class of restricted Hartree-Fock method. 16 | """ 17 | def __init__(self,Nelec,enuc,s,t,v,e2): 18 | self.Nelec = Nelec 19 | self.enuc = self.nuclear_repulsion(enuc) 20 | self.T = self.kinetic(t) 21 | self.V = self.potential(v) 22 | self.S = self.overlap(s) 23 | ################################################### 24 | #symmetric orthogonalization P.143 (3.166 - 3.167) 25 | ################################################### 26 | s, U = np.linalg.eig(self.S) 27 | s_mhalf = np.diag(s**(-0.5)) 28 | S_mhalf = np.dot(U, np.dot(s_mhalf, U.T)) 29 | self.X = S_mhalf 30 | ################################################### 31 | #set up a guess density matrix 32 | ################################################### 33 | self.P = np.zeros((len(self.T),len(self.T))) 34 | self.twoe = self.twoe_integrals(e2) 35 | self.energies = [] 36 | self.energy = 0 37 | self.converged = False 38 | 39 | def converge(self, iterator=SCFIterator, **kwargs): 40 | converger = iterator(self, **kwargs) 41 | for en in converger: 42 | self.energies.append(en) 43 | self.converged = converger.converged 44 | return self.energies 45 | 46 | def update(self): 47 | self.energy = self.enuc 48 | Hcore = self.T + self.V 49 | #print 'Hcore = \n', Hcore 50 | P = self.P 51 | #print "P = \n", P 52 | ################################################### 53 | #make Fock matrix P.141 3.154 54 | ################################################### 55 | F=self.fock(Hcore, P) 56 | #print 'F= \n', F 57 | ################################################### 58 | #F' matrix P.145 (3.177) 59 | ################################################### 60 | F_prime = np.dot(self.X.T, np.dot(F,self.X)) 61 | #print "F_prime = \n", F_prime 62 | ################################################### 63 | #eigenvalue problem of F' P.145 (3.178) 64 | ################################################### 65 | E, C_prime = np.linalg.eigh(F_prime) 66 | C = np.dot(self.X, C_prime) 67 | #print "E = \n", E 68 | #print "C_prime = \n", C_prime 69 | ################################################### 70 | #calculate the energy p.149 (3.183) 71 | ################################################### 72 | self.energy = self.energy + 0.5 * np.sum(P* (Hcore + F)) 73 | #self.energy = self.energy + self.get_energy(P,Hcore,F) 74 | #EN = self.currentenergy(P,Hcore,F,2) 75 | #print 0.5 * np.sum(P* (Hcore + F)) 76 | #print self.energy 77 | ################################################### 78 | #Form a new density matrix P from C p.139 (3.178) 79 | ################################################### 80 | #print C 81 | Pold, P = self.density(C,P,self.Nelec) 82 | P = 0.5 * P + 0.5 * Pold 83 | #print "Pold = \n", Pold 84 | #print "P = \n", P 85 | ################################################### 86 | #standard deviation of density matrix p.149 87 | ################################################### 88 | deltaP = self.delta(P, Pold) 89 | return deltaP, P 90 | 91 | def nuclear_repulsion(self, enuc): 92 | with open(enuc, 'r') as f: 93 | e = float(f.read()) 94 | return e 95 | 96 | def kinetic(self, t): 97 | Traw = np.loadtxt(t) 98 | #dim = int(np.sqrt(len(Traw))) 99 | dim = int(Traw[-1][0]) 100 | T = np.zeros((dim, dim)) 101 | for e in Traw: 102 | T[int(e[0])-1][int(e[1])-1] = e[2] 103 | return T 104 | 105 | def potential(self, v): 106 | V = self.kinetic(v) 107 | return V 108 | 109 | def overlap(self, s): 110 | S = self.kinetic(s) 111 | return S 112 | 113 | def twoe_integrals(self, e2): 114 | ERIraw = np.genfromtxt(e2,dtype=None) 115 | twoe = {self.eint(row[0],row[1],row[2],row[3]) : row[4] for row in ERIraw} 116 | return twoe 117 | 118 | def eint(self,a,b,c,d): 119 | if a > b: ab = a*(a+1)/2 + b 120 | else: ab = b*(b+1)/2 + a 121 | if c > d: cd = c*(c+1)/2 + d 122 | else: cd = d*(d+1)/2 + c 123 | if ab > cd: abcd = ab*(ab+1)/2 + cd 124 | else: abcd = cd*(cd+1)/2 + ab 125 | return abcd 126 | 127 | def tei(self,a,b,c,d): 128 | return self.twoe.get(self.eint(a,b,c,d),0.0) 129 | 130 | def fock(self, Hcore, P): 131 | dim = len(Hcore) 132 | F = np.empty((dim, dim)) 133 | for i in range(0, dim): 134 | for j in range(0, dim): 135 | F[i,j] = Hcore[i,j] 136 | for k in range(0, dim): 137 | for l in range(0, dim): 138 | F[i,j] = F[i,j] + P[k,l]*(self.tei(i+1,j+1,k+1,l+1) - \ 139 | 0.5e0*self.tei(i+1,k+1,j+1,l+1)) 140 | return F 141 | def density(self,C,P,Nelec): 142 | dim = len(P) 143 | Pold = np.zeros((dim,dim)) 144 | for mu in range(0,dim): 145 | for nu in range(0,dim): 146 | Pold[mu,nu] = P[mu,nu] 147 | P[mu,nu] = 0.0e0 148 | for m in range(0,Nelec//2): 149 | P[mu,nu] = P[mu,nu] + 2*C[mu,m]*C[nu,m] 150 | return Pold, P 151 | 152 | def currentenergy(self,P,Hcore,F,dim): 153 | EN = 0.0e0 154 | for mu in range(0,dim): 155 | for nu in range(0,dim): 156 | EN = EN + 0.5*P[mu,nu]*(Hcore[mu,nu] + F[mu,nu]) 157 | #print 'Energy = ', EN 158 | return EN 159 | 160 | def delta(self,P,Pold): 161 | DELTA = 0.0e0 162 | dim = len(P) 163 | for i in range(0,dim): 164 | for j in range(0,dim): 165 | DELTA = DELTA+((P[i,j]-Pold[i,j])**2) 166 | DELTA = (DELTA/4)**(0.5) 167 | return DELTA 168 | -------------------------------------------------------------------------------- /examples/water.xyz: -------------------------------------------------------------------------------- 1 | 3 2 | 3 | O -2.29508 1.43048 0.00000 4 | H -1.32508 1.43048 0.00000 5 | H -2.61841 0.75587 -0.61746 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | import SCFpy 5 | import os 6 | 7 | 8 | def extra_dependencies(): 9 | import sys 10 | ret = [] 11 | if sys.version_info < (2, 7): 12 | ret.append('argparse') 13 | return ret 14 | 15 | 16 | def read(*names): 17 | values = dict() 18 | extensions = ['.txt', '.rst'] 19 | for name in names: 20 | value = '' 21 | for extension in extensions: 22 | filename = name + extension 23 | if os.path.isfile(filename): 24 | value = open(name + extension).read() 25 | break 26 | values[name] = value 27 | return values 28 | 29 | long_description = """ 30 | %(README)s 31 | News 32 | ==== 33 | %(CHANGES)s 34 | """ % read('README', 'CHANGES') 35 | 36 | setup( 37 | name='SCFpy', 38 | version=SCFpy.__version__, 39 | description='A Simple restricted Hartree-Fock code', 40 | long_description=long_description, 41 | classifiers=[ 42 | "Development Status :: 3 - Alpha", 43 | "Environment :: Console", 44 | "Intended Audience :: Developers", 45 | "Programming Language :: Python :: 2", 46 | "Programming Language :: Python :: 2.6", 47 | "Programming Language :: Python :: 2.7", 48 | "Programming Language :: Python :: 3", 49 | "Programming Language :: Python :: 3.2", 50 | "Programming Language :: Python :: 3.3", 51 | "Programming Language :: Python :: 3.4", 52 | "Topic :: Scientific/Engineering :: Chemistry", 53 | ], 54 | keywords='A Simple restricted Hartree-Fock code', 55 | author='Pu Du', 56 | author_email='pudugg@gmail.com', 57 | maintainer='Pu Du', 58 | maintainer_email='pudugg@gmail.com', 59 | url='https://github.com/ipudu/SCFpy', 60 | license='MIT', 61 | packages=find_packages(), 62 | entry_points={ 63 | 'console_scripts': [ 64 | 'scfpy = SCFpy.main:command_line_runner', 65 | ] 66 | }, 67 | install_requires=[ 68 | 'numpy', 69 | ] + extra_dependencies(), 70 | ) 71 | --------------------------------------------------------------------------------