From 8b126f0bda55113b892330d0b1cd89ed5d488cad Mon Sep 17 00:00:00 2001 From: avcopan Date: Sat, 21 Sep 2024 16:11:30 -0500 Subject: [PATCH] New: Adds PM3 semi-empirical method for Gaussian 16 Also, makes basis set optional. --- elstruct/par.py | 35 +++++++++++++++++-- elstruct/reader/_gaussian16/energ.py | 2 ++ .../writer/_gaussian16/templates/all.mako | 4 ++- elstruct/writer/fill.py | 29 ++------------- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/elstruct/par.py b/elstruct/par.py index 2f15066f..927a1385 100644 --- a/elstruct/par.py +++ b/elstruct/par.py @@ -13,7 +13,7 @@ def standard_case(name): :type name: str :rtype: str """ - return name.lower() + return name.lower() if isinstance(name, str) else name class Module(): @@ -101,6 +101,12 @@ class Method(): 'hf', 'hf', ('R',), ('U', 'R'))}) + class SemiEmpirical(): + PM3 = ('pm3', + {Program.GAUSSIAN16: ( + 'pm3', 'pm3', + ('R',), ('U', 'R'))}) + class Corr(): """ Correlated method names """ MP2 = ('mp2', @@ -353,6 +359,21 @@ def contains(cls, name): return name in names + @classmethod + def is_semi_empirical(cls, name): + """ Assess if a method is a semi-empirical method. + + :param cls: class object + :type cls: obj + :param name: name of method + :type name: str + """ + + name = standard_case(name) + semi_emp_names = [row[0] for row in pclass.all_values(cls.SemiEmpirical)] + + return name in semi_emp_names + @classmethod def is_correlated(cls, name): """ Assess if a method is a single-reference correlated method. @@ -632,6 +653,16 @@ class Basis(): (name, {program: name}) """ + NONE = (None, {Program.CFOUR2: None, + Program.GAUSSIAN09: None, + Program.GAUSSIAN03: None, + Program.GAUSSIAN16: None, + Program.MOLPRO2015: None, + Program.MOLPRO2021: None, + Program.MRCC2018: None, + Program.NWCHEM6: None, + Program.ORCA4: None, + Program.PSI4: None}) STO3G = ('sto-3g', {Program.CFOUR2: None, Program.GAUSSIAN09: None, @@ -960,7 +991,7 @@ def is_nonstandard_basis(name): :param name: name of basis set :type name: str """ - return name.lower().startswith('basis:') + return isinstance(name, str) and name.lower().startswith('basis:') @classmethod def nonstandard_basis_name(cls, name): diff --git a/elstruct/reader/_gaussian16/energ.py b/elstruct/reader/_gaussian16/energ.py index 1245a676..ee273e32 100644 --- a/elstruct/reader/_gaussian16/energ.py +++ b/elstruct/reader/_gaussian16/energ.py @@ -106,6 +106,8 @@ def _doub_hyb_dft_energy(output_str): ENERGY_READER_DCT[(METHOD, frozenset({}))] = _dft_energy else: ENERGY_READER_DCT[(METHOD, frozenset({}))] = _doub_hyb_dft_energy + elif Method.is_semi_empirical(METHOD): + ENERGY_READER_DCT[(METHOD, frozenset({}))] = _dft_energy # Check if we have added any unsupported methods to the energy reader READ_METHODS = set(method[0] for method in ENERGY_READER_DCT) diff --git a/elstruct/writer/_gaussian16/templates/all.mako b/elstruct/writer/_gaussian16/templates/all.mako index 75c0aed1..83775783 100644 --- a/elstruct/writer/_gaussian16/templates/all.mako +++ b/elstruct/writer/_gaussian16/templates/all.mako @@ -4,8 +4,10 @@ ${machine_options} ## 1. theoretical method block % if reference: #P ${reference} ${method}/${basis} -% else: +% elif basis: #P ${method}/${basis} +% else: +#P ${method} % endif % if reference != 'rohf' and method != 'rohf': # SCF=(xqc) diff --git a/elstruct/writer/fill.py b/elstruct/writer/fill.py index 2cfc03b3..56a8a6f1 100644 --- a/elstruct/writer/fill.py +++ b/elstruct/writer/fill.py @@ -126,31 +126,6 @@ def _offset_planar_vals(val): return geo_str, zmat_vval_str, zmat_cval_str -def _name_mat(zma, frozen_coordinates, job_key): - """ Build the name matrix for a Z-Matrix data structure: - - used for cfour optimizations - - :param zma: cartesian or z-matrix geometry - :type zma: tuple - :param frozen_coordinates: only with z-matrix geometries; list of - coordinate names to freeze - :type fozen_coordinates: tuple[str] - :param job_key: job contained in the inpit file - :type job_key: str - """ - if job_key == 'optimization': - name_mat = [ - [name+'*' - if name is not None and name not in frozen_coordinates else name - for name in row] - for row in automol.zmat.name_matrix(zma)] - else: - name_mat = automol.zmat.name_matrix(zma) - - return name_mat - - def build_gen_lines(gen_lines, line1=None, line2=None, line3=None): """ Set three lines for writing in various blocks of files. Function either grabs lines from the dictionary and if nothing @@ -300,7 +275,7 @@ def program_method_names(prog, method, basis, mult, orb_restricted): elif method == Method.HF[0]: if prog in (Program.GAUSSIAN09, Program.GAUSSIAN03, Program.GAUSSIAN16): - prog_method = prog_reference + prog_method = prog_reference if prog_reference else method else: prog_method = program_method_name(prog, method, singlet=singlet) else: @@ -331,7 +306,7 @@ def _reference(prog, method, mult, orb_restricted): :rtype: str """ # Need a multiref version - if Method.is_dft(method): + if Method.is_dft(method) or Method.is_semi_empirical(method): reference = _dft_reference(prog, orb_restricted) elif method == Method.HF[0]: reference = _hf_reference(prog, mult, orb_restricted)