diff --git a/ccinput/calculation.py b/ccinput/calculation.py index e18fe44..9378108 100644 --- a/ccinput/calculation.py +++ b/ccinput/calculation.py @@ -447,25 +447,28 @@ def to_xtb(self): t = "dihedral" return f"{t}: {ids_str}\n" - + def to_nwchem(self): - ids_str = " ".join([str(i) for i in self.ids]) - converstion_factor = 1.00 - type = len(self.ids) - if type == 1: - t = "fix atom" - elif type == 2: - t = "spring bond" - converstion_factor = 1.8897259886 - elif type == 3: - raise UnimplementedError("Constraints on angles are not implemented in nwchem") - elif type == 4: - t = "spring dihedral" + ids_str = " ".join([str(i) for i in self.ids]) + converstion_factor = 1.00 + type = len(self.ids) + if type == 1: + t = "fix atom" + elif type == 2: + t = "spring bond" + converstion_factor = 1.8897259886 + elif type == 3: + raise UnimplementedError( + "Constraints on angles are not implemented in nwchem" + ) + elif type == 4: + t = "spring dihedral" + + if self.scan: + pass # TO DO + else: + return f"{t} {ids_str} 200.0 {self.start_d*converstion_factor :.8f} \n" - if self.scan: - pass # TO DO - else: - return f"{t} {ids_str} 200.0 {self.start_d*converstion_factor :.8f} \n" def parse_freeze_constraints(arr, xyz_str, software=""): if len(arr) == 0: diff --git a/ccinput/packages/nwchem.py b/ccinput/packages/nwchem.py index 1e6e12e..5fba95e 100644 --- a/ccinput/packages/nwchem.py +++ b/ccinput/packages/nwchem.py @@ -76,11 +76,15 @@ def __init__(self, calc): self.has_scan = False self.appendix = [] self.command_line = "" - if self.calc.parameters.theory_level == 'hf' or self.calc.parameters.method == 'uhf' or self.calc.parameters.method == 'rhf' : # Name of the block for HF is scf - self.calc.parameters.theory_level = 'scf' - self.method_block=f"{self.calc.parameters.theory_level}" - self.calculation_block="" - self.additional_block="" + if ( + self.calc.parameters.theory_level == "hf" + or self.calc.parameters.method == "uhf" + or self.calc.parameters.method == "rhf" + ): # Name of the block for HF is scf + self.calc.parameters.theory_level = "scf" + self.method_block = f"{self.calc.parameters.theory_level}" + self.calculation_block = "" + self.additional_block = "" self.method_block = f"{self.calc.parameters.theory_level}" self.calculation_block = "" self.additional_block = "" @@ -108,27 +112,27 @@ def clean(self, s): ) return "".join([c for c in s if c in WHITELIST]) - def separate_lines(self,text): - lines = text.split(';') + def separate_lines(self, text): + lines = text.split(";") clean = [] - for line in lines : - if line != '': + for line in lines: + if line != "": clean.append(self.clean(line.lower()).strip()) - else : + else: pass return "\n".join(clean) - + def handle_tasks(self): for word in self.KEYWORDS[self.calc.type]: - self.tasks += f'task {self.calc.parameters.theory_level} {word} \n' - #handle levels of theory - if(self.calc.parameters.theory_level == 'scf') : + self.tasks += f"task {self.calc.parameters.theory_level} {word} \n" + # handle levels of theory + if self.calc.parameters.theory_level == "scf": scf_block = "\n" - if self.calc.parameters.method != 'hf': + if self.calc.parameters.method != "hf": scf_block += f"{self.calc.parameters.method} \n" scf_block += f"{SOFTWARE_MULTIPLICITY['nwchem'][self.calc.multiplicity]} \n" - self.method_block+= scf_block - if(self.calc.parameters.theory_level == 'dft') : + self.method_block += scf_block + if self.calc.parameters.theory_level == "dft": dft_block = f""" xc {self.calc.parameters.method} mult {self.calc.multiplicity} @@ -147,33 +151,38 @@ def handle_basis_sets(self): self.basis_set = f"* library {basis_set}" def handle_specifications(self): - if self.calc.parameters.specifications != '': - temp = "\n" # Here we will store frequency related specifiations in case of FREQOPT calculations + if self.calc.parameters.specifications != "": + temp = "\n" # Here we will store frequency related specifiations in case of FREQOPT calculations s = self.separate_lines(self.calc.parameters.specifications) - for spec in s.split('\n'): + for spec in s.split("\n"): # format of the specifications is BLOCK_NAME1(command1);BLOCK_NAME2(command2);... - matched = re.search(r".*\((.*)\)",spec) - if matched == None : + matched = re.search(r".*\((.*)\)", spec) + if matched == None: self.additional_block += f"{spec} \n" - else : + else: command = matched.group(1) - block_name = spec[:matched.span(1)[0]-1] - if block_name == 'scf' or block_name == 'dft' or block_name == 'hf' : - self.method_block += f"{command} \n" - elif (block_name == 'opt' or block_name == 'ts') and (self.calc.type == CalcType.CONSTR_OPT or self.calc.type == CalcType.OPT or self.calc.type == CalcType.TS or self.calc.type == CalcType.OPTFREQ) : - if self.calculation_block == '': + block_name = spec[: matched.span(1)[0] - 1] + if block_name == "scf" or block_name == "dft" or block_name == "hf": + self.method_block += f"{command} \n" + elif (block_name == "opt" or block_name == "ts") and ( + self.calc.type == CalcType.CONSTR_OPT + or self.calc.type == CalcType.OPT + or self.calc.type == CalcType.TS + or self.calc.type == CalcType.OPTFREQ + ): + if self.calculation_block == "": self.calculation_block += f"\n driver \n" self.calculation_block += f"{command} \n" - elif block_name == 'nmr' and self.calc.type == CalcType.NMR : + elif block_name == "nmr" and self.calc.type == CalcType.NMR: self.calculation_block += f"{command} \n" - elif block_name == 'freq' and self.calc.type == CalcType.FREQ : - if self.calculation_block == '': + elif block_name == "freq" and self.calc.type == CalcType.FREQ: + if self.calculation_block == "": self.calculation_block += f"\n freq \n" self.calculation_block += f"{command} \n" - elif block_name == 'freq' and self.calc.type == CalcType.OPTFREQ : + elif block_name == "freq" and self.calc.type == CalcType.OPTFREQ: temp += f"{command} \n" - if temp != '\n': - self.additional_block += f'freq {temp} end \n' + if temp != "\n": + self.additional_block += f"freq {temp} end \n" # Handle contraints if self.calc.type == CalcType.CONSTR_OPT: @@ -184,9 +193,8 @@ def handle_specifications(self): self.additional_block += constraint.to_nwchem() self.additional_block += "end \n" - if self.additional_block.strip() != '': - self.additional_block = '\n' + self.additional_block - + if self.additional_block.strip() != "": + self.additional_block = "\n" + self.additional_block def handle_xyz(self): lines = [i + "\n" for i in clean_xyz(self.calc.xyz).split("\n") if i != ""] diff --git a/ccinput/tests/test_nwchem.py b/ccinput/tests/test_nwchem.py index 26d04c7..f4b3057 100644 --- a/ccinput/tests/test_nwchem.py +++ b/ccinput/tests/test_nwchem.py @@ -1,9 +1,12 @@ from ccinput.tests.testing_utilities import InputTests -from ccinput.exceptions import InvalidParameter, ImpossibleCalculation, UnimplementedError +from ccinput.exceptions import ( + InvalidParameter, + ImpossibleCalculation, + UnimplementedError, +) class NwchemTests(InputTests): - def test_sp_HF(self): params = { "nproc": 8, @@ -40,7 +43,7 @@ def test_sp_HF(self): """ self.assertTrue(self.is_equivalent(REF, inp.input_file)) - + def test_sp_DFT(self): params = { "nproc": 8, @@ -120,21 +123,21 @@ def test_sp_DFT_specifications(self): self.assertTrue(self.is_equivalent(REF, inp.input_file)) def test_sp_DFT_specifications2(self): - params = { - "nproc": 8, - "mem": "10000MB", - "type": "Single-Point Energy", - "file": "Cl.xyz", - "software": "nwchem", - "method": "M06-2X", - "basis_set": "Def2-SVP", - "charge": "-1", - "specifications": "dft(grid coarse)", - } - - inp = self.generate_calculation(**params) - - REF = """ + params = { + "nproc": 8, + "mem": "10000MB", + "type": "Single-Point Energy", + "file": "Cl.xyz", + "software": "nwchem", + "method": "M06-2X", + "basis_set": "Def2-SVP", + "charge": "-1", + "specifications": "dft(grid coarse)", + } + + inp = self.generate_calculation(**params) + + REF = """ TITLE "File created by ccinput" start Cl memory total 10000 mb @@ -157,7 +160,7 @@ def test_sp_DFT_specifications2(self): task dft energy """ - self.assertTrue(self.is_equivalent(REF, inp.input_file)) + self.assertTrue(self.is_equivalent(REF, inp.input_file)) def test_superfluous_specifications(self): params = { @@ -313,7 +316,7 @@ def test_opt_DFT(self): """ self.assertTrue(self.is_equivalent(REF, inp.input_file)) - + def test_freq_HF(self): params = { "nproc": 8, @@ -908,7 +911,6 @@ def test_specifications_mixed(self): self.assertTrue(self.is_equivalent(REF, inp.input_file)) - def test_special_char(self): params = { "nproc": 8, @@ -1251,7 +1253,6 @@ def test_opt_freq2(self): self.assertTrue(self.is_equivalent(REF, inp.input_file)) - def test_opt_freq_spec(self): params = { "nproc": 8, @@ -1392,7 +1393,6 @@ def test_d3_d3bj_crash(self): with self.assertRaises(InvalidParameter): self.generate_calculation(**params) - def test_unavailable_calc_type(self): params = { "nproc": 8, @@ -1407,4 +1407,3 @@ def test_unavailable_calc_type(self): with self.assertRaises(ImpossibleCalculation): self.generate_calculation(**params) - diff --git a/ccinput/utilities.py b/ccinput/utilities.py index d4ecf43..0bf3ea5 100644 --- a/ccinput/utilities.py +++ b/ccinput/utilities.py @@ -292,9 +292,7 @@ def get_method(method, software): pass else: if abs_method in SOFTWARE_METHODS[software]: - return ( - method[0] + SOFTWARE_METHODS[software][abs_method] - ) + return method[0] + SOFTWARE_METHODS[software][abs_method] # nwchem also supports combination of functionals if len(method.split()) == 2: xc_check = is_exchange_correlation_combination(