<jupyter_start><jupyter_text>Set logging level<jupyter_code>logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logging.getLogger("ppqm").setLevel(logging.INFO)
logging.getLogger("xtb").setLevel(logging.DEBUG)
SHOW_PROGRESS = False
_logger = logging.getLogger(__name__)<jupyter_output><empty_output><jupyter_text>Define xtb options and functionsTODO Add some documentation<jupyter_code>def optimize_molobj(
    molobj,
    show_progress=True,
    n_cores=1,
    scr=None,
    calculation_options={},
    xtb_options={},
    rmsd_threshold=3.0,
):
    """
    Optimize all the conformers in molobj.

    :param rmsd_threshold: Check if molcule moved away from local minima.
    """
    n_atoms = molobj.GetNumAtoms()
    molobj_prime = chembridge.copy_molobj(molobj)
    energies = []

    atoms, _, charge = chembridge.get_axyzc(molobj, atomfmt=str)

    n_conformers = molobj.GetNumConformers()
    coordinates_list = [
        np.asarray(conformer.GetPositions()) for conformer in molobj.GetConformers()
    ]

    n_procs = min(n_cores, n_conformers)
    results = []

    if "n_cores" in xtb_options:
        del xtb_options["n_cores"]

    if scr:
        xtb_options["scr"] = scr

    func = functools.partial(
        optimize_acxyz,
        atoms,
        charge,
        calculation_options=calculation_options,
        xtb_options=xtb_options,
    )

    results = ppqm.misc.func_parallel(
        func,
        coordinates_list,
        n_cores=n_procs,
        show_progress=show_progress,
        title="Optimize",
    )

    for idx, (coord, energy) in enumerate(results):

        # if conformer is unconverged, ignore
        if coord is None or energy is None:
            _logger.warning(f"conformer {idx} unconverged")
            continue

        # if conformer has changed a lot, warn
        original = chembridge.get_coordinates(molobj, confid=idx)
        displacement = rmsd.kabsch_rmsd(coord, original)
        if displacement > rmsd_threshold:
            _logger.warning(f"conformer {idx} has large displacement")

        # Molecule is converged, add to new molobj
        conformer = Chem.Conformer(n_atoms)
        chembridge.conformer_set_coordinates(conformer, coord)
        molobj_prime.AddConformer(conformer, assignId=True)
        energies.append(energy)

    energies = np.asarray(energies)

    return molobj_prime, energies
def optimize_acxyz(atoms, charge, coordinates, **kwargs):
    """ Meta function for parallel func mapping """
    return optimize_axyzc(atoms, coordinates, charge, **kwargs)
def optimize_axyzc(atoms, coordinates, charge, calculation_options={}, xtb_options={}) -> np.array:
    """
    Optimize conformer with fast quantum chemistry

    - optimize with XTB2
    - if not_converged
      restart with XTB0 with n_steps
      optimize with XTB2
    - if not_converged
      optimize with cartisian coordinates
    - if not_converged
      return None

    :return coordiantes: Nx3 Array of coordiantes
    """

    assert isinstance(atoms[0], str), "func requires atoms in string format"

    options_default = {
        "opt": None,
        "cycles": 100,
    }
    options_default = dict(collections.ChainMap(options_default, calculation_options))

    options_fast = {
        "gfn": 0,
        "opt": None,
        "cycles": 200,
    }
    options_fast = dict(collections.ChainMap(options_fast, calculation_options))

    options_lax = {
        "opt": "lax",
    }
    options_fast = dict(collections.ChainMap(options_lax, calculation_options))

    # Start calculating with the defined options

    properties = xtb.get_properties_from_axyzc(
        atoms, coordinates, charge, options=options_default, **xtb_options
    )

    if properties and properties[xtb.COLUMN_CONVERGED]:
        return properties[xtb.COLUMN_COORD], properties[xtb.COLUMN_ENERGY]

    properties = xtb.get_properties_from_axyzc(
        atoms, coordinates, charge, options=options_fast, **xtb_options
    )

    if not properties or xtb.COLUMN_COORD not in properties:
        return None, None

    fast_coordinates = properties[xtb.COLUMN_COORD]

    properties = xtb.get_properties_from_axyzc(
        atoms, fast_coordinates, charge, options_default, **xtb_options
    )

    if properties and properties[xtb.COLUMN_CONVERGED]:
        return properties[xtb.COLUMN_COORD], properties[xtb.COLUMN_ENERGY]

    if properties[xtb.COLUMN_COORD] is None:
        return None, None

    fast_coordinates = properties[xtb.COLUMN_COORD]

    properties = xtb.get_properties_from_axyzc(
        atoms, fast_coordinates, charge, options=options_lax, **xtb_options
    )

    if not properties or properties[xtb.COLUMN_COORD] is None:
        return None, None

    return properties[xtb.COLUMN_COORD], properties[xtb.COLUMN_ENERGY]<jupyter_output><empty_output><jupyter_text>Define a molecule you like<jupyter_code>smiles = "O=C(OCCN1CCN(C(c2ccc(F)cc2)c2ccc(F)cc2)CC1)C12C[C@H]3C[C@@H](C1)C[C@@H](C2)C3"  # CHEMBL1191174
molobj = Chem.MolFromSmiles(smiles)
molobj
# With 3D cooordinates
molobj = ppqm.tasks.generate_conformers(molobj)<jupyter_output><empty_output><jupyter_text>Let's optimize a molobj<jupyter_code>calculation_options = {
    "gfn": 2,
    "alpb": "water",
}
xtb_options = {"cmd": "xtb"}
kwargs = {
    "n_cores": 2,
    "scr": "_tmp_directory_",
    "xtb_options": xtb_options,
    "calculation_options": calculation_options,
    "show_progress": SHOW_PROGRESS,
}
molobj_prime = optimize_molobj(molobj, **kwargs)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09787543126929363, 'fraction_numerical': 0.006718721627020156, 'mean_word_length': 3.6053511705685617, 'pattern_counts': {'":': 14, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '167172', 'license': 'MIT', 'n_tokens_mistral': 1923, 'n_tokens_neox': 1837, 'n_words': 361, 'path': 'notebooks/example_xtb_optimization_pipeline.ipynb', 'repo_name': 'koerstz/ppqm', 'size': '5507'}