diff --git a/CLAUDE.md b/CLAUDE.md index 6a72de5..585701e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,10 +96,12 @@ Always use `parallel::makeCluster()` (socket clusters). **Never use `mclapply`** Three built-in options via `load_LRM_database()`: - `"connectomedb2025"` (default) — bundled `.rda` files, human/mouse/rat/pig, no internet required - `"fantom5"` — bundled `.rda` files, same four species -- `"omnipath"` — requires `OmnipathR` (Suggests) and internet access +- `"omnipath"` — requires internet access; uses `OmnipathR` (Suggests) when available, with a base-R REST fallback Custom data frames are also accepted; must have `ligand` and `receptor` columns (configurable via `ligand.col`/`receptor.col`). +**OmniPath resilient loading** (ported from NICHESv1 PR #72 / issue #70): newer OmnipathR (≥ ~3.17, incl. 4.1.0) resolves the `organism` argument via `ncbi_taxid()`, which downloads/coalesces species tables from Ensembl, OMA, and UniProt — when any of those ancillary services is unreachable the call aborts with `"Can't combine ..1 and ..3"` even though the OmniPath interaction server is fine. `.load_omnipath()` therefore delegates to `.niches_fetch_omnipath_ligrec()`, which (1) tries the standard OmnipathR path with its logger muted, (2) falls back to a direct OmniPath REST query (`omnipathdb.org/interactions?datasets=ligrecextra&organisms=`) using the already-known NCBI taxon id, bypassing the organism-name lookup, and (3) raises an informative network error only if both fail. Fallback is base-R only (`utils::download.file`/`read.delim`), no new dependencies. All four OmniPath helpers live in `R/load_LRM_database.R`. + ### Data ingest - `extract_NICHESInputs_Seurat()` — supports Seurat V4 and V5; detects assay class automatically; **spatial coordinates must be extracted manually** and added to the returned metadata before calling `create_NICHESObject()` diff --git a/NEWS.md b/NEWS.md index 167bdf1..ef64804 100644 --- a/NEWS.md +++ b/NEWS.md @@ -37,8 +37,10 @@ release will accompany the methods paper. ConnectomeDB2025 pairs. - FANTOM5 bundled as pre-filtered `.rda` objects for human, mouse, rat, and pig; no internet connection required. -- OmniPath loaded via `import_ligrecextra_interactions()` for human, mouse, - and rat (pig not supported by OmniPath). +- OmniPath loaded for human, mouse, and rat (pig not supported by OmniPath). + Uses `OmnipathR::import_ligrecextra_interactions()` when available, with a + base-R REST fallback for resilience against upstream service outages (see + Bug fixes below). - User-supplied data frames accepted directly with `ligand.col` and `receptor.col` arguments for non-standard column names. @@ -152,6 +154,25 @@ release will accompany the methods paper. --- +## Bug fixes + +- **OmniPath loading no longer fails when ancillary services are down.** + Ported from NICHESv1 (PR #72 / issue #70). Newer OmnipathR (>= ~3.17, + including 4.1.0) resolves the `organism` argument through + `OmnipathR::ncbi_taxid()`, which builds an organism-name table by + downloading and coalescing species lists from Ensembl, OMA, and UniProt. + When one of those services is unreachable (e.g. omabrowser.org returning + HTTP 502), that step aborts with `"Can't combine `..1` and `..3`"` even + though the OmniPath interaction server itself is fine. `load_LRM_database("omnipath")` + now first attempts the standard OmnipathR path (with its console logging + muted) and, on failure, falls back to querying the OmniPath REST API + directly with the known NCBI taxon id, bypassing the organism-name lookup. + If both paths fail, an informative network error is raised. The fallback is + base-R only (`utils::download.file` / `utils::read.delim`) and adds no new + dependencies. + +--- + ## Known limitations - **`[.NICHESObject` does not propagate attributes.** Subsetting a diff --git a/R/load_LRM_database.R b/R/load_LRM_database.R index fddd88e..ee63da7 100644 --- a/R/load_LRM_database.R +++ b/R/load_LRM_database.R @@ -247,7 +247,18 @@ load_LRM_database <- function(db = "connectomedb2025", species, organism )) - raw <- OmnipathR::import_ligrecextra_interactions(organism = organism) + # NOTE (ported from NICHESv1 PR #72 / issue #70): newer OmnipathR (>= ~3.17, + # incl. 4.1.0) resolves the `organism` argument through OmnipathR::ncbi_taxid(), + # which builds an organism-name table by downloading and coalescing species + # lists from Ensembl, OMA and UniProt. When one of those ancillary services is + # unreachable (e.g. omabrowser.org returning HTTP 502), that step aborts with + # "Can't combine `..1` and `..3`" - even though the OmniPath interaction server + # itself is fine. Since we have already mapped the species to its NCBI taxon id + # above, we do not need that translation. The helper below first tries the + # normal OmnipathR path (preserving its caching when the services are healthy) + # and, if that fails, falls back to querying the OmniPath REST API directly with + # the known taxon id, which bypasses the organism-name lookup. + raw <- .niches_fetch_omnipath_ligrec(organism, verbose) result <- data.frame( ligand = raw$source_genesymbol, @@ -300,3 +311,151 @@ load_LRM_database <- function(db = "connectomedb2025", result } + + +## OMNIPATH RESILIENT FETCH (ported from NICHESv1 PR #72 / issue #70) ---- + +#' Resiliently fetch OmniPath ligand-receptor interactions +#' +#' Internal helper for \code{.load_omnipath()} that works around a known upstream +#' OmnipathR failure (NICHESv1 issue #70). Newer OmnipathR versions resolve the +#' \code{organism} argument via an organism-name table built from Ensembl, OMA and +#' UniProt downloads; when one of those services is down the call aborts before any +#' interactions are retrieved. This helper first attempts the standard OmnipathR +#' path, and on failure falls back to a direct OmniPath REST query using the +#' already-known NCBI taxon id, which does not require the organism-name lookup. +#' +#' @param organism Integer NCBI taxonomy id (9606 human, 10090 mouse, 10116 rat). +#' @param verbose Print progress/provenance messages. +#' +#' @return A data.frame of ligand-receptor interactions containing at least the +#' \code{source_genesymbol} and \code{target_genesymbol} columns. +#' +#' @noRd +.niches_fetch_omnipath_ligrec <- function(organism, verbose = TRUE) { + + # 1) Preferred path: standard OmnipathR call (uses its cache when services are + # up). The organism-name download noise is muted; if it fails we recover. + result <- tryCatch( + .niches_quiet_omnipath(OmnipathR::import_ligrecextra_interactions(organism = organism)), + error = function(e) e + ) + if (!inherits(result, "error")) { + if (verbose) { + ver <- tryCatch(as.character(utils::packageVersion("OmnipathR")), + error = function(e) NA_character_) + message(sprintf( + "[load_LRM_database] Loaded OmniPath via OmnipathR %s (retrieved %s): %d interactions.", + ver, Sys.Date(), nrow(result))) + } + return(result) + } + + # 2) Fallback: query the OmniPath REST API directly with the known taxon id. + # This avoids OmnipathR's organism-name translation (the step that fails). + fallback <- tryCatch(.niches_omnipath_rest_ligrec(organism), error = function(e) e) + if (!inherits(fallback, "error") && nrow(fallback) > 0L) { + if (verbose) { + ver <- .niches_omnipath_server_version() + message(sprintf( + "[load_LRM_database] Loaded OmniPath via REST fallback (OmniPath server %s, retrieved %s): %d interactions.", + if (is.na(ver)) "REST API" else ver, Sys.Date(), nrow(fallback))) + } + return(fallback) + } + + # 3) Both paths failed: raise an informative error (this stays loud on purpose). + stop( + "[load_LRM_database] Unable to load the OmniPath ligand-receptor database.\n", + " OmnipathR error : ", conditionMessage(result), "\n", + " REST fallback : ", + if (inherits(fallback, "error")) conditionMessage(fallback) else "returned no interactions", "\n", + " The OmniPath interaction server (omnipathdb.org) may be unreachable from\n", + " this machine (check network/proxy access to https://omnipathdb.org).\n", + " See NICHESv1 issue #70 for background.", + call. = FALSE + ) +} + + +#' Run an OmnipathR expression with its console logging muted +#' +#' OmnipathR reports failed ancillary downloads (e.g. the OMA/Ensembl species +#' lists) through its own \code{logger}-based console appender, which is not +#' captured by \code{suppressWarnings}/\code{suppressMessages}. Since NICHES +#' handles those failures itself (issue #70), this helper temporarily raises the +#' OmnipathR console log threshold so the expected noise is hidden, restoring the +#' previous level afterwards. Genuine errors still propagate as R conditions. +#' +#' @param expr An expression calling OmnipathR (evaluated lazily inside here). +#' +#' @noRd +.niches_quiet_omnipath <- function(expr) { + old <- getOption("omnipathr.console_loglevel", default = "success") + try(OmnipathR::omnipath_set_console_loglevel("fatal"), silent = TRUE) + on.exit(try(OmnipathR::omnipath_set_console_loglevel(old), silent = TRUE), add = TRUE) + suppressWarnings(suppressMessages(expr)) +} + + +#' Best-effort OmniPath web server version +#' +#' Reads the OmniPath service banner (\code{https://omnipathdb.org/about}) and +#' extracts the server version for provenance reporting. Returns \code{NA} if the +#' banner cannot be read or parsed - version reporting must never block a load. +#' +#' @return Character server version (e.g. "0.1.5"), or \code{NA_character_}. +#' +#' @noRd +.niches_omnipath_server_version <- function() { + tryCatch({ + txt <- paste(readLines("https://omnipathdb.org/about", warn = FALSE, n = 3), collapse = " ") + m <- regmatches(txt, regexec("omnipath-server[[:space:]]+([0-9]+(\\.[0-9]+)*)", txt))[[1]] + if (length(m) >= 2 && nzchar(m[2])) m[2] else NA_character_ + }, error = function(e) NA_character_) +} + + +#' Direct OmniPath REST query for ligrecextra interactions +#' +#' Downloads the \code{ligrecextra} ligand-receptor dataset straight from the +#' OmniPath web service for a given NCBI taxon id, bypassing OmnipathR's +#' organism-name translation. The web service performs ortholog translation +#' server-side, so mouse (10090) and rat (10116) return organism-appropriate +#' gene symbols, matching what OmnipathR would otherwise produce. +#' +#' @param organism Integer NCBI taxonomy id. +#' +#' @return A data.frame with (at least) \code{source_genesymbol} and +#' \code{target_genesymbol} columns. +#' +#' @noRd +.niches_omnipath_rest_ligrec <- function(organism) { + + url <- sprintf( + "https://omnipathdb.org/interactions?datasets=ligrecextra&organisms=%s&genesymbols=yes", + as.integer(organism) + ) + + tmp <- tempfile(fileext = ".tsv") + on.exit(unlink(tmp), add = TRUE) + utils::download.file(url, destfile = tmp, quiet = TRUE, mode = "wb") + + df <- utils::read.delim( + tmp, + sep = "\t", + header = TRUE, + quote = "", + stringsAsFactors = FALSE, + check.names = FALSE + ) + + if (!all(c("source_genesymbol", "target_genesymbol") %in% colnames(df))) + stop("OmniPath REST response did not contain the expected gene symbol columns.") + + # Drop rows without gene symbols on either side (mirrors usable interactions). + df <- df[nzchar(df$source_genesymbol) & nzchar(df$target_genesymbol) & + !is.na(df$source_genesymbol) & !is.na(df$target_genesymbol), , drop = FALSE] + + df +} diff --git a/omnipathr-log/omnipathr-20260814-1107.log b/omnipathr-log/omnipathr-20260814-1107.log new file mode 100644 index 0000000..179ad74 --- /dev/null +++ b/omnipathr-log/omnipathr-20260814-1107.log @@ -0,0 +1,48 @@ +[2026-08-14 11:07:12] [TRACE] [OmnipathR] Reading JSON from `/Users/nw426/Library/Caches/OmnipathR/cache.json` (encoding: UTF-8). +[2026-08-14 11:07:12] [TRACE] [OmnipathR] JSON validation successful: TRUE +[2026-08-14 11:07:12] [INFO] [OmnipathR] Initialized cache: `/Users/nw426/Library/Caches/OmnipathR`. +[2026-08-14 11:07:12] [INFO] [OmnipathR] Package `OmnipathR` packaged: 2026-08-12 21:54:43 UTC; nw426 +[2026-08-14 11:07:12] [INFO] [OmnipathR] Package `OmnipathR` date/publication: NA +[2026-08-14 11:07:12] [INFO] [OmnipathR] Package `OmnipathR` built: R 4.3.3; ; 2026-08-12 21:54:43 UTC; unix +[2026-08-14 11:07:12] [INFO] [OmnipathR] Package `OmnipathR` version: 4.1.0 +[2026-08-14 11:07:12] [INFO] [OmnipathR] Package `OmnipathR` repository: NA +[2026-08-14 11:07:13] [INFO] [OmnipathR] Session info: [version=R version 4.3.3 (2024-02-29); os=macOS Sonoma 14.7.2; system=aarch64, darwin20; ui=RStudio; language=(EN); collate=en_US.UTF-8; ctype=en_US.UTF-8; tz=America/New_York; date=2026-08-14; rstudio=2024.12.1+563 Kousa Dogwood (desktop); pandoc=3.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown); quarto=1.5.57 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/quarto] +[2026-08-14 11:07:14] [INFO] [OmnipathR] External libraries: [cairo=; cairoFT=; pango=; png=; jpeg=; tiff=; tcl=; curl=8.7.1; zlib=1.2.12; bzlib=1.0.8, 13-Jul-2019; xz=5.4.4; PCRE=10.42 2022-12-11; ICU=74.1; TRE=TRE 0.8.0 R_fixes (BSD); iconv=Apple or GNU libiconv 1.11; readline=5.2; BLAS=/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib; lapack=/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib; lapack_version=3.11.0] +[2026-08-14 11:07:14] [INFO] [OmnipathR] Loaded packages: abind 1.4-8(2024-09-12); backports 1.5.0(2024-05-23); bit 4.6.0(2025-03-06); bit64 4.6.0-1(2025-01-16); blob 1.2.4(2023-03-17); brio 1.1.5(2024-04-24); cachem 1.1.0(2024-05-16); cellranger 1.1.0(2016-07-27); checkmate 2.3.2(2024-07-29); cli 3.6.4(2025-02-13); cluster 2.1.8.3(2026-07-30); codetools 0.2-20(2024-03-31); colorspace 2.1-1(2024-07-26); cowplot 1.2.0(2025-07-07); crayon 1.5.3(2024-06-20); curl 6.2.1(2025-02-19); data.table 1.17.0(2025-02-22); DBI 1.2.3(2024-06-02); dbscan 1.2.2(2025-01-26); deldir 2.0-4(2024-02-28); desc 1.4.3(2023-12-10); devtools 2.4.5(2022-10-11); digest 0.6.37(2024-08-19); dotCall64 1.2(2024-10-04); dplyr 1.2.1(2026-04-03); ellipsis 0.3.2(2021-04-29); evaluate 1.0.3(2025-01-10); farver 2.1.2(2024-05-13); fastDummies 1.7.5(2025-01-20); fastmap 1.2.0(2024-05-15); fitdistrplus 1.2-2(2025-01-07); fs 1.6.5(2024-10-30); future 1.34.0(2024-07-29); future.apply 1.11.3(2024-10-27); generics 0.1.3(2022-07-05); ggplot2 3.5.2(2025-04-09); ggrepel 0.9.6(2024-09-07); ggridges 0.5.6(2024-01-23); globals 0.16.3(2024-03-08); glue 1.8.0(2024-09-30); goftest 1.2-3(2021-10-07); gridExtra 2.3(2017-09-09); gtable 0.3.6(2024-10-25); hms 1.1.3(2023-03-21); htmltools 0.5.8.1(2024-04-04); htmlwidgets 1.6.4(2023-12-06); httpuv 1.6.15(2024-03-26); httr 1.4.7(2023-08-15); httr2 1.1.0(2025-01-18); ica 1.0-3(2022-07-08); ifnb.SeuratData 3.1.0(2026-04-17); igraph 2.1.4(2025-01-23); irlba 2.3.5.1(2022-10-03); jsonlite 1.9.1(2025-03-03); KernSmooth 2.23-26(2025-01-01); knitr 1.49(2024-11-08); later 1.4.1(2024-11-27); lattice 0.22-6(2024-03-20); lazyeval 0.2.2(2019-03-15); lifecycle 1.0.5(2026-01-08); listenv 0.9.1(2024-01-29); lmtest 0.9-40(2022-03-21); logger 0.4.0(2024-10-22); lubridate 1.9.4(2024-12-08); magrittr 2.0.3(2022-03-30); MASS 7.3-60.0.1(2024-01-13); Matrix 1.6-5(2024-01-11); matrixStats 1.5.0(2025-01-07); memoise 2.0.1(2021-11-26); mime 0.12(2021-09-28); miniUI 0.1.1.1(2018-05-18); NICHESv2 0.0.9000(2026-06-18); nlme 3.1-167(2025-01-27); OmnipathR 4.1.0(2026-08-12); panc8.SeuratData 3.0.2(2026-08-10); parallelly 1.42.0(2025-01-30); patchwork 1.3.0(2024-09-16); pbapply 1.7-2(2023-06-27); pillar 1.10.1(2025-01-07); pkgbuild 1.4.6(2025-01-16); pkgconfig 2.0.3(2019-09-22); pkgload 1.4.0(2024-06-28); plotly 4.10.4(2024-01-13); plyr 1.8.9(2023-10-02); png 0.1-8(2022-11-29); polyclip 1.10-7(2024-07-23); prettyunits 1.2.0(2023-09-24); profvis 0.4.0(2024-09-20); progress 1.2.3(2023-12-06); progressr 0.15.1(2024-11-22); promises 1.3.2(2024-11-28); purrr 1.0.4(2025-02-05); R.methodsS3 1.8.2(2022-06-13); R.oo 1.27.0(2024-11-01); R.utils 2.13.0(2025-02-24); R6 2.6.1(2025-02-15); RANN 2.6.2(2024-08-25); rappdirs 0.3.3(2021-01-31); RColorBrewer 1.1-3(2022-04-03); Rcpp 1.1.0(2025-07-02); RcppAnnoy 0.0.22(2024-01-23); RcppHNSW 0.6.0(2024-02-04); readr 2.1.5(2024-01-10); readxl 1.4.4(2025-02-27); remotes 2.5.0(2024-03-17); reshape2 1.4.4(2020-04-09); reticulate 1.41.0(2025-02-24); rlang 1.3.0(2026-07-05); rmarkdown 2.29(2024-11-04); ROCR 1.0-11(2020-05-02); rprojroot 2.0.4(2023-11-05); RSpectra 0.16-2(2024-07-18); RSQLite 2.4.3(2025-08-20); rstudioapi 0.17.1(2024-10-22); Rtsne 0.17(2023-12-07); rvest 1.0.4(2024-02-12); scales 1.4.0(2025-04-24); scattermore 1.2(2023-06-12); sctransform 0.4.1(2023-10-19); sessioninfo 1.2.3(2025-02-05); Seurat 5.2.1(2025-01-24); SeuratData 0.2.2.9002(2025-03-07); SeuratObject 5.0.2(2024-05-08); shiny 1.10.0(2024-12-14); sp 2.1-4(2024-04-30); spam 2.11-1(2025-01-20); spatstat.data 3.1-4(2024-11-15); spatstat.explore 3.3-4(2025-01-08); spatstat.geom 3.3-5(2025-01-18); spatstat.random 3.3-2(2024-09-18); spatstat.sparse 3.1-0(2024-06-21); spatstat.univar 3.1-2(2025-03-05); spatstat.utils 3.1-2(2025-01-08); stringi 1.8.4(2024-05-06); stringr 1.5.1(2023-11-14); stxBrain.SeuratData 0.1.2(2026-04-20); survival 3.8-3(2024-12-17); tensor 1.5(2012-05-05); testthat 3.2.3(2025-01-13); tibble 3.2.1(2023-03-20); tidyr 1.3.2(2025-12-19); tidyselect 1.2.1(2024-03-11); timechange 0.3.0(2024-01-18); tzdb 0.5.0(2025-03-15); urlchecker 1.0.1(2021-11-30); usethis 3.1.0(2024-11-26); uwot 0.2.3(2025-02-24); vctrs 0.7.3(2026-04-11); viridisLite 0.4.2(2023-05-02); withr 3.0.2(2024-10-28); xfun 0.52(2025-04-02); XML 3.99-0.18(2025-01-01); xml2 1.3.7(2025-02-28); xtable 1.8-4(2019-04-21); yaml 2.3.10(2024-07-26); zip 2.3.2(2025-02-01); zoo 1.8-13(2025-02-22) +[2026-08-14 11:07:14] [INFO] [OmnipathR] CURL: version: 8.11.1; headers: 8.11.1; ssl_version: OpenSSL/3.3.2 (SecureTransport); libz_version: 1.2.12; libssh_version: NA; libidn_version: NA; host: aarch64-apple-darwin23.6.0; protocols: dict, file, ftp, ftps, gopher, gophers, http, https, imap, imaps, ldap, ldaps, mqtt, pop3, pop3s, rtsp, smb, smbs, smtp, smtps, telnet, tftp, ws, wss; ipv6: TRUE; http2: TRUE; idn: FALSE; url_parser: TRUE +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Reading JSON from `/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library/OmnipathR/db/db_def.json` (encoding: UTF-8). +[2026-08-14 11:07:14] [TRACE] [OmnipathR] JSON validation successful: TRUE +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Reading JSON from `/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library/OmnipathR/internal/magic_bytes.json` (encoding: UTF-8). +[2026-08-14 11:07:14] [TRACE] [OmnipathR] JSON validation successful: TRUE +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Reading JSON from `/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library/OmnipathR/internal/urls.json` (encoding: UTF-8). +[2026-08-14 11:07:14] [TRACE] [OmnipathR] JSON validation successful: TRUE +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Reading JSON from `/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library/OmnipathR/internal/id_types.json` (encoding: UTF-8). +[2026-08-14 11:07:14] [TRACE] [OmnipathR] JSON validation successful: TRUE +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Processing args for OmniPath query +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Arguments for OmniPath query: [organisms=9606,datasets=ligrecextra] +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Processing args for OmniPath query +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Arguments for OmniPath query: [organisms=9606,datasets=ligrecextra,query_type=interactions] +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Param in `omnipath_query`: [query_type=interactions,organism=9606,resources=NULL(0),datasets=ligrecextra,types=NULL(0),genesymbols=yes,fields=NULL(0),default_fields=TRUE,silent=FALSE,logicals=NULL(0),download_args=[],format=data.frame,references_by_resource=TRUE,add_counts=TRUE,license=NULL(0),password=NULL(0),exclude=NULL(0),json_param=[],strict_evidences=FALSE,genesymbol_resource=UniProt,cache=TRUE] +[2026-08-14 11:07:14] [INFO] [OmnipathR] Loading database `Ensembl and OMA organism names`. +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Checking on-disk cache for database `organisms`. +[2026-08-14 11:07:14] [INFO] [OmnipathR] Cache record does not exist: `db://organisms` +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Loading database `organisms` from source. +[2026-08-14 11:07:14] [INFO] [OmnipathR] Looking up in cache `https://www.ensembl.org/info/about/species.html`: key=7332486db7400730697234bad76ca0c8e4d00799, latest version=1. +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Cache file path: /Users/nw426/Library/Caches/OmnipathR/7332486db7400730697234bad76ca0c8e4d00799-1.html +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Downloading by `generic_downloader`. +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Looking up in cache: `https://omabrowser.org/All/oma-species.txt`. +[2026-08-14 11:07:14] [TRACE] [OmnipathR] Loaded data from RDS `/Users/nw426/Library/Caches/OmnipathR/30e690cbb55dfc63b5903ab337f34ffc2f4be397-2.rds`. +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Processing args for OmniPath query +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Arguments for OmniPath query: [organisms=9606,datasets=ligrecextra] +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Processing args for OmniPath query +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Arguments for OmniPath query: [organisms=9606,datasets=ligrecextra,query_type=interactions] +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Param in `omnipath_query`: [query_type=interactions,organism=9606,resources=NULL(0),datasets=ligrecextra,types=NULL(0),genesymbols=yes,fields=NULL(0),default_fields=TRUE,silent=FALSE,logicals=NULL(0),download_args=[],format=data.frame,references_by_resource=TRUE,add_counts=TRUE,license=NULL(0),password=NULL(0),exclude=NULL(0),json_param=[],strict_evidences=FALSE,genesymbol_resource=UniProt,cache=TRUE] +[2026-08-14 11:07:15] [INFO] [OmnipathR] Loading database `Ensembl and OMA organism names`. +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Checking on-disk cache for database `organisms`. +[2026-08-14 11:07:15] [INFO] [OmnipathR] Cache record does not exist: `db://organisms` +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Loading database `organisms` from source. +[2026-08-14 11:07:15] [INFO] [OmnipathR] Looking up in cache `https://www.ensembl.org/info/about/species.html`: key=7332486db7400730697234bad76ca0c8e4d00799, latest version=1. +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Cache file path: /Users/nw426/Library/Caches/OmnipathR/7332486db7400730697234bad76ca0c8e4d00799-1.html +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Downloading by `generic_downloader`. +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Looking up in cache: `https://omabrowser.org/All/oma-species.txt`. +[2026-08-14 11:07:15] [TRACE] [OmnipathR] Loaded data from RDS `/Users/nw426/Library/Caches/OmnipathR/30e690cbb55dfc63b5903ab337f34ffc2f4be397-2.rds`. diff --git a/working-scripts-NW/test_omnipath_ifnb.R b/working-scripts-NW/test_omnipath_ifnb.R new file mode 100644 index 0000000..f457c6a --- /dev/null +++ b/working-scripts-NW/test_omnipath_ifnb.R @@ -0,0 +1,120 @@ +# test_omnipath_ifnb.R --------------------------------------------------------- +# End-to-end test of the OmniPath resilient-loading fix (NICHESv1 PR #72 / +# issue #70, ported into NICHESv2's load_LRM_database.R). +# +# Goal: confirm that create_NICHESObject() runs cleanly with LRM.db = "omnipath" +# on a real, public Seurat dataset (ifnb interferon-beta-stimulated human PBMCs), +# exercising the full pipeline: OmniPath fetch -> edge list -> LR scoring -> +# NICHESObject assembly -> neighborhood extraction -> aggregation. +# +# The dataset is non-spatial, so we use mode = "nonspatial" (v1-style stratified +# random sampling), which requires a cell.type.col. +# +# Run from the package root: +# Rscript working-scripts-NW/test_omnipath_ifnb.R +# ------------------------------------------------------------------------------ + +# -- 0. Dependencies ----------------------------------------------------------- +# Seurat / SeuratData provide the public ifnb dataset; OmnipathR is needed for +# the *primary* OmniPath path (the REST fallback is base-R only). +suppressPackageStartupMessages({ + library(Seurat) + library(SeuratData) +}) + +# Load the package under test (dev version, straight from source). +devtools::load_all(quiet = TRUE) + +set.seed(42) + +# -- 1. Get the ifnb dataset --------------------------------------------------- +if (!"ifnb" %in% SeuratData::AvailableData()[, "Dataset"] || + is.na(SeuratData::AvailableData()["ifnb.SeuratData", "Installed"]) || + !isTRUE(SeuratData::AvailableData()["ifnb.SeuratData", "Installed"])) { + message("Installing ifnb dataset via SeuratData ...") + SeuratData::InstallData("ifnb") +} + +ifnb <- SeuratData::LoadData("ifnb") +ifnb <- UpdateSeuratObject(ifnb) # harmless if already current; normalizes V4/V5 + +# ifnb metadata carries: +# - seurat_annotations : cell-type labels (used as cell.type.col) +# - stim : CTRL vs STIM condition +cat("\n--- ifnb meta.data columns ---\n") +print(colnames(ifnb@meta.data)) +cat("\n--- cell-type counts (seurat_annotations) ---\n") +print(table(ifnb$seurat_annotations, useNA = "ifany")) + +# Optional: subset to keep the test fast. Comment out to run on the full object. +n.sub <- 2000L +if (ncol(ifnb) > n.sub) { + ifnb <- ifnb[, sample(colnames(ifnb), n.sub)] + message(sprintf("Subsetted to %d cells for a fast smoke test.", ncol(ifnb))) +} + +# Normalize so LR scoring runs on expression-scaled values (v1 convention). +ifnb <- NormalizeData(ifnb, verbose = FALSE) + +# -- 2. Extract NICHES inputs -------------------------------------------------- +# Pull the normalized "data" layer; extract does NOT add coordinates (fine for +# non-spatial mode). +inputs <- extract_NICHESInputs_Seurat(ifnb, assay = "RNA", layer = "data") + +# Drop cells with an NA cell-type label (nonspatial mode stratifies on it). +keep <- !is.na(inputs$meta.data$seurat_annotations) +inputs$count.mtx <- inputs$count.mtx[, keep, drop = FALSE] +inputs$meta.data <- inputs$meta.data[keep, , drop = FALSE] + +# -- 3. Load OmniPath directly (isolates the fix before the full pipeline) ----- +cat("\n========== STEP A: load_LRM_database('omnipath') ==========\n") +op.db <- load_LRM_database("omnipath", species = "human", verbose = TRUE) +cat(sprintf("OmniPath LR pairs loaded: %d\n", nrow(op.db))) +stopifnot(nrow(op.db) > 0, + all(c("ligand", "receptor") %in% colnames(op.db))) + +# -- 4. Full pipeline with LRM.db = "omnipath" --------------------------------- +cat("\n========== STEP B: create_NICHESObject(LRM.db = 'omnipath') ==========\n") +niches <- create_NICHESObject( + count.mtx = inputs$count.mtx, + meta.data = inputs$meta.data, + LRM.db = "omnipath", + mode = "nonspatial", + species = "human", + cell.type.col = "seurat_annotations", + max.cells = 100L, # cap edges per cell-type crossing (nonspatial) + method = "product", + n.cores = 1L, + verbose = TRUE +) + +# -- 5. Sanity checks on the resulting object ---------------------------------- +cat("\n========== STEP C: sanity checks ==========\n") +cat("class : ", paste(class(niches), collapse = ", "), "\n") +cat("mode attr : ", attr(niches, "mode"), "\n") +cat("n cells : ", length(niches$cell.list), "\n") +cat("n edges : ", length(niches$edge.list), "\n") +cat("n LRMs : ", length(niches$LRM.list), "\n") +cat("edge.data rows : ", nrow(niches$edge.data), "\n") + +stopifnot( + inherits(niches, "NICHESObject"), + length(niches$edge.list) > 0, + length(niches$LRM.list) > 0, + nrow(niches$edge.data) > 0 +) + +# -- 6. Aggregation (optional; exercises the rest of the pipeline) ------------- +# Non-fatal: the OmniPath fix is already proven by STEP B/C. This just confirms +# downstream aggregation runs on an OmniPath-scored object. +cat("\n========== STEP D: aggregate_NICHESObject() (optional) ==========\n") +tryCatch({ + niches <- aggregate_NICHESObject(niches, + cell.type.col = "seurat_annotations", + verbose = TRUE) + cat("aggregation slots: ", paste(names(niches$aggregations), collapse = ", "), "\n") +}, error = function(e) { + cat("STEP D skipped (non-fatal):", conditionMessage(e), "\n") +}) + +cat("\n\n*** OmniPath end-to-end test PASSED (STEPS A-C) ***\n")