Source code for plbenchmark.utils

"""
utils.py
Contains utility functions
"""

import numpy as np
from scipy import constants
import requests
import json
from openff.units import unit

import warnings


[docs]def find_pdb_url(pdb): """ Finds the links to a pdb or a list of pdb codes. :param pdb: string or list of strings :return: string compiled string including the urls to the pdb entries """ if pdb is None: return "" if type(pdb) == str: pdb = [pdb] result = [] for p in pdb: url = f"https://data.rcsb.org/rest/v1/core/entry/{p}" try: response = requests.get(url) if response.status_code == requests.codes.ok: page = response.text result.append(f"REP1http://www.rcsb.org/structure/{p}REP2{p}REP3") else: warnings.warn(f"Could not find PDB {p}") result.append(p) except requests.exceptions.RequestException as e: warnings.warn(f"Could not find PDB {p}\n{e}") result.append(p) return ("\n").join(result)
[docs]def find_doi_url(doi): """ Finds the links to a digital object identifier (doi). :param doi: string :return: string compiled string including the urls to the publication """ url = "https://api.crossref.org/works/" + str(doi) try: response = requests.get(url) except requests.exceptions.RequestException as e: warnings.warn(f"Could not find DOI: {doi}\n{e}") if response.status_code == requests.codes.ok: obj = response.json() obj = obj["message"] aut = obj["author"] if len(aut) > 0: aut = obj["author"][0]["family"] else: aut = "" tit = obj["short-container-title"] if len(tit) > 0: tit = tit[0] else: tit = "" if "published-print" in obj.keys(): dat = obj["published-print"]["date-parts"][0][0] else: dat = "XXXX" desc_string = "{} et al., {} {}".format( aut, tit, dat ) # , obj['journal-issue']['published-online']['date-parts'][0][0]) return f'REP1{obj["URL"]}REP2{desc_string}REP3' else: warnings.warn(f"Could not find DOI: {doi}") return doi
[docs]def convert_value(value, original_type, final_type, temperature=300.0, out_unit=None): """ Converts an experimental value into another derived quantity with specified unit. :param value: float, numerical value :param original_type: string, code for the original observable. Can be `dg`, `ki`, `ic50`, `pic50` :param final_type: string, code for the desired derived quantity. Can be `dg`, `ki`, `ic50`, `pic50` :param temperature: float, temperature in kelvin :param out_unit: unit of type :py:class:`pint`, output unit of final_type, needs to fit to the requested final_type :return: :py:class:`pint.Quantity` with desired unit """ # define default units if out_unit is None: if final_type == "dg": out_unit = unit("kilocalories / mole") elif final_type == "ki": out_unit = unit("nanomolar") elif final_type == "ic50": out_unit = unit("nanomolar") elif final_type == "pic50": out_unit = unit("") if original_type == "dg": if final_type == "dg": return value.to(out_unit) elif final_type == "ki": result = ( np.exp( -value / (unit.molar_gas_constant * temperature * unit.kelvin) ) * unit.molar ) return result.to(out_unit) elif final_type == "ic50": result = ( np.exp( -value / (unit.molar_gas_constant * temperature * unit.kelvin) ) * unit.molar ) return result.to(out_unit) elif final_type == "pic50": result = ( value / (unit.molar_gas_constant * temperature * unit.kelvin) / np.log(10) ) return result.to(out_unit) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "ki": if final_type == "dg": if value < 1e-15 * unit("molar"): return 0.0 * out_unit else: result = ( unit.molar_gas_constant * temperature * unit.kelvin * np.log(value / unit.molar) ) return result.to(out_unit).round(2) elif final_type == "ki": return value.to(out_unit) elif final_type == "ic50": return value.to(out_unit) elif final_type == "pic50": if value < 1e-15 * unit("molar"): return -1e15 * out_unit else: result = -np.log(value / unit.molar) / np.log(10) return result else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "ic50": if final_type == "dg": if value < 1e-15 * unit("molar"): return 0.0 * out_unit else: result = ( unit.molar_gas_constant * temperature * unit.kelvin * np.log(value.to("molar") / unit.molar) ) return result.to(out_unit).round(2) elif final_type == "ki": return value.to(out_unit) elif final_type == "ic50": return value.to(out_unit) elif final_type == "pic50": if value.to("molar") < 1e-15 * unit("molar"): return -1e15 * out_unit else: result = -np.log(value / unit.molar) / np.log(10) return result else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "pic50": if final_type == "dg": result = ( -1 * unit.molar_gas_constant * temperature * unit.kelvin * value * np.log(10) ) return result.to(out_unit).round(2) elif final_type == "ki": result = 10 ** (-value) * unit("molar") return result.to(out_unit) elif final_type == "ic50": result = 10 ** (-value) * unit("molar") return result.to(out_unit) elif final_type == "pic50": return value.to(out_unit) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." )
[docs]def convert_error( error_value, value, original_type, final_type, temperature=300.0, out_unit=None ): """ Converts an experimental value into another derived quantity with specified unit. :param error_value: float, error of val, numerical value :param value: float, numerical value :param original_type: string, code for the original observable. Can be `dg`, `ki`, `ic50`, `pic50` :param final_type: string, code for the desired derived quantity. Can be `dg`, `ki`, `ic50`, `pic50` :param temperature: float, temperature in kelvin :param out_unit: unit of type :py:class:`pint`, output unit of final_type, needs to fit to the requested final_type :return: :py:class:`pint.Quantity` with desired unit """ # define default units if out_unit is None: if final_type == "dg": out_unit = unit("kilocalories / mole") elif final_type == "ki": out_unit = unit("nanomolar") elif final_type == "ic50": out_unit = unit("nanomolar") elif final_type == "pic50": out_unit = unit("") if original_type == "dg": if final_type == "dg": return error_value.to(out_unit) elif final_type == "ki": # e_ki^2 = (del K/del dG)^2 * e_dG^2 # e_ki = 1/RT * exp(-dG/RT) * e_dG k_bt = unit.molar_gas_constant * temperature * unit.kelvin error = ( 1.0 / k_bt * np.exp(-value / k_bt) * error_value * unit.molar ) return error.to(out_unit) elif final_type == "ic50": k_bt = unit.molar_gas_constant * temperature * unit.kelvin error = ( 1.0 / k_bt * np.exp(-value / k_bt) * error_value * unit.molar ) return error.to(out_unit) elif final_type == "pic50": # e_pic50^2 = (del pic50/del dG)^2 * e_dG^2 # e_pic50 = 1/(RT*ln(10)) * e_dG k_bt = unit.molar_gas_constant * temperature * unit.kelvin error = 1.0 / (k_bt * np.log(10)) * error_value return error.to(out_unit) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "ki": if final_type == "dg": if value < 1e-15 * unit.molar: return 0.0 * out_unit else: error = ( unit.molar_gas_constant * temperature * unit.kelvin / value * error_value ) return error.to(out_unit).round(2) elif final_type == "ki": return error_value.to(out_unit) elif final_type == "ic50": return error_value.to(out_unit) elif final_type == "pic50": # e_pic50^2 = (del pic50/del Ki)^2 * e_Ki^2 # e_pic50 = 1/(Ki*ln(10)) * e_Ki if (value * np.log(10)) < 1e-15 * unit("molar"): return 1e15 * out_unit else: result = 1 / (value * np.log(10)) * error_value return result.to(out_unit).round(2) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "ic50": if final_type == "dg": if value < 1e-15 * unit.molar: return 0.0 * out_unit else: error = ( unit.molar_gas_constant * temperature * unit.kelvin / value * error_value ) return error.to(out_unit).round(2) elif final_type == "ki": return error_value.to(out_unit) elif final_type == "ic50": return error_value.to(out_unit) elif final_type == "pic50": # e_pic50^2 = (del pic50/del IC50)^2 * e_IC50^2 # e_pic50 = 1/(IC50*ln(10)) * e_IC50 if (value * np.log(10)) < 1e-15 * unit("molar"): return 1e15 * out_unit else: result = 1 / (value * np.log(10)) * error_value return result.to(out_unit).round(2) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." ) elif original_type == "pic50": if final_type == "dg": error = ( unit.molar_gas_constant * temperature * unit.kelvin * np.log(10) * error_value ) return error.to(out_unit).round(2) elif final_type == "ki": # Ki = 10^(-pIC50) # dKi^2 = (del Ki / del pIC50)^2 * dpIC50^2 # dKi = ln(10) * 10^(-pIC50) * dpIC50 error = np.log(10) * 10 ** (-value) * error_value * unit("molar") return error.to(out_unit).round(2) elif final_type == "ic50": # IC50 = 10^(-pIC50) # dIC50^2 = (del IC50 / del pIC50)^2 * dpIC50^2 # dIC50 = ln(10) * 10^(-pIC50) * dpIC50 error = np.log(10) * 10 ** (-value) * error_value * unit("molar") return error.to(out_unit).round(2) elif final_type == "pic50": return error_value.to(out_unit).round(2) else: raise NotImplementedError( f"Conversion to observable {final_type} not possible. " f"Observable must be any of: dg, ki, ic50 or pic50." )