Source code for qm_atlas.wrappers.cosmotherm.cosmo_cocrystal

import math
from functools import partial
from pathlib import Path

import pandas as pd
from more_itertools import batched
from ppqm.utils import functools as ppqm_functools

from qm_atlas.wrappers.cosmotherm import cosmo_utils, interface

COCRYSTAL_SETTINGS = cosmo_utils.CosmoSettings(
    action_lines=None,  # overwritten in code
    command_lines=["unit notempty ehfile wtln"],
    comment_line="!! cocrystal screening !!",
)


[docs] def screen_cocrystals( compound_conformers: list[str | Path], coformers: list[str], level: str = "bp-tzvpd", options: cosmo_utils.CosmoSettings = COCRYSTAL_SETTINGS, coformers_dirs: list[Path] | None = None, temperature_Celsius: float = 25.0, stoichiometry: tuple[int, int] = (1, 1), f_fit: bool | tuple[float, float, float] = False, f_fit_cof: bool | tuple[float, float] = True, f_fit_sol: bool | tuple[float, float, float] = False, pZWI: bool = False, n_cores: int = 1, use_config_cosmo_dir: bool = True, **kwargs, ) -> pd.DataFrame: """ This function performs a cocrystal screening using Cosmotherm, calculating values such as the excess enthalpy of the mixture. The method and example applications are described in reference [1]. Args: compound_conformers (list[str | Path]): List of compound conformers, either specifying the path to the file or providing the contents as a string. In the latter case, the file must be a .cosmo file, otherwise it can be any file that cosmotherm accepts. coformers (list[str]): List of coformers, specified by their names. Corresponding .cosmo files must be found in the database, either the standard cosmotherm one or an additional, user-provided one. level (str, optional): Level of theory. Defaults to "bp-tzvpd". options (cosmo_utils.CosmoSettings, optional): Settings for the Cosmo task. Defaults to :data:`COCRYSTAL_SETTINGS`. coformers_dirs (list[Path] | None, optional): Additional directories to find the .cosmo files for the coformers. Defaults to None, i.e. just relying on the cosmotherm database. If additional directories are considered, the cosmotherm database is searched first, the the coformers_dirs. temperature_Celsius (float, optional): Temperature for the calculation in °C. Defaults to 25. stoichiometry (tuple[int, int], optional): Stoichiometry of the cocrystal. Will be used for all cocrystals in the screening. Defaults to (1, 1). f_fit (bool | tuple[float, float, float], optional): Add the key 'f_fit' to the cosmotherm action line. This will lead to additionally calculating a fitted function for a heuristic expression. Set to True to use the default cosmotherm parameters, provide a tuple to set the parameters. Defaults to False. f_fit_cof (bool | tuple[float, float], optional): Add the key 'f_fit_cof' to the cosmotherm action line. This will lead to additionally calculating a fitted function for a heuristic expression. Set to True to use the default cosmotherm parameters, provide a tuple to set the parameters. Defaults to True. f_fit_sol (bool | tuple[float, float, float], optional): Add the key 'f_fit_sol' to the cosmotherm action line. This will lead to additionally calculating a fitted function for a heuristic expression. Set to True to use the default cosmotherm parameters, provide a tuple to set the parameters. Defaults to False. pZWI (bool, optional): Add the key 'pZWI' to the cosmotherm action line. This is suggested if either the API or the coformer is zwitterionic (punish zwittterions). Defaults to False. n_cores (int, optional): Number of cores to use for the calculation. Defaults to 1. use_config_cosmo_dir (bool, optional): Whether to prepend the solvent directories from the software configuration file to the user-provided coformers_dirs. If False, only the user-provided directories are used. Defaults to True. **kwargs: Additional keyword arguments for the output reader. Returns: pd.DataFrame: A DataFrame containing the results of the cocrystal screening. References: [1] Loschen, C. & Klamt, A. Solubility prediction, solvate and cocrystal screening as tools for rational crystal engineering. *J. Pharm. Pharmacol.* **67**, 803-811 (2015). https://doi.org/10.1111/jphp.12376 """ n_procs = min(n_cores, len(coformers)) # split coformers in chunks batch_size = math.ceil(len(coformers) / n_procs) coformer_batches = list(batched(coformers, batch_size)) calculator_function = partial( _screen_cocrystals, compound_conformers=compound_conformers, level=level, options=options, coformers_dirs=coformers_dirs, temperature_Celsius=temperature_Celsius, stoichiometry=stoichiometry, f_fit=f_fit, f_fit_cof=f_fit_cof, f_fit_sol=f_fit_sol, pZWI=pZWI, use_config_cosmo_dir=use_config_cosmo_dir, **kwargs, ) if len(coformer_batches) == 1: dataframes = [calculator_function(coformer_batches[0])] else: dataframes = ppqm_functools.func_parallel( calculator_function, coformer_batches, n_cores=n_procs, title="Cosmo_Cocrystal", ) return pd.concat(dataframes, ignore_index=True)
def _screen_cocrystals( coformers: list[str], compound_conformers: list[str | Path], level: str = "bp-tzvpd", options: cosmo_utils.CosmoSettings = COCRYSTAL_SETTINGS, coformers_dirs: list[Path] | None = None, temperature_Celsius: float = 25.0, stoichiometry: tuple[int, int] = (1, 1), f_fit: bool | tuple = False, f_fit_cof: bool | tuple = False, f_fit_sol: bool | tuple = False, pZWI: bool = False, use_config_cosmo_dir: bool = True, **kwargs, ) -> pd.DataFrame: cosmo_utils.check_input_conformers(compound_conformers) cosmo_paths = interface.get_cosmo_paths(level=level) coformers_databases = cosmo_utils.get_databases( cosmo_paths, level=level, other_databases=coformers_dirs, use_config_cosmo_dir=use_config_cosmo_dir, ) coformer_files = [ cosmo_utils.find_cosmo_files(coformers_databases, name) for name in coformers ] conformers = [compound_conformers] + coformer_files if options.action_lines is None: action_line = f"tc={temperature_Celsius} cocrystal API=1 coformer=ALL" action_line += f" cocrystal_n={{{stoichiometry[0]} {stoichiometry[0]}}}" added_fits = ["f_fit", "f_fit_cof", "f_fit_sol", "pZWI"] commands = [f_fit, f_fit_cof, f_fit_sol, pZWI] for fit, command in zip(added_fits, commands): if isinstance(command, tuple): action_line += f" {fit}" + "={" + " ".join(command) + "}" elif command: action_line += f" {fit}" action_lines = [action_line] else: action_lines = options.action_lines command_lines = options.get_command_lines(cosmo_paths) comment_line = options.comment_line res_df_list = interface.calculate_cosmotherm( conformers, action_lines, command_lines=command_lines, comment_line=comment_line, table_start_patterns=["Nr Coformer"], **kwargs, ) return res_df_list[0]