Skip to content
Open
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c9c49a4
Create gaussian.py
QuChem Nov 29, 2023
70596ae
Update gaussian.py
QuChem Nov 29, 2023
b4c5d30
Cleaning gaussian.py
QuChem Nov 29, 2023
1aa3522
Removed the empty line
QuChem Nov 30, 2023
10ae387
implement cclib into gaussian.py
QuChem Dec 12, 2023
11fc097
Convert QCElemental inputs to Gaussian inputs
QuChem Dec 20, 2023
ee0dcb4
Convert QCElemental inputs to Gaussian inputs
QuChem Dec 20, 2023
f93420c
Add input generator
QuChem Jan 10, 2024
3050155
Improved the parse_output function
QuChem Jan 10, 2024
b1097be
Add versioning
QuChem Jan 18, 2024
a5265b6
Improved parse_output function
QuChem Jan 23, 2024
5d73c9f
the parse_output function extracts more data
QuChem Feb 1, 2024
6eb55e4
Cleaning gaussian.py
QuChem Feb 14, 2024
aad52cf
Create programs.py
QuChem Feb 16, 2024
ce55302
Add gaussian to base.py
QuChem Feb 16, 2024
0ccc8ef
Delete qcengine/programs.py
QuChem Mar 1, 2024
30e2579
Changed "Gaussian" to "gaussian"
QuChem Mar 14, 2024
d76c5e7
Try to handle .fchk file format in gaussian.py
QuChem Apr 11, 2024
95d16f0
Handle the fchk file in gaussian.py
QuChem Apr 12, 2024
61216dd
Dependency cclib check
QuChem Jun 12, 2024
a2ff5c8
gaussian.py encompasses g09 and g16
QuChem Jun 12, 2024
ab23bb6
Extract different energy values
QuChem Jul 15, 2024
5df8272
reordered the if-else statement
QuChem Jul 16, 2024
6377cb0
Add additional libraries to gaussian.py
QuChem Jan 16, 2025
c7c0c1c
Add major updates to most of the methos in gaussian.py
QuChem Jan 16, 2025
39d565a
Update parse_output method
QuChem Jan 29, 2025
1d04979
Update compute method
QuChem Jan 29, 2025
62b0b9b
Update build_input method
QuChem Jan 29, 2025
3ccdc93
Update the execute method
QuChem Jan 29, 2025
9c39b94
Update the parse_output method
QuChem Jan 31, 2025
f9819c5
Create gaussian_opt.py
QuChem Mar 25, 2025
9006ffd
Create __init__.py
QuChem Mar 25, 2025
60a4270
Added GaussianDriverProcedure
QuChem Mar 25, 2025
e7829be
Update gaussian.py
QuChem Mar 26, 2025
6af8c3c
Added some comments to methods
QuChem Apr 1, 2025
4a7dca6
Cleanup & add additional_route
bennybp May 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions qcengine/programs/gaussian.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
"""
Calls the GAUSSIAN executable
"""

import os
import re

Check notice

Code scanning / CodeQL

Unused import

Import of 're' is not used.
import tempfile

Check notice

Code scanning / CodeQL

Unused import

Import of 'tempfile' is not used.
import warnings

Check notice

Code scanning / CodeQL

Unused import

Import of 'warnings' is not used.
from collections import defaultdict

Check notice

Code scanning / CodeQL

Unused import

Import of 'defaultdict' is not used.
from typing import Any, Dict, List, Optional, Tuple

Check notice

Code scanning / CodeQL

Unused import

Import of 'Tuple' is not used.
import cclib
from cclib.method import Nuclear

import numpy as np

Check notice

Code scanning / CodeQL

Unused import

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

Import of 'Molecule' is not used.
from qcelemental.molparse import regex

Check notice

Code scanning / CodeQL

Unused import

Import of 'regex' is not used.
from qcelemental.util import parse_version, safe_version, which

Check notice

Code scanning / CodeQL

Unused import

Import of 'parse_version' is not used.

from qcengine.config import TaskConfig, get_config

Check notice

Code 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 notice

Code 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",
Comment thread
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`."
)
Comment thread
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 notice

Code 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"])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 AtIn.keywords as the experience user would use in the program natively. Is Conver=N on https://gaussian.com/scf/ ("Options" tab) what you're targeting?

if 'POPULATION' in keywords:
gaussian_kw.append('Pop=' + keywords['POPULATION'])

keywords = {'scf_damp': 'true',

Check notice

Code 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 notice

Code 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
Comment thread
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 notice

Code 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()
Comment thread Fixed

stdout = outfiles.pop('stdout')

Check notice

Code scanning / CodeQL

Unused local variable

Variable stdout is not used.
stderr = outfiles.pop('stderr')

Check notice

Code scanning / CodeQL

Unused local variable

Variable stderr is not used.
#print("\nPRINT STDOUT: \n", stdout)


method = input_model.model.method.lower()

Check notice

Code 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)