-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Gaussian harness #442
base: master
Are you sure you want to change the base?
Add Gaussian harness #442
Changes from 19 commits
c9c49a4
70596ae
b4c5d30
1aa3522
10ae387
11fc097
ee0dcb4
f93420c
3050155
b1097be
a5265b6
5d73c9f
6eb55e4
aad52cf
ce55302
0ccc8ef
30e2579
d76c5e7
95d16f0
61216dd
a2ff5c8
ab23bb6
5df8272
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
""" | ||
Calls the GAUSSIAN executable | ||
""" | ||
|
||
import os | ||
import re | ||
import tempfile | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'tempfile' is not used.
|
||
import warnings | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'warnings' is not used.
|
||
from collections import defaultdict | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'defaultdict' is not used.
|
||
from typing import Any, Dict, List, Optional, Tuple | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'Tuple' is not used.
|
||
import cclib | ||
from cclib.method import Nuclear | ||
|
||
import numpy as np | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'np' is not used.
|
||
from qcelemental import constants | ||
from qcelemental.models import AtomicInput, AtomicResult, Molecule, Provenance | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'Molecule' is not used.
|
||
from qcelemental.molparse import regex | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'regex' is not used.
|
||
from qcelemental.util import parse_version, safe_version, which | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'parse_version' is not used.
Import of 'safe_version' is not used. |
||
|
||
from qcengine.config import TaskConfig, get_config | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'get_config' is not used.
|
||
|
||
from ..exceptions import InputError, UnknownError | ||
from ..util import disk_files, execute, temporary_directory | ||
Check notice Code scanning / CodeQL Unused import Note
Import of 'disk_files' is not used.
Import of 'temporary_directory' is not used. |
||
from .model import ProgramHarness | ||
|
||
class GaussianHarness(ProgramHarness): | ||
|
||
_defaults = { | ||
"name": "gaussian", | ||
"scratch": True, | ||
"thread_safe": False, | ||
"thread_parallel": True, | ||
"node_parallel": False, | ||
"managed_memory": True, | ||
} | ||
version_cache: Dict[str, str] = {} | ||
|
||
class Config(ProgramHarness.Config): | ||
pass | ||
|
||
def found(self, raise_error: bool = False) -> bool: | ||
return which( | ||
"g09", | ||
QuChem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return_bool=True, | ||
raise_error=raise_error, | ||
raise_msg="Please install Gaussian. Check it's in your PATH with `which g09`." | ||
) | ||
QuChem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_version(self) -> str: | ||
self.found(raise_error=True) | ||
|
||
which_prog = which("g09") | ||
|
||
v_input = '''%mem=20MW | ||
#P HF/sto-3g | ||
|
||
#test HF/sto-3g for H atom | ||
|
||
0 2 | ||
H | ||
|
||
''' | ||
if which_prog not in self.version_cache: | ||
success, output = execute([which_prog, 'v.inp', 'v.log'], | ||
{'v.inp': v_input}, | ||
['v.log'] | ||
) | ||
if success: | ||
outtext = output['outfiles']['v.log'] | ||
outtext = outtext.splitlines() | ||
for line in outtext: | ||
if 'Gaussian 09' in line: | ||
#version_line = line.split('Gaussian 09:')[-1] | ||
#version_line = version_line.split()[0] | ||
#self.version_cache[which_prog] = safe_version(version_line) | ||
self.version_cache[which_prog] = '2009' | ||
|
||
return self.version_cache[which_prog] | ||
|
||
def compute(self, input_model: "AtomicInput", config: TaskConfig) -> "AtomicResult": | ||
""" | ||
Run Gaussian | ||
""" | ||
# Check if Gaussian executable is found | ||
self.found(raise_error=True) | ||
|
||
# Setup the job | ||
job_inputs = self.build_input(input_model, config) | ||
|
||
# Run Gaussian | ||
exe_success, proc = self.execute(job_inputs) | ||
|
||
# Determine whether the calculation succeeded | ||
if exe_success: | ||
# If execution succeeded, collect results | ||
result = self.parse_output(proc, input_model) | ||
|
||
return result | ||
|
||
else: | ||
proc['outfiles']['stderr'] = proc['outfiles']['output.log'] | ||
outfile = proc['outfiles']['output.log'] | ||
|
||
if 'Error termination via ' in outfile: | ||
raise InputError(proc['outfiles']['output.log']) | ||
|
||
else: | ||
# Return UnknownError for error propagation | ||
raise UnknownError(proc['outfiles']['output.log']) | ||
|
||
def build_input( | ||
self, input_model: AtomicInput, config: TaskConfig, template: Optional[str] = None | ||
) -> Dict[str, Any]: | ||
|
||
# Build keywords | ||
keywords = {k.upper(): v for k, v in input_model.keywords.items()} | ||
|
||
gaussian_kw = [] | ||
|
||
if input_model.driver == "energy": | ||
gaussian_kw.append("sp") | ||
elif input_model.driver == "gradient": | ||
gaussian_kw.append("force") | ||
elif input_model.driver == "hessian": | ||
gaussian_kw.append("freq") | ||
else: | ||
raise InputError(f"Driver {input_model.driver} not implemented for Gaussian.") | ||
|
||
#if input_model.molecule.fix_com or input_model.molecule.fix_orientation: | ||
# keywords["SYM_IGNORE"] = "TRUE" | ||
Comment on lines
+166
to
+167
Check notice Code scanning / CodeQL Commented-out code Note
This comment appears to contain commented-out code.
|
||
if 'SCF_CONVERGENCE' in keywords: | ||
gaussian_kw.append('SCF=' + keywords["SCF_CONVERGENCE"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is the name "SCF_CONVERGENCE" coming from? QCNG tends to want the same keyword names in |
||
if 'POPULATION' in keywords: | ||
gaussian_kw.append('Pop=' + keywords['POPULATION']) | ||
|
||
keywords = {'scf_damp': 'true', | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable keywords is not used.
|
||
'scf_diis': 'false'} | ||
|
||
save_fchk = False | ||
|
||
if input_model.protocols.native_files == 'all': | ||
save_fchk = True | ||
gaussian_kw.append('formcheck') | ||
|
||
# Begin input file | ||
input_file = [] | ||
input_file.append('%mem={}MB'.format(int(config.memory * 1024))) | ||
input_file.append('#P {}/{}'.format(input_model.model.method, input_model.model.basis) + ' ' + ' '.join(gaussian_kw) + '\n') | ||
input_file.append('write your comment here\n') | ||
|
||
# Create a mol object | ||
mol = input_model.molecule | ||
input_file.append(f'{int(mol.molecular_charge)} {mol.molecular_multiplicity}') | ||
|
||
# Write the geometry | ||
for real, sym, geom in zip(mol.real, mol.symbols, mol.geometry): | ||
if real is False: | ||
raise InputError('Cannot handle ghost atoms yet.') | ||
input_file.append(f'{sym} {geom[0]:14.8f} {geom[1]:14.8f} {geom[2]:14.8f}') | ||
input_file.append("\n") | ||
|
||
# print ('*' * 100) | ||
# print ('\n'.join(input_file)) | ||
# print ('*' * 100) | ||
|
||
gaussian_ret = { | ||
'infiles': {'input.inp': '\n'.join(input_file)}, | ||
'commands': [which("g09"), 'input.inp', 'output.log'] | ||
#'scratch_directory': config.scratch_directory | ||
} | ||
|
||
return gaussian_ret | ||
|
||
def execute(self, | ||
inputs, | ||
*, | ||
extra_infiles: Optional[Dict[str, str]] = None, | ||
extra_outfiles: Optional[Dict[str, str]] = None, | ||
extra_commands: Optional[List[str]] = None, | ||
scratch_name = None, | ||
timeout: Optional[int] = None | ||
): | ||
|
||
success, dexe = execute( | ||
inputs['commands'], | ||
inputs['infiles'], | ||
outfiles = ['output.log'], | ||
scratch_messy = True | ||
) | ||
|
||
if (dexe['outfiles']['output.log'] is None) or ( | ||
'Error termination via' in dexe['outfiles']['output.log']): | ||
print ('THERE IS AN ERROR!') | ||
|
||
success = False | ||
|
||
return success, dexe | ||
|
||
def parse_output(self, outfiles: Dict[str, str], input_model: AtomicInput) -> AtomicResult: | ||
|
||
output_data = {} | ||
properties = {} | ||
cclib_vars = {} | ||
|
||
tmp_output_path = outfiles['scratch_directory'] | ||
tmp_output_file = os.path.join(tmp_output_path, 'output.log') | ||
data = cclib.io.ccread(tmp_output_file) | ||
cclib_vars = data.getattributes(True) | ||
|
||
last_occupied_energy = data.moenergies[0][data.homos[0]] | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable last_occupied_energy is not used.
|
||
#output_data['HOMO ENERGY'] = last_occupied_energy | ||
|
||
scf_energy = data.scfenergies[0] / constants.conversion_factor("hartree", "eV") # Change from the eV unit to the Hartree unit | ||
#output_data['SCF ENERGY'] = scf_energy | ||
|
||
if input_model.driver == 'energy': | ||
output_data['return_result'] = scf_energy | ||
QuChem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#print (os.system('ccget --list ' + tmp_output_file)) #data available in the output for parsing | ||
|
||
#if input_model.driver == 'energy': | ||
# print (cclib.__version__) | ||
# print (output_data) | ||
Comment on lines
+261
to
+263
Check notice Code scanning / CodeQL Commented-out code Note
This comment appears to contain commented-out code.
|
||
#print (input_model) | ||
|
||
properties = { | ||
'nuclear_repulsion_energy': Nuclear(data).repulsion_energy(), | ||
'scf_total_energy': scf_energy, | ||
'return_energy': scf_energy | ||
} | ||
|
||
output_data['properties'] = properties | ||
output_data['stdout'] = outfiles['outfiles']['output.log'] | ||
output_data['success'] = True | ||
#print ('output_data: ', output_data) | ||
|
||
provenance = Provenance(creator="gaussian", version=self.get_version(), routine='g09').dict() | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable provenance is not used.
|
||
|
||
stdout = outfiles.pop('stdout') | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable stdout is not used.
|
||
stderr = outfiles.pop('stderr') | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable stderr is not used.
|
||
#print("\nPRINT STDOUT: \n", stdout) | ||
|
||
|
||
method = input_model.model.method.lower() | ||
Check notice Code scanning / CodeQL Unused local variable Note
Variable method is not used.
|
||
#method = method[4:] if method.startswith("") else method | ||
|
||
# filter unwanted data | ||
to_remove = ['atomnos', 'atomcoords', 'natom'] | ||
output_data['extras'] = {'cclib': {k:v for k, v in cclib_vars.items() if k not in to_remove}} | ||
|
||
# HACK - scf_values can have NaN | ||
# remove for now | ||
output_data['extras']['cclib'].pop('scfvalues') | ||
|
||
if (input_model.protocols.native_files == 'all'): | ||
output_data['native_files'] = {} | ||
|
||
tmp_input_file = os.path.join(tmp_output_path, 'input.inp') | ||
with open(tmp_input_file, 'rt') as f: | ||
output_data['native_files']['input.inp'] = f.read() | ||
|
||
# formcheck keyword always creates the Test.FChk file | ||
tmp_check_file = os.path.join(tmp_output_path, 'Test.FChk') | ||
with open(tmp_check_file, 'rt') as f: | ||
output_data['native_files']['gaussian.fchk'] = f.read() | ||
|
||
merged_data = {**input_model.dict(), **output_data} | ||
|
||
return AtomicResult(**merged_data) | ||
|
Check notice
Code scanning / CodeQL
Unused import Note