Skip to content

deeporigin.drug_discovery.secondary_pharma

SecondaryPharmacology drives platform tool deeporigin.secondary-pharma. It scores ligands against a baked off-target panel, using either a served ligand-ML path (method="ligand-ml", use run()) or an async docking workflow (method="docking", use start()). See the tool guide for the full explanation of why these are separate execution modes on one class.

SecondaryPharmacology -- score ligands against a baked off-target panel.

Backed by the platform tool deeporigin.secondary-pharma, which offers two mutually exclusive scoring methods, chosen at construction:

  • "ligand-ml" -- served, synchronous XGBoost booster scoring. Use :meth:run.
  • "docking" -- asynchronous Argo workflow. Use :meth:start (and :meth:watch in Jupyter).

The tool schema has no inputs.sync field (unlike deeporigin.docking), so method alone decides the execution mode. :meth:get_results returns a :class:pandas.DataFrame either way, but reads from different places: ligand-ml results come back in the run's jobOutputs, while docking results are only in result-explorer (jobOutputs is empty for an async run). For the docked poses themselves, use :meth:get_poses; each pose carries the path of its verified panel receptor in pose.props["receptor_local_path"] when the run recorded one. :meth:show_panel_pose shows one in its pocket.

Usage::

from deeporigin.drug_discovery import SecondaryPharmacology, Ligand

ligand = Ligand.from_smiles("CCO")

ml = SecondaryPharmacology(ligands=[ligand], method="ligand-ml")
df = ml.run()

dock = SecondaryPharmacology(ligands=[ligand], method="docking", effort=2)
dock.start()
dock.wait()
df = dock.get_results()  # pose_score, binding_energy, etc.

Attributes

Classes

SecondaryPharmacology

Bases: Execution, SyncExecutableMixin, AsyncExecutableMixin, NotebookWatchMixin

Score ligands against a baked secondary-pharmacology panel.

Attributes:

Name Type Description
ligands list[Ligand]

Ligands to score. Empty only when :attr:self_test is True.

method str

Scoring path for this instance -- "docking" (async workflow, use :meth:start) or "ligand-ml" (served, use :meth:run).

uniprots list[str] | tuple[str, ...] | None

Panel accessions to restrict scoring to, or None for the whole panel. Validated against the live tool definition's enum.

effort int

Docking effort level (1 = fastest, 5 = most thorough). Ignored on the ligand-ml path.

batch_size int

Panel cells (ligand x target) packed per docking leaf -- a soft cap; a single target's cells are never split across leaves. Ignored on the ligand-ml path.

self_test bool

When True, runs a served ligand-ml health-check against the full panel with a baked test ligand and ignores :attr:ligands. Not supported with method="docking".

Attributes

USER_LOG_COLUMNS class-attribute
USER_LOG_COLUMNS: list[str] = [
    "log_level",
    "tool_key",
    "timestamp",
    "message",
]
app instance-attribute
app: str | None = None
approve_amount instance-attribute
approve_amount: int | None = None
batch_size property
batch_size: int

Panel cells packed per docking leaf (default 30). Read-only.

Ignored on the ligand-ml path.

client instance-attribute
client: DeepOriginClient = client
completed_at instance-attribute
completed_at: str | None = None
cost property
cost: float | None

Actual cost in dollars, set after execution completes.

This property cannot be set manually.

created_at instance-attribute
created_at: str | None = None
created_by instance-attribute
created_by: str | None = None
dto property
dto: dict[str, Any] | None

Last tools execution DTO from the platform, if any.

effort class-attribute instance-attribute
effort: int = effort
estimate property
estimate: float | None

Cost estimate in dollars, populated when the platform returns a quotation.

Set after run(quote=True), start(quote=True), or any call with approve_amount=-1. None until a quotation result is received. This property cannot be set manually.

id property
id: str | None

Platform execution ID when set (read-only).

ligands property
ligands: list[Ligand]

Ligands targeted by this run (read-only).

method property
method: str

Scoring path for this instance ("docking" or "ligand-ml").

name instance-attribute
name = (
    name
    if name is not None
    else _secondary_pharma_default_name(
        method=self._method,
        ligands=self._ligands,
        uniprots=self._uniprots,
        self_test=self._self_test,
    )
)
panel_version property
panel_version: str | None

The panel version this run's results were produced against (docking only).

Read from the run's own results, so it is set once they're loaded (:meth:get_results, :meth:get_poses or a gap check) and None before that, for ligand-ml runs, and for runs from before results named their panel. What you asked for (:attr:uniprots) is left as requested.

progress instance-attribute
progress: dict | None = None
runtime property
runtime: float | None

Seconds from DTO startedAt to completedAt or current UTC time.

Uses :attr:dto (same shape as client.executions.get). When completedAt is present, it is the end time; otherwise the end time is datetime.now(timezone.utc). Returns None if there is no DTO or startedAt is missing or empty.

self_test property
self_test: bool

Whether this run scores the baked test ligand against the full panel.

session instance-attribute
session: str | None = None
started_at instance-attribute
started_at: str | None = None
status instance-attribute
status: PlatformStatus | None = None
tool_key class-attribute instance-attribute
tool_key: str = TOOL_KEYS_AND_VERSIONS["secondary_pharma"][
    "tool_key"
]
tool_version instance-attribute
tool_version = tool_version
uniprots property writable
uniprots: list[str] | tuple[str, ...] | None

Panel accessions this run is restricted to, or None for the whole panel.

A mutable list on a draft instance. After an execution id is set, a tuple. None when unset or a rehydrated execution omitted the field.

Methods:

cancel
cancel() -> None

Cancel a running or queued execution.

Raises:

Type Description
ValueError

If the job has no execution ID.

ValueError

If the job is not in a cancellable state.

confirm
confirm() -> None

Confirm a quoted tools execution on the platform.

Requires :attr:id and status equal to "Quoted". Uses :meth:~deeporigin.platform.executions.Executions.confirm with :data:~deeporigin.utils.constants.TOOL_EXECUTION_POST_TIMEOUT_SECONDS (10 minutes) and retry=False, then applies the returned DTO via :meth:update_from_dto so status, :attr:cost, and :attr:dto reflect the platform response. For direct (blocking) tools the response is typically terminal (Completed); for async tools it may be Created or Running — call :meth:sync until the job finishes.

Raises:

Type Description
ValueError

If there is no platform execution id or status is not "Quoted".

duplicate
duplicate(
    *, client: DeepOriginClient | None = None
) -> Self

Copy configuration into a new draft with a writable uniprots.

from_dto does not fetch the tool definition, so a rehydrated instance has no allowlist. Fetch it here so the draft can assign uniprots like a constructor-built instance.

from_dto classmethod
from_dto(
    dto: dict[str, Any],
    *,
    client: DeepOriginClient | None = None
) -> Self

Construct a SecondaryPharmacology from a tools execution DTO.

Restores ligands, method, uniprots, effort, and self_test from userInputs, and batch_size from the execution's top-level batchSize field (or its metadata, for an older execution record) -- like batch_size, batchSize is not a schema input, so it doesn't live in userInputs. Does not fetch the live tool definition, so a rehydrated instance's uniprots is read-only until :meth:duplicate is called.

Parameters:

Name Type Description Default
dto dict[str, Any]

Execution payload (same shape as client.executions.get).

required
client DeepOriginClient | None

Optional API client. Uses the default if not provided.

None

Returns:

Type Description
Self

A SecondaryPharmacology with id, lifecycle fields, and

Self

domain inputs set.

from_id classmethod
from_id(
    id: str, *, client: DeepOriginClient | None = None
) -> Self

Construct an instance from an existing platform execution ID.

Fetches the execution DTO via client.executions.get and delegates to :meth:from_dto. Concrete subclasses override :meth:from_dto to attach domain state from userInputs.

Parameters:

Name Type Description Default
id str

Platform execution ID.

required
client DeepOriginClient | None

Optional API client. Uses the default if not provided.

None

Returns:

Type Description
Self

A partially-hydrated instance with common fields populated.

Raises:

Type Description
NotImplementedError

If cls has no tool_key (bare :class:Execution).

from_last_run classmethod
from_last_run(
    *, client: DeepOriginClient | None = None
) -> Self

Construct an instance from the most recently created execution of this tool.

Scoped to the client's project, like :meth:list. Calls client.executions.list with tool_key, order set to :data:~deeporigin.utils.constants.EXECUTION_LIST_ORDER_CREATED_DESC, the client's project_id, and page_size=1, then delegates to :meth:from_dto. Concrete subclasses inherit this method; domain state is restored via their from_dto overrides.

Parameters:

Name Type Description Default
client DeepOriginClient | None

Optional API client. Uses the default if not provided.

None

Returns:

Type Description
Self

A partially-hydrated instance for the newest execution by

Self

createdAt.

Raises:

Type Description
NotImplementedError

If cls has no tool_key (bare :class:Execution).

ValueError

If no executions exist for this tool type.

get_missing_pairs
get_missing_pairs() -> list[tuple[Ligand, str]] | None

(ligand, uniprot) pairs with no docked pose, or None if complete.

Valid only when :attr:method is "docking".

get_panel classmethod
get_panel(
    *,
    full: bool = False,
    panel_version: str | None = None,
    tool_version: str = TOOL_KEYS_AND_VERSIONS[
        "secondary_pharma"
    ]["tool_version"],
    client: DeepOriginClient | None = None
) -> DataFrame

Return the current scoring panel, or an earlier version of it.

No ligand or instance needed. Pass panel_version (e.g. a run's :attr:panel_version) to get exactly the panel that run used, even if the panel has changed since. Both methods validate uniprots against this same panel today; they may grow separate catalogs later.

Parameters:

Name Type Description Default
full bool

Return every member instead of a preview.

False
panel_version str | None

An immutable panel version to look up instead of the current panel.

None
tool_version str

Platform tool version to look up (ignored when panel_version is given). Defaults to the pinned major version in :data:TOOL_KEYS_AND_VERSIONS.

TOOL_KEYS_AND_VERSIONS['secondary_pharma']['tool_version']
client DeepOriginClient | None

Optional API client. Uses the default if not provided.

None

Returns:

Name Type Description
A DataFrame

class:pandas.DataFrame with one row per member (uniprot_id,

DataFrame

gene_name).

Raises:

Type Description
DeepOriginException

If panel_version is malformed or its catalog can't be loaded.

get_poses
get_poses(
    *,
    verify_receptor_digest: bool = True,
    dto: dict[str, Any] | None = None
) -> PoseSet

Download this docking run's poses, each with its panel receptor.

Docking only. Loads the run's panel_poses rows (as :meth:get_results does), builds a :class:PoseSet and downloads the pose files, like :meth:Docking.get_poses <deeporigin.drug_discovery.docking.Docking.get_poses>. Each pose whose row names a receptor_file_path also gets the receptor it was docked into, downloaded once per distinct receptor (cached) and checked against the row's structure_sha256; its local path is in pose.props["receptor_local_path"]. Rows without a receptor path (runs from before receptors were recorded) still yield a pose, just without that key.

These are panel poses, not registered poses, so they have no pose result id and can't be passed to tools that take a registered Pose (e.g. ABFE, RBFE). Not available for self_test runs, which publish no panel poses. To view one pose in its pocket, use :meth:show_panel_pose.

Parameters:

Name Type Description Default
verify_receptor_digest bool

Reject a receptor whose bytes don't match the row's structure_sha256 (default True).

True
dto dict[str, Any] | None

Optional execution payload, forwarded to :func:_load_panel_pose_rows.

None

Returns:

Name Type Description
A PoseSet

class:PoseSet of the downloaded panel poses.

Raises:

Type Description
ValueError

If :attr:method is not "docking", or this is a self_test run.

DeepOriginException

If a row's receptor_file_path is outside protected/ or contains .., or a receptor fails digest verification.

get_results
get_results(dto: dict[str, Any] | None = None) -> DataFrame

Return this execution's results as a :class:pandas.DataFrame.

Ligand-ml results are read from the run's jobOutputs (like :meth:Admet.get_results <deeporigin.drug_discovery.admet.Admet.get_results>). Docking is an async workflow, so its rows are read from result-explorer, falling back to jobOutputs (see :func:_load_panel_pose_rows). Docking rows include pose_score, binding_energy and file_path; every row has a method column.

The ligand-ml path drops the platform's unused p_affinity column. Each row sets exactly one of p_active / p_affinity, so a NaN p_active is that omission, not missing data.

Parameters:

Name Type Description Default
dto dict[str, Any] | None

Optional execution payload. Ligand-ml reads it instead of making an extra GET; docking consults it only if result-explorer has no rows yet.

None

Returns:

Type Description
DataFrame

A DataFrame of ligand_ml_predictions or panel_poses rows.

Raises:

Type Description
ValueError

If :attr:id is unset and dto is omitted, or for a self_test docking run (only a legacy rehydrated run can be one).

DeepOriginException

If no rows could be loaded.

get_undocked_ligands
get_undocked_ligands() -> LigandSet | None

Ligands with zero docked poses, or None if none. Docking only.

A ligand with at least one docked pose won't appear here, even if it's missing poses for other targets -- use :meth:get_missing_pairs for that.

get_user_logs
get_user_logs(
    *,
    limit: int | None = None,
    offset: int | None = None,
    select: list[str] | None = None,
    with_total_count: bool = False
) -> DataFrame | None

Search data-platform user_logs rows for this execution.

Uses :meth:deeporigin.platform.user_logs.UserLogs.search with this execution's id (tools executionId), stored as execution_id on user_logs rows — the same string passed to :meth:get_results as compute_job_id.

When no execution id is assigned yet, returns None without calling the API.

Parameters:

Name Type Description Default
limit int | None

Max rows to return (forwarded to UserLogs.search).

None
offset int | None

Skip offset (forwarded).

None
select list[str] | None

Columns to select (forwarded).

None
with_total_count bool

Request total count from the server (forwarded).

False

Returns:

Type Description
DataFrame | None

A DataFrame with columns log_level, tool_key, timestamp,

DataFrame | None

and message. tool_key omits the deeporigin. prefix;

DataFrame | None

timestamp is a compact humanized relative time. Returns None

DataFrame | None

if this instance has no execution id yet or the client has no

DataFrame | None

user_logs API.

list classmethod
list(
    *,
    client: DeepOriginClient | None = None,
    status: list[str] | None = None
) -> list[Self]

List executions of this tool, newest first, scoped to the client's project.

Parameters:

Name Type Description Default
client DeepOriginClient | None

Optional API client. Uses the default if not provided.

None
status list[str] | None

Optional list of statuses to keep.

None

Returns:

Type Description
list[Self]

Instances of this class, newest first.

Raises:

Type Description
NotImplementedError

If cls has no tool_key (bare :class:Execution).

plot
plot(
    *,
    dto: dict[str, Any] | None = None,
    metric: Literal[
        "binding_energy", "pose_score"
    ] = "binding_energy",
    clim: tuple[float, float] | None = None
) -> None

Visualize this run's results -- method-aware.

ligand-ml: heatmap colored by p_active. docking: heatmap colored by metric.

Parameters:

Name Type Description Default
dto dict[str, Any] | None

Optional execution payload, forwarded to :meth:get_results.

None
metric Literal['binding_energy', 'pose_score']

Docking only. Which column to color the heatmap by.

'binding_energy'
clim tuple[float, float] | None

Override the default color range. A value outside the chosen range still renders, clipped to the nearest edge color.

None
run
run(
    *,
    quote: bool = False,
    approve_amount: int | None = None
) -> DataFrame | SecondaryPharmacology

Execute the ligand-ml path synchronously and return predictions.

Valid only when :attr:method is "ligand-ml"; use :meth:start for the docking path.

With quote=True (or approve_amount=0), requests a cost estimate only, updates execution fields from the platform DTO, and returns self without running inference.

Parameters:

Name Type Description Default
quote bool

Shorthand for approve_amount=0.

False
approve_amount int | None

Spend cap forwarded as approveAmount.

None

Returns:

Name Type Description
A DataFrame | SecondaryPharmacology

class:pandas.DataFrame of ligand-ml predictions, or self

DataFrame | SecondaryPharmacology

when quoting.

Raises:

Type Description
ValueError

If :attr:method is not "ligand-ml", or if uniprots was mutated in place into an invalid selection.

DeepOriginException

If effort is outside 1-5, or the execution did not complete successfully.

show
show() -> None

Display the current execution in Jupyter using the execution card HTML view.

If no platform execution ID exists yet, shows the same card with a short notice instead of raising (see :meth:~deeporigin.platform.execution_display.ExecutionDisplay.from_pending).

show_panel_pose
show_panel_pose(
    *,
    ligand_id: str | None = None,
    ligand: Ligand | None = None,
    uniprot_id: str | None = None,
    gene_name: str | None = None,
    verify_receptor_digest: bool = True,
    height: int = 620,
    dto: dict[str, Any] | None = None
)

Show one docked pose in its target's pocket, in a Jupyter notebook.

Downloads the pose's panel receptor (checked against its recorded checksum) and overlays the docked ligand on it in an interactive Mol* viewer. Choose the pose by ligand and by target. Needs a completed docking run whose results record their receptor; use :meth:get_poses to work with poses in code.

Parameters:

Name Type Description Default
ligand_id str | None

Platform ligand id (from get_results()).

None
ligand Ligand | None

A ligand, instead of ligand_id. One without an id (for example rebuilt from SMILES after reloading a run) is matched on its SMILES.

None
uniprot_id str | None

Panel target accession.

None
gene_name str | None

Panel target gene symbol, matched against this run's results (instead of uniprot_id).

None
verify_receptor_digest bool

Verify the downloaded receptor against the row's structure_sha256 when it has one.

True
height int

Iframe height in pixels.

620
dto dict[str, Any] | None

Optional execution payload for loading pose rows.

None

Returns:

Name Type Description
The

func:~deeporigin.utils.notebook.render_html result for the viewer.

Raises:

Type Description
ValueError

If :attr:method is not "docking", the target isn't named exactly once, no single pose matches, or this is a self_test run.

DeepOriginException

If the row has no receptor_file_path, digest verification fails, or a download fails.

start
start(
    *,
    quote: bool = False,
    approve_amount: int | None = None,
    **kwargs
) -> None

Submit a persisted async execution to the platform.

Only valid when status is None (no execution exists yet). All other statuses raise immediately to prevent re-submission.

Pass quote=True or approve_amount=-1 to request a cost estimate without running. If the platform returns a Quoted DTO the instance is left in that state — call :meth:~deeporigin.drug_discovery.execution.Execution.confirm explicitly to proceed.

Parameters:

Name Type Description Default
quote bool

Shorthand for approve_amount=-1. Takes precedence when both quote and approve_amount are provided.

False
approve_amount int | None

Spend cap passed to the platform as approveAmount. -1 (or any negative value) forces a quote-only create. None omits the field (platform may auto-confirm).

None
**kwargs

Forwarded verbatim to _start_impl.

{}

Raises:

Type Description
ValueError

If the current status is not None.

stop_watching
stop_watching() -> None

Cancel an in-flight watch loop if one is running.

Safe to call when no watch is active. Does not cancel the caller's current task when invoked from inside the watch loop.

sync
sync() -> None

Fetch the latest tools execution from the platform and refresh fields.

Calls client.executions.get for :attr:id and applies the response with :meth:update_from_dto. Use when the job may have changed outside this process (for example after submission from the web UI), to poll lifecycle state, or to refresh an instance built from an older DTO. Available on sync-only and async execution types alike.

Rejected executions are refreshed without raising for their status, so history inspection and notebook displays remain available. HTTP failures while fetching the execution still raise.

If executions.get returns a falsy value, this instance is left unchanged.

Raises:

Type Description
ValueError

If this instance has no execution id yet.

NotImplementedError

If type(self).tool_key is empty (bare :class:Execution).

ValueError

If the returned DTO tool.key does not match this class (see :meth:update_from_dto).

update_from_dto
update_from_dto(dto: dict[str, Any]) -> None

Apply execution fields from dto, freeze uniprots, drop a stale panel version.

wait
wait(
    *,
    poll_interval: float = 5.0,
    timeout: float | None = None
) -> dict[str, Any] | None

Block until this execution reaches a terminal state.

Parameters:

Name Type Description Default
poll_interval float

Seconds to sleep between polling cycles.

5.0
timeout float | None

Maximum total seconds to wait. If None, waits indefinitely.

None

Returns:

Type Description
dict[str, Any] | None

The latest execution DTO, or None if the platform returned no DTO.

Raises:

Type Description
ValueError

If this instance has no execution id yet.

TimeoutError

If timeout elapses before the execution terminates.

watch async
watch(
    *, interval: float = 5.0, blocking: bool = False
) -> Task | None

Live notebook updates for the docking path only.

Valid only when :attr:method is "docking" -- the ligand-ml path is synchronous (:meth:run blocks until done), so there is never an in-flight async job to poll.

See :meth:~deeporigin.drug_discovery.notebook_watch_mixin.NotebookWatchMixin.watch for the full parameter/return contract.

Raises:

Type Description
ValueError

If :attr:method is not "docking".

Functions: