示例#1
0
def run_simulation(smiles: str, n_nodes: int, spec_name: str = 'small_basis', solvent: Optional[str] = None)\
        -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    """Run the ionization potential computation

    Args:
        smiles: SMILES string to evaluate
        n_nodes: Number of nodes to use
        spec_name: Name of the quantum chemistry specification
        solvent: Name of the solvent to use
    Returns:
        Relax records for the neutral and ionized geometry
    """
    from moldesign.simulate.functions import generate_inchi_and_xyz, relax_structure, run_single_point
    from moldesign.simulate.specs import get_qcinput_specification
    from moldesign.utils.chemistry import get_baseline_charge

    # Make the initial geometry
    inchi, xyz = generate_inchi_and_xyz(smiles)
    neu_charge = get_baseline_charge(smiles)
    chg_charge = neu_charge + 1

    # Make the compute spec
    compute_config = {'nnodes': n_nodes, 'cores_per_rank': 2, 'ncores': 64}

    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification(spec_name)
    if code == "nwchem":
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

    # Compute the neutral geometry and hessian
    neutral_xyz, _, neutral_relax = relax_structure(xyz, spec, compute_config=compute_config, charge=neu_charge, code=code)

    # Compute the relaxed geometry
    oxidized_xyz, _, oxidized_relax = relax_structure(neutral_xyz, spec, compute_config=compute_config, charge=chg_charge, code=code)

    # If desired, compute the solvent energies
    if solvent is None:
        return [neutral_relax, oxidized_relax], []

    spec, code = get_qcinput_specification(spec_name, solvent=solvent)
    if code == "nwchem":
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

    neutral_solvent = run_single_point(neutral_xyz, 'energy', spec, compute_config=compute_config, charge=neu_charge, code=code)
    charged_solvent = run_single_point(oxidized_xyz, 'energy', spec, compute_config=compute_config, charge=chg_charge, code=code)

    return [neutral_relax, oxidized_relax], [neutral_solvent, charged_solvent]
示例#2
0
def _run_simulation(smiles: str, solvent: Optional[str], spec_name: str = 'xtb')\
        -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    """Run the ionization potential computation

    Args:
        smiles: SMILES string to evaluate
        solvent: Name of the solvent
        spec: Quantum chemistry specification for the molecule
    Returns:
        Relax records for the neutral and ionized geometry
    """
    from moldesign.simulate.functions import generate_inchi_and_xyz, relax_structure, run_single_point
    from moldesign.simulate.specs import get_qcinput_specification
    from moldesign.utils.chemistry import get_baseline_charge
    from qcelemental.models import DriverEnum

    # Make the initial geometry
    inchi, xyz = generate_inchi_and_xyz(smiles)
    init_charge = get_baseline_charge(smiles)

    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification(spec_name)

    # Compute the geometries
    neutral_xyz, _, neutral_relax = relax_structure(xyz,
                                                    spec,
                                                    charge=init_charge,
                                                    code=code)
    oxidized_xyz, _, oxidized_relax = relax_structure(neutral_xyz,
                                                      spec,
                                                      charge=init_charge + 1,
                                                      code=code)

    # Perform the solvation energy computations, if desired
    if solvent is None:
        return [neutral_relax, oxidized_relax], []

    solv_spec, code = get_qcinput_specification(spec_name, solvent=solvent)
    neutral_solv = run_single_point(neutral_xyz,
                                    DriverEnum.energy,
                                    solv_spec,
                                    charge=init_charge,
                                    code=code)
    oxidized_solv = run_single_point(oxidized_xyz,
                                     DriverEnum.energy,
                                     solv_spec,
                                     charge=init_charge + 1,
                                     code=code)
    return [neutral_relax, oxidized_relax], [neutral_solv, oxidized_solv]
示例#3
0
def run_simulation(
        smiles: str,
        n_nodes: int) -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    """Run the ionization potential computation

    Args:
        smiles: SMILES string to evaluate
        n_nodes: Number of nodes to use
    Returns:
        Relax records for the neutral and ionized geometry
    """
    from moldesign.simulate.functions import generate_inchi_and_xyz, relax_structure, run_single_point
    from moldesign.simulate.specs import get_qcinput_specification
    from qcelemental.models import DriverEnum

    # Make the initial geometry
    inchi, xyz = generate_inchi_and_xyz(smiles)

    # Make the compute spec
    compute_config = {'nnodes': n_nodes, 'cores_per_rank': 2}

    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification('small_basis')
    if code == "nwchem":
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

    # Compute the neutral geometry and hessian
    neutral_xyz, _, neutral_relax = relax_structure(
        xyz, spec, compute_config=compute_config, charge=0, code=code)
    neutral_hessian = run_single_point(neutral_xyz,
                                       DriverEnum.hessian,
                                       spec,
                                       charge=0,
                                       compute_config=compute_config,
                                       code=code)

    # Compute the relaxed geometry
    oxidized_xyz, _, oxidized_relax = relax_structure(
        neutral_xyz, spec, compute_config=compute_config, charge=1, code=code)
    oxidized_hessian = run_single_point(oxidized_xyz,
                                        DriverEnum.hessian,
                                        spec,
                                        charge=1,
                                        compute_config=compute_config,
                                        code=code)
    return [neutral_relax, oxidized_relax], [neutral_hessian, oxidized_hessian]
示例#4
0
def compute_single_point(xyz: str, charge: int, solvent: Optional[str] = None,
                         spec_name: str = 'normal_basis', n_nodes: int = 2) \
        -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    """Perform a single point energy computation, return results in same format as other assays

    Args:
        xyz: Molecular geometry
        charge: Molecular charge
        solvent: Name of the solvent, if desired
        spec_name: Name of the QC specification
        n_nodes: Number of nodes for the computation
    Returns:
        - Not used
        - Single point energy computation
    """

    # Get the specification and make it more resilient
    compute_config = {'nnodes': n_nodes, 'cores_per_rank': 2}
    if spec_name == 'diffuse_basis':
        compute_config['cores_per_rank'] = 8
    spec, code = get_qcinput_specification(spec_name, solvent)
    if code == "nwchem":
        # Reduce the accuracy needed to 1e-7
        spec.keywords['dft__convergence__energy'] = 1e-7
        spec.keywords['dft__convergence__fast'] = True
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

        # Make sure to allow restarting
        spec.extras["allow_restarts"] = True
        runhash = hashlib.sha256(
            f'{xyz}_{charge}_{spec_name}_{solvent}'.encode()).hexdigest()[:12]
        spec.extras["scratch_name"] = f'nwc_{runhash}'

    # Run the computation
    spe_record = run_single_point(xyz,
                                  DriverEnum.energy,
                                  spec,
                                  charge=charge,
                                  code=code,
                                  compute_config=compute_config)

    return [], [spe_record]
示例#5
0
def run_simulation(smiles: str, n_nodes: int,
                   mode: str) -> Union[OptimizationResult, AtomicResult]:
    """Run a single-point or relaxation computation computation

    Args:
        smiles: SMILES string to evaluate
        n_nodes: Number of nodes to use
        mode: What computation to perform: single, gradient, hessian, relax
    Returns:
        Result of the energy computation
    """
    from moldesign.simulate.functions import generate_inchi_and_xyz, relax_structure, run_single_point
    from moldesign.simulate.specs import get_qcinput_specification
    from qcelemental.models import DriverEnum

    # Make the initial geometry
    inchi, xyz = generate_inchi_and_xyz(smiles)

    # Make the compute spec
    compute_config = {'nnodes': n_nodes, 'cores_per_rank': 2}

    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification('small_basis')
    if code == "nwchem":
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

    # Compute the neutral geometry and hessian
    if mode == 'relax':
        _, _, neutral_relax = relax_structure(xyz,
                                              spec,
                                              compute_config=compute_config,
                                              charge=0,
                                              code=code)
        return neutral_relax
    else:
        return run_single_point(xyz,
                                mode,
                                spec,
                                charge=0,
                                compute_config=compute_config,
                                code=code)
示例#6
0
def _compute_adiabatic(xyz: str, init_charge: int, solvent: Optional[str], spec_name: str = 'xtb') \
        -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification(spec_name)

    # Compute the geometries
    oxid_xyz, _, oxidized_relaxed = relax_structure(xyz,
                                                    spec,
                                                    charge=init_charge + 1,
                                                    code=code)

    # Perform the solvation energy computations, if desired
    if solvent is None:
        return [oxidized_relaxed], []

    solv_spec, code = get_qcinput_specification(spec_name, solvent=solvent)
    oxidized_solv = run_single_point(oxid_xyz,
                                     DriverEnum.energy,
                                     solv_spec,
                                     charge=init_charge + 1,
                                     code=code)
    return [oxidized_relaxed], [oxidized_solv]
示例#7
0
def compute_vertical(smiles: str, oxidize: bool, solvent: Optional[str] = None,
                     spec_name: str = 'small_basis', n_nodes: int = 2) \
        -> Tuple[List[OptimizationResult], List[AtomicResult]]:
    """Perform the initial ionization potential computation of the vertical

    First relaxes the structure and then runs a single-point energy at the

    Args:
        smiles: SMILES string to evaluate
        oxidize: Whether to perform an oxidation or reduction
        solvent: Name of the solvent, if desired. Runs on the neutral geometry after relaxation
        spec_name: Quantum chemistry specification for the molecule
        n_nodes: Number of nodes per computation
    Returns:
        - Relax records for the neutral
        - Single point energy in oxidized or reduced state
    """

    # Make the initial geometry
    inchi, xyz = generate_inchi_and_xyz(smiles)
    init_charge = get_baseline_charge(smiles)

    # Make the compute spec
    compute_config = {'nnodes': n_nodes, 'cores_per_rank': 2}

    # Get the specification and make it more resilient
    spec, code = get_qcinput_specification(spec_name)
    if code == "nwchem":
        spec.keywords['dft__convergence__energy'] = 1e-7
        spec.keywords['dft__convergence__fast'] = True
        spec.keywords["dft__iterations"] = 150
        spec.keywords["driver__maxiter"] = 150
        spec.keywords["geometry__noautoz"] = True

        # Make a repeatably-named scratch directory.
        #  We cannot base it off a hash of the input file,
        #  because the XYZ file generator is stochastic.
        runhash = hashlib.sha256(
            f'{smiles}_{oxidize}_{spec_name}'.encode()).hexdigest()[:12]
        spec.extras["scratch_name"] = f'nwc_{runhash}'
        spec.extras["allow_restarts"] = True

    # Compute the geometries
    neutral_xyz, _, neutral_relax = relax_structure(
        xyz,
        spec,
        charge=init_charge,
        code=code,
        compute_config=compute_config)

    # Perform the single-point energy for the ionized geometry
    new_charge = init_charge + 1 if oxidize else init_charge - 1
    oxid_spe = run_single_point(neutral_xyz,
                                DriverEnum.energy,
                                spec,
                                charge=new_charge,
                                code=code,
                                compute_config=compute_config)

    # If desired, submit a solvent computation as well
    if solvent is None:
        return [neutral_relax], [oxid_spe]

    spec, code = get_qcinput_specification(spec_name, solvent)
    if code == "nwchem":
        # Reduce the accuracy needed to 1e-7
        spec.keywords['dft__convergence__energy'] = 1e-7
        spec.keywords['dft__convergence__fast'] = True
        spec.keywords["dft__iterations"] = 150
        spec.keywords["geometry__noautoz"] = True

        # Make sure to allow restarting
        spec.extras["allow_restarts"] = True
    solv_spe = run_single_point(neutral_xyz,
                                DriverEnum.energy,
                                spec,
                                charge=init_charge,
                                code=code,
                                compute_config=compute_config)
    return [neutral_relax], [oxid_spe, solv_spe]
from moldesign.simulate.specs import get_qcinput_specification

if __name__ == "__main__":
    # Make a water molecule
    inchi, xyz = generate_inchi_and_xyz('O')

    # Generate the neutral geometry with XTB
    xtb_spec, xtb = get_qcinput_specification("xtb")
    xtb_neutral_xyz, _, record = relax_structure(xyz, xtb_spec, code=xtb)
    with open('records/xtb-neutral.json', 'w') as fp:
        print(record.json(), file=fp)

    # Compute the vertical oxidation potential
    record = run_single_point(xtb_neutral_xyz,
                              "energy",
                              xtb_spec,
                              code=xtb,
                              charge=1)
    with open('records/xtb-neutral_xtb-oxidized-energy.json', 'w') as fp:
        print(record.json(), file=fp)

    # Compute the adiabatic oxidation potential
    xtb_oxidized_xyz, _, record = relax_structure(xtb_neutral_xyz,
                                                  xtb_spec,
                                                  code=xtb,
                                                  charge=1)
    with open('records/xtb-oxidized.json', 'w') as fp:
        print(record.json(), file=fp)

    # Compute the solvation energy for the neutral and the oxidized
    xtb_spec, xtb = get_qcinput_specification('xtb', 'acetonitrile')