def parse_output(self, outfiles: Dict[str, str], input_model: 'ResultInput') -> 'Result': stdout = outfiles.pop("stdout") for fl, contents in outfiles.items(): if contents is not None: # LOG text += f'\n DFTD3 scratch file {fl} has been read.\n' pass # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) real = np.array(input_model.molecule.real) full_nat = real.shape[0] real_nat = np.sum(real) for ln in stdout.splitlines(): if re.match(' Edisp /kcal,au', ln): ene = Decimal(ln.split()[3]) elif re.match(r" E6\(ABC\) \" :", ln): # c. v3.2.0 raise ResourceError( "Cannot process ATM results from DFTD3 prior to v3.2.1.") elif re.match(r""" E6\(ABC\) /kcal,au:""", ln): atm = Decimal(ln.split()[-1]) elif re.match(' normal termination of dftd3', ln): break else: if not ((real_nat == 1) and (input_model.driver == 'gradient')): raise UnknownError( 'Unsuccessful run. Possibly -D variant not available in dftd3 version.' ) # parse gradient output # * DFTD3 crashes on one-atom gradients. Avoid the error (above) and just force the correct result (below). if outfiles['dftd3_gradient'] is not None: srealgrad = outfiles['dftd3_gradient'].replace('D', 'E') realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=' ').reshape((-1, 3)) elif real_nat == 1: realgrad = np.zeros((1, 3)) if outfiles['dftd3_abc_gradient'] is not None: srealgrad = outfiles['dftd3_abc_gradient'].replace('D', 'E') realgradabc = np.fromstring(srealgrad, count=3 * real_nat, sep=' ').reshape((-1, 3)) elif real_nat == 1: realgradabc = np.zeros((1, 3)) if input_model.driver == 'gradient': ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) rg = realgradabc if (input_model.extras['info']['dashlevel'] == 'atmgr') else realgrad try: fullgrad[ireal, :] = rg except NameError as exc: raise UnknownError( 'Unsuccessful gradient collection.') from exc qcvkey = input_model.extras['info']['fctldash'].upper() calcinfo = [] if input_model.extras['info']['dashlevel'] == 'atmgr': calcinfo.append(qcel.Datum('CURRENT ENERGY', 'Eh', atm)) calcinfo.append( qcel.Datum('DISPERSION CORRECTION ENERGY', 'Eh', atm)) calcinfo.append( qcel.Datum('3-BODY DISPERSION CORRECTION ENERGY', 'Eh', atm)) calcinfo.append( qcel.Datum( 'AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY', 'Eh', atm)) if input_model.driver == 'gradient': calcinfo.append( qcel.Datum('CURRENT GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('3-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum( 'AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) else: calcinfo.append(qcel.Datum('CURRENT ENERGY', 'Eh', ene)) calcinfo.append( qcel.Datum('DISPERSION CORRECTION ENERGY', 'Eh', ene)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION ENERGY', 'Eh', ene)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION ENERGY', 'Eh', ene)) if input_model.driver == 'gradient': calcinfo.append( qcel.Datum('CURRENT GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) #LOGtext += qcel.datum.print_variables({info.label: info for info in calcinfo}) calcinfo = {info.label: info.data for info in calcinfo} #calcinfo = qcel.util.unnp(calcinfo, flat=True) # got to even out who needs plump/flat/Decimal/float/ndarray/list # Decimal --> str preserves precision calcinfo = { k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in qcel.util.unnp(calcinfo, flat=True).items() } # jobrec['properties'] = {"return_energy": ene} # jobrec["molecule"]["real"] = list(jobrec["molecule"]["real"]) retres = calcinfo[f'CURRENT {input_model.driver.upper()}'] if isinstance(retres, Decimal): retres = float(retres) elif isinstance(retres, np.ndarray): retres = retres.ravel().tolist() output_data = { 'extras': input_model.extras, 'properties': {}, 'provenance': Provenance(creator="DFTD3", version=self.get_version(), routine=__name__ + '.' + sys._getframe().f_code.co_name), 'return_result': retres, 'stdout': stdout, } # yapf: disable output_data["extras"]['local_keywords'] = input_model.extras['info'] output_data["extras"]['qcvars'] = calcinfo output_data['success'] = True return Result(**{**input_model.dict(), **output_data})
def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": stdout = outfiles.pop("stdout") for fl, contents in outfiles.items(): if contents is not None: # LOG text += f'\n DFTD3 scratch file {fl} has been read.\n' pass # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) real = np.array(input_model.molecule.real) full_nat = real.shape[0] real_nat = np.sum(real) for ln in stdout.splitlines(): if re.match(" Edisp /kcal,au", ln): ene = Decimal(ln.split()[3]) elif re.match(r" E6\(ABC\) \" :", ln): # c. v3.2.0 raise ResourceError( "Cannot process ATM results from DFTD3 prior to v3.2.1.") elif re.match(r""" E6\(ABC\) /kcal,au:""", ln): atm = Decimal(ln.split()[-1]) elif re.match(" normal termination of dftd3", ln): break else: if not ((real_nat == 1) and (input_model.driver == "gradient")): raise UnknownError( f"Unsuccessful run. Check input, particularly geometry in [a0]. Model: {input_model.model}" ) # parse gradient output # * DFTD3 crashes on one-atom gradients. Avoid the error (above) and just force the correct result (below). if outfiles["dftd3_gradient"] is not None: srealgrad = outfiles["dftd3_gradient"].replace("D", "E") realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) elif real_nat == 1: realgrad = np.zeros((1, 3)) if outfiles["dftd3_abc_gradient"] is not None: srealgrad = outfiles["dftd3_abc_gradient"].replace("D", "E") realgradabc = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) elif real_nat == 1: realgradabc = np.zeros((1, 3)) if input_model.driver == "gradient": ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) rg = realgradabc if (input_model.extras["info"]["dashlevel"] == "atmgr") else realgrad try: fullgrad[ireal, :] = rg except NameError as exc: raise UnknownError( "Unsuccessful gradient collection.") from exc qcvkey = input_model.extras["info"]["fctldash"].upper() calcinfo = [] if input_model.extras["info"]["dashlevel"] == "atmgr": calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", atm)) calcinfo.append( qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", atm)) calcinfo.append( qcel.Datum("3-BODY DISPERSION CORRECTION ENERGY", "Eh", atm)) calcinfo.append( qcel.Datum( "AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY", "Eh", atm)) if input_model.driver == "gradient": calcinfo.append( qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("3-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum( "AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) else: calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) calcinfo.append( qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", ene)) calcinfo.append( qcel.Datum("2-BODY DISPERSION CORRECTION ENERGY", "Eh", ene)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} DISPERSION CORRECTION ENERGY", "Eh", ene)) if input_model.driver == "gradient": calcinfo.append( qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("2-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) # LOGtext += qcel.datum.print_variables({info.label: info for info in calcinfo}) calcinfo = {info.label: info.data for info in calcinfo} # calcinfo = qcel.util.unnp(calcinfo, flat=True) # got to even out who needs plump/flat/Decimal/float/ndarray/list # Decimal --> str preserves precision calcinfo = { k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in qcel.util.unnp(calcinfo, flat=True).items() } # jobrec['properties'] = {"return_energy": ene} # jobrec["molecule"]["real"] = list(jobrec["molecule"]["real"]) retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] if isinstance(retres, Decimal): retres = float(retres) elif isinstance(retres, np.ndarray): retres = retres.ravel().tolist() output_data = { "extras": input_model.extras, "properties": {}, "provenance": Provenance(creator="DFTD3", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name), "return_result": retres, "stdout": stdout, } output_data["extras"]["local_keywords"] = input_model.extras["info"] output_data["extras"]["qcvars"] = calcinfo output_data["success"] = True return AtomicResult(**{**input_model.dict(), **output_data})
def dftd3_harvest(jobrec, modulerec): """Process raw results from read-only `dftd3rec` into Datum fields in returned `jobrec`: jobrec@i, dftd3rec@io -> jobrec@io. Parameters ---------- jobrec : dict Nested dictionary with input specifications for DFTD3 in generic QC terms. dftd3rec : dict Nested dictionary with input specification and output collection from DFTD3 in program-specific commands, files, & output capture. Returns ------- jobrec : dict Nested dictionary with input specification and output collection from DFTD3 in generic QC terms. Notes ----- Central to harvesting is the fact (to the planting, not to the DFTD3 program) that 2-body and 3-body are run separately. Because of how damping functions work (see GH:psi4/psi4#1407), some 2-body damping schemes can give wrong answers for 3-body. And because 3-body is set to run with some dummy values, the 2-body values are no good. """ # amalgamate output text = modulerec['stdout'] text += '\n <<< DFTD3 Results >>>\n' for fl, contents in modulerec['outfiles'].items(): if contents is not None: text += f'\n DFTD3 scratch file {fl} has been read.\n' text += contents # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) real = np.array(jobrec['molecule']['real']) full_nat = real.shape[0] real_nat = np.sum(real) for ln in modulerec['stdout'].splitlines(): if re.search('DFTD3 V', ln): version = ln.replace('DFTD3', '').replace('|', '').strip().lower() elif re.match(' Edisp /kcal,au', ln): ene = Decimal(ln.split()[3]) elif re.match(r" E6\(ABC\) \" :", ln): # c. v3.2.0 raise ResourceError( "Cannot process ATM results from DFTD3 prior to v3.2.1.") elif re.match(r""" E6\(ABC\) /kcal,au:""", ln): atm = Decimal(ln.split()[-1]) elif re.match(' normal termination of dftd3', ln): break else: if not ((real_nat == 1) and (jobrec['driver'] == 'gradient')): raise UnknownError( 'Unsuccessful run. Possibly -D variant not available in dftd3 version.' ) # parse gradient output # * DFTD3 crashes on one-atom gradients. Avoid the error (above) and just force the correct result (below). if modulerec['outfiles']['dftd3_gradient'] is not None: srealgrad = modulerec['outfiles']['dftd3_gradient'].replace('D', 'E') realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=' ').reshape((-1, 3)) elif real_nat == 1: realgrad = np.zeros((1, 3)) if modulerec['outfiles']['dftd3_abc_gradient'] is not None: srealgrad = modulerec['outfiles']['dftd3_abc_gradient'].replace( 'D', 'E') realgradabc = np.fromstring(srealgrad, count=3 * real_nat, sep=' ').reshape((-1, 3)) elif real_nat == 1: realgradabc = np.zeros((1, 3)) if jobrec['driver'] == 'gradient': ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) rg = realgradabc if (jobrec['extras']['info']['dashlevel'] == 'atmgr') else realgrad try: fullgrad[ireal, :] = rg except NameError as exc: raise Dftd3Error('Unsuccessful gradient collection.') from exc qcvkey = jobrec['extras']['info']['fctldash'].upper() # OLD WAY calcinfo = [] if jobrec['extras']['info']['dashlevel'] == 'atmgr': calcinfo.append(qcel.Datum('DISPERSION CORRECTION ENERGY', 'Eh', atm)) calcinfo.append( qcel.Datum('3-BODY DISPERSION CORRECTION ENERGY', 'Eh', atm)) calcinfo.append( qcel.Datum( 'AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY', 'Eh', atm)) if jobrec['driver'] == 'gradient': calcinfo.append( qcel.Datum('DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('3-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum( 'AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) else: calcinfo.append(qcel.Datum('DISPERSION CORRECTION ENERGY', 'Eh', ene)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION ENERGY', 'Eh', ene)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION ENERGY', 'Eh', ene)) if jobrec['driver'] == 'gradient': calcinfo.append( qcel.Datum('DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo1 = {info.label: info for info in calcinfo} text += qcel.datum.print_variables(calcinfo1) calcinfo = {info.label: info.data for info in calcinfo} calcinfo = qcel.util.unnp(calcinfo, flat=True) # NEW WAY #module_vars = {} #module_vars['DISPERSION CORRECTION ENERGY'] = ene #module_vars['{} DISPERSION CORRECTION ENERGY'.format(qcvkey)] = ene #if jobrec['driver'] == 'gradient': # module_vars['DISPERSION CORRECTION GRADIENT'] = fullgrad # module_vars['{} DISPERSION CORRECTION GRADIENT'.format(qcvkey)] = fullgrad # #module_vars = PreservingDict(module_vars) #qcvars.build_out(module_vars) #calcinfo = qcvars.certify(module_vars) #text += print_variables(calcinfo) jobrec['stdout'] = text jobrec['extras']['qcvars'] = calcinfo prov = {} prov['creator'] = 'dftd3' prov['routine'] = sys._getframe().f_code.co_name prov['version'] = version jobrec['provenance'] = prov return jobrec
def test_complex_array(): datum1 = qcel.Datum('complex array', '', np.arange(3, dtype=np.complex_) + 1j) ans = {'label': 'complex array', 'units': '', 'data': [complex(0, 1), complex(1, 1), complex(2, 1)]} dicary = datum1.dict() assert compare_recursive(ans, dicary, 9)
def test_creation_error(): with pytest.raises(pydantic.ValidationError): qcel.Datum('ze lbl', 'ze unit', 'ze data')
def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": stdout = outfiles.pop("stdout") # parse energy output (could go further and break into E6, E8, E10 and Cn coeff) real = np.array(input_model.molecule.real) full_nat = real.shape[0] real_nat = np.sum(real) for ln in stdout.splitlines(): if re.match(" Egcp:", ln): ene = Decimal(ln.split()[1]) elif re.match(" normal termination of gCP", ln): break else: if self._defaults["name"] == "GCP" and not ( (real_nat == 1) and (input_model.driver == "gradient")): raise UnknownError( f"Unsuccessful run. Check input, particularly geometry in [a0]. Model: {input_model.model}" ) # parse gradient output if outfiles["gcp_gradient"] is not None: srealgrad = outfiles["gcp_gradient"].replace("D", "E") realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) elif real_nat == 1: realgrad = np.zeros((1, 3)) if input_model.driver == "gradient": ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) try: fullgrad[ireal, :] = realgrad except NameError as exc: raise UnknownError( "Unsuccessful gradient collection.") from exc qcvkey = input_model.model.method.upper() calcinfo = [] calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) calcinfo.append(qcel.Datum("GCP CORRECTION ENERGY", "Eh", ene)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} GCP CORRECTION ENERGY", "Eh", ene)) if input_model.driver == "gradient": calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("GCP CORRECTION GRADIENT", "Eh/a0", fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} GCP CORRECTION GRADIENT", "Eh/a0", fullgrad)) calcinfo = {info.label: info.data for info in calcinfo} # Decimal --> str preserves precision calcinfo = { k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in calcinfo.items() } retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] if isinstance(retres, Decimal): retres = float(retres) elif isinstance(retres, np.ndarray): retres = retres.ravel().tolist() output_data = { "extras": input_model.extras, "properties": {}, "provenance": Provenance(creator="GCP", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name), "return_result": retres, "stdout": stdout, } output_data["extras"]["qcvars"] = calcinfo output_data["success"] = True return AtomicResult(**{**input_model.dict(), **output_data})
def test_creation_error(): with pytest.raises(pydantic.ValidationError): qcel.Datum("ze lbl", "ze unit", "ze data")
def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> "AtomicResult": stdout = outfiles.pop("stdout") stderr = outfiles.pop("stderr") for fl, contents in outfiles.items(): if contents is not None: # LOG text += f'\n MP2D scratch file {fl} has been read.\n' pass # parse energy output (could go further and break into UCHF, CKS) real = np.array(input_model.molecule.real) full_nat = real.shape[0] real_nat = np.sum(real) for ln in stdout.splitlines(): if re.match(" MP2D dispersion correction Eh", ln): ene = Decimal(ln.split()[4]) elif re.match("Atomic Coordinates in Angstroms", ln): break else: if not ((real_nat == 1) and (input_model.driver == "gradient")): raise UnknownError("Unknown issue occured.") # parse gradient output if outfiles["mp2d_gradient"] is not None: srealgrad = outfiles["mp2d_gradient"] realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=" ").reshape((-1, 3)) if input_model.driver == "gradient": ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) try: fullgrad[ireal, :] = realgrad except NameError as exc: raise UnknownError( "Unsuccessful gradient collection.") from exc qcvkey = input_model.extras["info"]["fctldash"].upper() calcinfo = [] calcinfo.append(qcel.Datum("CURRENT ENERGY", "Eh", ene)) calcinfo.append(qcel.Datum("DISPERSION CORRECTION ENERGY", "Eh", ene)) calcinfo.append( qcel.Datum("2-BODY DISPERSION CORRECTION ENERGY", "Eh", ene)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} DISPERSION CORRECTION ENERGY", "Eh", ene)) if input_model.driver == "gradient": calcinfo.append(qcel.Datum("CURRENT GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) calcinfo.append( qcel.Datum("2-BODY DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f"{qcvkey} DISPERSION CORRECTION GRADIENT", "Eh/a0", fullgrad)) # LOGtext += qcel.datum.print_variables({info.label: info for info in calcinfo}) calcinfo = {info.label: info.data for info in calcinfo} # calcinfo = qcel.util.unnp(calcinfo, flat=True) # got to even out who needs plump/flat/Decimal/float/ndarray/list # Decimal --> str preserves precision calcinfo = { k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in calcinfo.items() } # jobrec['properties'] = {"return_energy": ene} # jobrec["molecule"]["real"] = list(jobrec["molecule"]["real"]) retres = calcinfo[f"CURRENT {input_model.driver.upper()}"] if isinstance(retres, Decimal): retres = float(retres) elif isinstance(retres, np.ndarray): retres = retres.ravel().tolist() output_data = { "extras": input_model.extras, "native_files": {k: v for k, v in outfiles.items() if v is not None}, "properties": {}, "provenance": Provenance(creator="MP2D", version=self.get_version(), routine=__name__ + "." + sys._getframe().f_code.co_name), "return_result": retres, "stderr": stderr, "stdout": stdout, } output_data["extras"]["local_keywords"] = input_model.extras["info"] output_data["extras"]["qcvars"] = calcinfo output_data["success"] = True return AtomicResult(**{**input_model.dict(), **output_data})
def mp2d_harvest(jobrec, modulerec): """Process raw results from read-only `mp2drec` into Datum fields in returned `jobrec`: jobrec@i, mp2drec@io -> jobrec@io. Parameters ---------- jobrec : dict Nested dictionary with input specifications for MP2D in generic QC terms. mp2drec : dict Nested dictionary with input specification and output collection from MP2D in program-specific commands, files, & output capture. Returns ------- jobrec : dict Nested dictionary with input specification and output collection from MP2D in generic QC terms. """ # amalgamate output text = modulerec['stdout'] text += '\n <<< MP2D Results >>>\n' for fl, contents in modulerec['outfiles'].items(): if contents is not None: text += f'\n MP2D scratch file {fl} has been read.\n' text += contents # parse energy output (could go further and break into UCHF, CKS) real = np.array(jobrec['molecule']['real']) full_nat = real.shape[0] real_nat = np.sum(real) for ln in modulerec['stdout'].splitlines(): if re.search('MP2D dispersion correction v', ln): version = ln.replace('MP2D dispersion correction', '').replace('-', '').strip().lower() elif re.match(' MP2D dispersion correction Eh', ln): ene = Decimal(ln.split()[4]) elif re.match('normal termination of mp2d', ln): break else: if not ((real_nat == 1) and (jobrec['driver'] == 'gradient')): raise UnknownError('Unknown issue occured.') # parse gradient output if modulerec['outfiles']['mp2d_gradient'] is not None: srealgrad = modulerec['outfiles']['mp2d_gradient'] realgrad = np.fromstring(srealgrad, count=3 * real_nat, sep=' ').reshape((-1, 3)) if jobrec['driver'] == 'gradient': ireal = np.argwhere(real).reshape((-1)) fullgrad = np.zeros((full_nat, 3)) try: fullgrad[ireal, :] = realgrad except NameError as exc: raise UnknownError('Unsuccessful gradient collection.') from exc qcvkey = jobrec['extras']['info']['fctldash'].upper() calcinfo = [] calcinfo.append(qcel.Datum('DISPERSION CORRECTION ENERGY', 'Eh', ene)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION ENERGY', 'Eh', ene)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION ENERGY', 'Eh', ene)) if jobrec['driver'] == 'gradient': calcinfo.append( qcel.Datum('DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo.append( qcel.Datum('2-BODY DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) if qcvkey: calcinfo.append( qcel.Datum(f'{qcvkey} DISPERSION CORRECTION GRADIENT', 'Eh/a0', fullgrad)) calcinfo1 = {info.label: info for info in calcinfo} text += qcel.datum.print_variables(calcinfo1) calcinfo = {info.label: info.data for info in calcinfo} calcinfo = qcel.util.unnp(calcinfo, flat=True) jobrec['stdout'] = text jobrec['extras']['qcvars'] = calcinfo prov = {} prov['creator'] = 'mp2d' prov['routine'] = sys._getframe().f_code.co_name prov['version'] = version jobrec['provenance'] = prov return jobrec
def test_atomic_input_to_job_input_cisco_casci_similarity(ethylene): """ Test that the new atomic_input_to_job_input function produces the same protobuf messages that Stefan's old method created """ # Dicts of options used according to Stefan's old methodology old_methodoloy_options = { "method": "hf", "basis": "6-31g**", "atoms": ethylene["atoms"], } keywords = { # base options "charge": 0, "spinmult": 1, "closed_shell": True, "restricted": True, "precision": "double", "convthre": 1e-8, "threall": 1e-20, # cisno options "cisno": "yes", "cisnostates": 2, "cisnumstates": 2, "closed": 7, "active": 2, "cassinglets": 2, "dcimaxiter": 100, } # Construct Geometry in bohr geom_angstrom = qcel.Datum("geometry", "angstrom", np.array(ethylene["geometry"])) geom_bohr = _round(geom_angstrom.to_units("bohr")) # Construct Molecule object m_ethylene = Molecule.from_data({ "symbols": ethylene["atoms"], "geometry": geom_bohr, "molecular_multiplicity": keywords["spinmult"], "molecular_charge": keywords["charge"], }) # Construct AtomicInput atomic_input = AtomicInput( molecule=m_ethylene, driver="energy", model={ "method": "hf", "basis": "6-31g**" }, keywords=keywords, ) # Create protobof JobInput using Stefan's old approach client = TCProtobufClient("host", 11111) stefan_style = client._create_job_input_msg( "energy", geom_bohr, "bohr", **{ **old_methodoloy_options, **keywords }) # Create protobuf JobInput using AtomicInput object job_input = atomic_input_to_job_input(atomic_input) assert job_input == stefan_style