From 3b6d8775b63ee9fb14cf61f4fc6d419ef82d3e23 Mon Sep 17 00:00:00 2001 From: dariarom94 Date: Wed, 24 Jun 2026 18:12:50 +0200 Subject: [PATCH 1/4] fix bug --- src/methods_segmentation/custom_segmentation/script.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/methods_segmentation/custom_segmentation/script.py b/src/methods_segmentation/custom_segmentation/script.py index c108117f..d29cb9ab 100644 --- a/src/methods_segmentation/custom_segmentation/script.py +++ b/src/methods_segmentation/custom_segmentation/script.py @@ -2,6 +2,7 @@ import anndata as ad import os import shutil +import pandas as pd ## VIASH START par = { @@ -20,14 +21,18 @@ assert par["labels_key"] in sdata.labels, f"Key '{par['labels_key']}' not found in input data." print(f"Copy segmentation from '{par['labels_key']}'", flush=True) +metadata = sdata.tables["metadata"] +# Select only the columns that exist — Xenium provides cell_id and region, +# Vizgen uses different column names (or an empty obs) so we take what's available. +obs_cols = [c for c in ["cell_id", "region"] if c in metadata.obs.columns] sdata_segmentation_only = sd.SpatialData( labels={ "segmentation": sdata[par["labels_key"]] }, tables={ "table": ad.AnnData( - obs=sdata.tables["metadata"].obs[["cell_id", "region"]], - var=sdata.tables["metadata"].var[[]] + obs=metadata.obs[obs_cols], + var=metadata.var[[]] ) } ) From 207d00f190940b5e35931b1a198689f6c5bf2e0d Mon Sep 17 00:00:00 2001 From: dariarom94 Date: Wed, 24 Jun 2026 19:59:07 +0200 Subject: [PATCH 2/4] adjust startdist --- src/methods_segmentation/stardist/config.vsh.yaml | 1 + .../basic_transcript_assignment/script.py | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/methods_segmentation/stardist/config.vsh.yaml b/src/methods_segmentation/stardist/config.vsh.yaml index 5b5207c0..b8a6e7dc 100644 --- a/src/methods_segmentation/stardist/config.vsh.yaml +++ b/src/methods_segmentation/stardist/config.vsh.yaml @@ -34,6 +34,7 @@ engines: - stardist - tensorflow==2.17.0 - numpy<2.0.0 + - scipy<1.15.0 - type: native runners: diff --git a/src/methods_transcript_assignment/basic_transcript_assignment/script.py b/src/methods_transcript_assignment/basic_transcript_assignment/script.py index e28fed31..6e0967db 100644 --- a/src/methods_transcript_assignment/basic_transcript_assignment/script.py +++ b/src/methods_transcript_assignment/basic_transcript_assignment/script.py @@ -1,6 +1,7 @@ import numpy as np import xarray as xr import dask +import dask.dataframe as dd import spatialdata as sd import anndata as ad import pandas as pd @@ -33,14 +34,14 @@ assert par['coordinate_system'] in segmentation_coord_systems, f"Coordinate system '{par['coordinate_system']}' not found in input data." print('Transforming transcripts coordinates', flush=True) -# Parquet partitions each start from index 0, causing duplicate index values in the -# combined dask DataFrame. sd.transform() internally builds pd.Series(..., index=transformed.index) -# which fails with "cannot reindex on an axis with duplicate labels". -# Fix: reset to a global monotonic index before transforming; restore attrs explicitly -# because reset_index() drops them, which would break spatialdata's PointsModel check. +# Multi-partition parquet files each start with a 0-based index, producing duplicate index +# values in the combined dask DataFrame. sd.transform() internally creates a pd.Series with +# index=transformed.index; when that dask index is computed it triggers an assign expression +# that fails on duplicate/lazy indices. Fix: materialize to pandas and rebuild as a single +# dask partition with a clean RangeIndex before transforming. # The original sdata[transcripts_key] is left unchanged so lines below remain consistent. transcripts_input = sdata[par['transcripts_key']] -transcripts_reset = transcripts_input.reset_index(drop=True) +transcripts_reset = dd.from_pandas(transcripts_input.compute().reset_index(drop=True), npartitions=1) transcripts_reset.attrs.update(transcripts_input.attrs) transcripts = sd.transform(transcripts_reset, to_coordinate_system=par['coordinate_system']) From 8eb9becb455bddafed19ff7e29d1ac062c0d3ed6 Mon Sep 17 00:00:00 2001 From: dariarom94 Date: Wed, 24 Jun 2026 20:06:28 +0200 Subject: [PATCH 3/4] adjust mem for similarity --- src/base/labels_nebius.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/labels_nebius.config b/src/base/labels_nebius.config index 409ba948..bce93b74 100644 --- a/src/base/labels_nebius.config +++ b/src/base/labels_nebius.config @@ -124,8 +124,8 @@ withLabel: veryhightime { time = 24.h } // similarity metric does not need veryhighmem resources withName: '.*similarity_process' { - memory = '50.GB' - disk = '50.GB' + memory = '100.GB' + disk = '100.GB' } } From 8d59d913fc83ea3c4bba761b3b90f17c120c2623 Mon Sep 17 00:00:00 2001 From: dariarom94 Date: Wed, 24 Jun 2026 21:39:12 +0200 Subject: [PATCH 4/4] Fix duplicate parquet index in basic_transcript_assignment; force image pull on Nebius --- src/base/labels_nebius.config | 3 +++ .../basic_transcript_assignment/script.py | 16 +++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/base/labels_nebius.config b/src/base/labels_nebius.config index bce93b74..809ec963 100644 --- a/src/base/labels_nebius.config +++ b/src/base/labels_nebius.config @@ -33,6 +33,9 @@ process { // Default disk space disk = 50.GB + // Always pull the latest image digest so nodes never serve a stale cached image + pod = [[imagePullPolicy: 'Always']] + // Retry for exit codes that have something to do with memory issues // always retry once errorStrategy = { exitStrat(task) } diff --git a/src/methods_transcript_assignment/basic_transcript_assignment/script.py b/src/methods_transcript_assignment/basic_transcript_assignment/script.py index 6e0967db..20377b8a 100644 --- a/src/methods_transcript_assignment/basic_transcript_assignment/script.py +++ b/src/methods_transcript_assignment/basic_transcript_assignment/script.py @@ -56,16 +56,14 @@ label_image = sdata_segm["segmentation"]["scale0"].image.to_numpy() else: label_image = sdata_segm["segmentation"].to_numpy() -cell_id_dask_series = dask.dataframe.from_dask_array( - dask.array.from_array( - label_image[y_coords, x_coords], chunks=tuple(sdata[par['transcripts_key']].map_partitions(len).compute()) - ), - index=sdata[par['transcripts_key']].index -) -sdata[par['transcripts_key']]["cell_id"] = cell_id_dask_series +# Assign cell ids directly to transcripts_reset (clean-index single-partition dask DataFrame). +# Using sdata[par['transcripts_key']] here would reintroduce the duplicate parquet index, +# causing the same "cannot reindex on an axis with duplicate labels" error at write time. +cell_ids = label_image[y_coords, x_coords] +transcripts_reset["cell_id"] = pd.Series(cell_ids) #create new .obs for cells based on the segmentation output (corresponding with the transcripts 'cell_id') -unique_cells = np.unique(cell_id_dask_series) +unique_cells = np.unique(cell_ids) # check if a '0' (noise/background) cell is in cell_id and remove zero_idx = np.where(unique_cells == 0) @@ -83,7 +81,7 @@ print('Subsetting to transcripts cell id data', flush=True) sdata_transcripts_only = sd.SpatialData( points={ - "transcripts": sdata[par['transcripts_key']] + "transcripts": transcripts_reset }, tables={ "table": ad.AnnData(