-
Notifications
You must be signed in to change notification settings - Fork 85
Add Gaussian harness #442
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
base: master
Are you sure you want to change the base?
Add Gaussian harness #442
Changes from 13 commits
c9c49a4
70596ae
b4c5d30
1aa3522
10ae387
11fc097
ee0dcb4
f93420c
3050155
b1097be
a5265b6
5d73c9f
6eb55e4
aad52cf
ce55302
0ccc8ef
30e2579
d76c5e7
95d16f0
61216dd
a2ff5c8
ab23bb6
5df8272
6377cb0
c7c0c1c
39d565a
1d04979
62b0b9b
3ccdc93
9c39b94
f9819c5
9006ffd
60a4270
e7829be
6af8c3c
4a7dca6
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,240 @@ | ||
| """ | ||
| Calls the GAUSSIAN executable | ||
| """ | ||
|
|
||
| import os | ||
| import re | ||
| import tempfile | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'tempfile' is not used.
|
||
| import warnings | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'warnings' is not used.
|
||
| from collections import defaultdict | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'defaultdict' is not used.
|
||
| from typing import Any, Dict, List, Optional, Tuple | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'Tuple' is not used.
|
||
| import cclib | ||
| from cclib.method import Nuclear | ||
|
|
||
| import numpy as np | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'np' is not used.
|
||
| from qcelemental import constants | ||
| from qcelemental.models import AtomicInput, AtomicResult, Molecule, Provenance | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'Molecule' is not used.
|
||
| from qcelemental.molparse import regex | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'regex' is not used.
|
||
| from qcelemental.util import parse_version, safe_version, which | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'parse_version' is not used.
|
||
|
|
||
| from qcengine.config import TaskConfig, get_config | ||
Check noticeCode scanning / CodeQL Unused import
Import of 'get_config' is not used.
|
||
|
|
||
| from ..exceptions import InputError, UnknownError | ||
| from ..util import disk_files, execute, temporary_directory | ||
Check noticeCode scanning / CodeQL Unused import
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.
|
||
| 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.
|
||
|
|
||
| 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) | ||
|
|
||
| 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" | ||
Check noticeCode scanning / CodeQL Commented-out code
This comment appears to contain commented-out code.
|
||
| if 'SCF_CONVERGENCE' in keywords: | ||
| gaussian_kw.append('SCF=' + keywords["SCF_CONVERGENCE"]) | ||
|
Collaborator
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 noticeCode scanning / CodeQL Unused local variable
Variable keywords is not used.
|
||
| 'scf_diis': 'false'} | ||
| # 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") | ||
|
|
||
| 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_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 noticeCode scanning / CodeQL Unused local variable
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.
Outdated
|
||
| #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
+259
to
+261
Check noticeCode scanning / CodeQL Commented-out code
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() | ||
|
|
||
|
|
||
| stdout = outfiles.pop('stdout') | ||
Check noticeCode scanning / CodeQL Unused local variable
Variable stdout is not used.
|
||
| stderr = outfiles.pop('stderr') | ||
Check noticeCode scanning / CodeQL Unused local variable
Variable stderr is not used.
|
||
| #print("\nPRINT STDOUT: \n", stdout) | ||
|
|
||
|
|
||
| method = input_model.model.method.lower() | ||
Check noticeCode scanning / CodeQL Unused local variable
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}} | ||
|
|
||
| merged_data = {**input_model.dict(), **output_data} | ||
|
|
||
| return AtomicResult(**merged_data) | ||
|
|
||
Check notice
Code scanning / CodeQL
Unused import