Esempio n. 1
0
    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})
Esempio n. 2
0
    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})
Esempio n. 3
0
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
Esempio n. 4
0
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)
Esempio n. 5
0
def test_creation_error():
    with pytest.raises(pydantic.ValidationError):
        qcel.Datum('ze lbl', 'ze unit', 'ze data')
Esempio n. 6
0
    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})
Esempio n. 7
0
def test_creation_error():
    with pytest.raises(pydantic.ValidationError):
        qcel.Datum("ze lbl", "ze unit", "ze data")
Esempio n. 8
0
    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})
Esempio n. 9
0
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
Esempio n. 10
0
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