diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 503f3c54..43669112 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -14,25 +14,19 @@ export_plots: true disable_version_detection: true -run_module: - - xenium - -module_order: - - xenium - log_filesize_limit: 5000000000 # 5GB sp: - cell_feature_matrix: - fn: cell_feature_matrix.h5 - cells: - fn: cells.parquet - experiment: - fn: experiment.xenium + xenium/cell_feature_matrix: + fn: "*/cell_feature_matrix.h5" + xenium/cells: + fn: "*/cells.parquet" + xenium/experiment: + fn: "*/experiment.xenium" num_lines: 50 - metrics: + xenium/metrics: contents: num_cells_detected - fn: metrics_summary.csv + fn: "*/metrics_summary.csv" num_lines: 5 - transcripts: - fn: transcripts.parquet + xenium/transcripts: + fn: "*/transcripts.parquet" diff --git a/conf/modules.config b/conf/modules.config index 232e504b..42875673 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -32,6 +32,7 @@ process { path: "${params.outdir}/xeniumranger/resegment", mode: params.publish_dir_mode ] + ext.prefix = {"${meta.id}"} } withName: XENIUMRANGER_IMPORT_SEGMENTATION { @@ -39,6 +40,7 @@ process { path: "${params.outdir}/xeniumranger/import_segementation", mode: params.publish_dir_mode ] + ext.prefix = {"${meta.id}"} } withName: FICTURE_PREPROCESS { @@ -152,4 +154,24 @@ process { ext.args = { "--diameter 9 --channel_axis 0 --save_flows" } } + withName: OPT_FLIP { + publishDir = [ + path: { "${params.outdir}/opt/flip" }, + mode: params.publish_dir_mode, + ] + } + + withName: OPT_TRACK { + publishDir = [ + path: { "${params.outdir}/opt/track" }, + mode: params.publish_dir_mode, + ] + } + + withName: OPT_STAT { + publishDir = [ + path: { "${params.outdir}/opt/stat" }, + mode: params.publish_dir_mode, + ] + } } diff --git a/modules.json b/modules.json index 9cff4eec..aad699b5 100644 --- a/modules.json +++ b/modules.json @@ -19,17 +19,20 @@ "opt/flip": { "branch": "master", "git_sha": "66d5baa4e9b6ac3ab95e84f88709b8e3ebf4b62b", - "installed_by": ["modules"] + "installed_by": ["modules"], + "patch": "modules/nf-core/opt/flip/opt-flip.diff" }, "opt/stat": { "branch": "master", "git_sha": "66d5baa4e9b6ac3ab95e84f88709b8e3ebf4b62b", - "installed_by": ["modules"] + "installed_by": ["modules"], + "patch": "modules/nf-core/opt/stat/opt-stat.diff" }, "opt/track": { "branch": "master", "git_sha": "66d5baa4e9b6ac3ab95e84f88709b8e3ebf4b62b", - "installed_by": ["modules"] + "installed_by": ["modules"], + "patch": "modules/nf-core/opt/track/opt-track.diff" }, "untar": { "branch": "master", diff --git a/modules/local/baysor/create_dataset/main.nf b/modules/local/baysor/create_dataset/main.nf index ef09970e..c3daf0e0 100644 --- a/modules/local/baysor/create_dataset/main.nf +++ b/modules/local/baysor/create_dataset/main.nf @@ -9,8 +9,8 @@ process BAYSOR_CREATE_DATASET { val(sample_fraction) output: - tuple val(meta), path("sampled_transcripts.csv"), emit: sampled_transcripts - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/sampled_transcripts.csv"), emit: sampled_transcripts + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -21,6 +21,8 @@ process BAYSOR_CREATE_DATASET { error "BAYSOR_CREATE_DATASET module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + template 'create_dataset.py' stub: @@ -29,8 +31,11 @@ process BAYSOR_CREATE_DATASET { error "BAYSOR_CREATE_DATASET module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + """ - touch sampled_transcripts.csv + mkdir -p ${prefix} + touch "${prefix}/sampled_transcripts.csv" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/baysor/create_dataset/templates/create_dataset.py b/modules/local/baysor/create_dataset/templates/create_dataset.py index 21db550b..51c67ab5 100644 --- a/modules/local/baysor/create_dataset/templates/create_dataset.py +++ b/modules/local/baysor/create_dataset/templates/create_dataset.py @@ -14,7 +14,8 @@ def generate_dataset( transcripts: Path, sampled_transcripts: Path, sample_fraction: float = 0.3, - random_state: int = 42 + random_state: int = 42, + prefix: str = "" ) -> None: """ Reads a csv file & randomly samples a fraction of rows, @@ -28,8 +29,8 @@ def generate_dataset( """ random.seed(random_state) - - with open(transcripts, mode='rt', newline='') as infile, \ + output_path = f"{prefix}/{transcripts}" + with open(output_path, mode='rt', newline='') as infile, \ open(sampled_transcripts, mode='wt', newline='') as outfile: reader = csv.reader(infile) @@ -61,13 +62,15 @@ def main() -> None: """ transcripts: str = "${transcripts}" sample_fraction: float = "${sample_fraction}" + prefix: str = "${meta.id}" sampled_transcripts: str = "sampled_transcripts.csv" # generate dataset BaysorPreview.generate_dataset ( transcripts=transcripts, sampled_transcripts=sampled_transcripts, - sample_fraction=sample_fraction + sample_fraction=sample_fraction, + prefix=prefix ) # generate versions.yml diff --git a/modules/local/baysor/preprocess/main.nf b/modules/local/baysor/preprocess/main.nf index 1a240af7..aaf9d378 100644 --- a/modules/local/baysor/preprocess/main.nf +++ b/modules/local/baysor/preprocess/main.nf @@ -13,22 +13,35 @@ process BAYSOR_PREPROCESS_TRANSCRIPTS { val(min_y) output: - tuple val(meta), path("*.parquet"), emit: transcripts_parquet - path("versions.yml") , emit: versions + tuple val(meta), + path("${prefix}/filtered_transcripts.parquet"), emit: transcripts_parquet + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "PREPROCESS_TRANSCRIPTS module does not support Conda. Please use Docker / Singularity / Podman instead." + error "BAYSOR_PREPROCESS_TRANSCRIPTS module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + template 'preprocess_transcripts.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "BAYSOR_PREPROCESS_TRANSCRIPTS module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" + """ - touch ${transcripts}.parquet + mkdir -p ${prefix} + touch ${prefix}/filtered_transcripts.parquet + cat <<-END_VERSIONS > versions.yml "${task.process}": baysor_preprocess_transcripts: "1.0.0" diff --git a/modules/local/baysor/preprocess/templates/preprocess_transcripts.py b/modules/local/baysor/preprocess/templates/preprocess_transcripts.py index 9820e16d..5851b133 100644 --- a/modules/local/baysor/preprocess/templates/preprocess_transcripts.py +++ b/modules/local/baysor/preprocess/templates/preprocess_transcripts.py @@ -9,7 +9,8 @@ def filter_transcripts ( min_x: float = 0.0, max_x: float = 24000.0, min_y: float = 0.0, - max_y: float = 24000.0 + max_y: float = 24000.0, + prefix: str = "" ) -> None: """ Filter transcripts based on the specified thresholds @@ -48,7 +49,7 @@ def filter_transcripts ( # Output filtered transcripts to parquet filtered_df.to_parquet( - '_'.join(["X"+str(min_x)+"-"+str(max_x), "Y"+str(min_y)+"-"+str(max_y), "filtered_transcripts.parquet"]), + '_'.join(["X"+str(min_x)+"-"+str(max_x), "Y"+str(min_y)+"-"+str(max_y), f"{prefix}/filtered_transcripts.parquet"]), index=False ) @@ -66,9 +67,11 @@ def generate_version_yml() -> None: if __name__ == "__main__": transcripts: str = "${transcripts}" + prefix: str = "${meta.id}" filter_transcripts ( transcripts=transcripts, + prefix=prefix ) generate_version_yml() diff --git a/modules/local/baysor/preview/main.nf b/modules/local/baysor/preview/main.nf index e6c2e67d..3b7582aa 100644 --- a/modules/local/baysor/preview/main.nf +++ b/modules/local/baysor/preview/main.nf @@ -9,9 +9,8 @@ process BAYSOR_PREVIEW { path(config) output: - tuple val(meta), path("preview.html"), emit: preview_html - path("preview_preview_log.log") , emit: preview_log - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/preview.html"), emit: preview_html + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -21,14 +20,20 @@ process BAYSOR_PREVIEW { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "BAYSOR_PREVIEW module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" """ + mkdir -p ${prefix} + baysor preview \\ ${transcripts} \\ --config ${config} \\ ${args} + mv preview.html ${prefix}/preview.html + cat <<-END_VERSIONS > versions.yml "${task.process}": baysor: 0.7.1 @@ -41,9 +46,11 @@ process BAYSOR_PREVIEW { error "BAYSOR_PREVIEW module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + """ - touch preview.html - touch preview_preview_log.log + mkdir -p ${prefix} + touch "${prefix}/preview.html" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/baysor/run/main.nf b/modules/local/baysor/run/main.nf index 455308e2..94c8adf3 100644 --- a/modules/local/baysor/run/main.nf +++ b/modules/local/baysor/run/main.nf @@ -5,21 +5,17 @@ process BAYSOR_RUN { container "khersameesh24/baysor:0.7.1" input: - tuple val(meta), path(transcripts) - path(prior_segmentation) - path(config) - val(scale) + tuple val(meta), + path(transcripts), + path(prior_segmentation), + path(config), + val(scale) output: - tuple val(meta), path("segmentation.csv"), emit: segmentation - path("segmentation_polygons_2d.json") , emit: polygons2d - path("segmentation_polygons_3d.json") , emit: polygons3d - path("*.toml") , emit: params - path("*.log") , emit: log - path("*.loom") , emit: loom - path("*.html") , emit: htmls - path("segmentation_cell_stats.csv") , emit: stats - path("versions.yml") , emit: versions + tuple val(meta), + path("${prefix}/segmentation.csv"), + path("${prefix}/segmentation_polygons_2d.json"), emit: segmentation + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -29,15 +25,20 @@ process BAYSOR_RUN { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "BAYSOR_RUN module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' def prior_seg = "${prior_segmentation}" ? "${prior_segmentation}" : "" def scaling_factor = scale ? "--scale=${scale}": "" + prefix = task.ext.prefix ?: "${meta.id}" """ + mkdir -p ${prefix} + baysor run \\ ${transcripts} \\ ${prior_seg} \\ ${scaling_factor} \\ + --output="${prefix}/segmentation.csv" \\ --config=${config} \\ --plot \\ --polygon-format=GeometryCollectionLegacy \\ @@ -55,15 +56,12 @@ process BAYSOR_RUN { error "BAYSOR_RUN module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + """ - touch segmentation.csv - touch segmentation_polygons_2d.json - touch segmentation_polygons_3d.json - touch segmentation_log.log - touch segmentation_counts.loom - touch segmentation_cell_stats.csv - touch segmentation_params.dump.toml - touch segmentation_run.html + mkdir -p ${prefix} + touch "${prefix}/segmentation.csv" + touch "${prefix}/segmentation_polygons_2d.json" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/baysor/segfree/main.nf b/modules/local/baysor/segfree/main.nf index 409c1c6c..62d4554e 100644 --- a/modules/local/baysor/segfree/main.nf +++ b/modules/local/baysor/segfree/main.nf @@ -9,9 +9,8 @@ process BAYSOR_SEGFREE { path(config) output: - tuple val(meta), path("ncvs.loom"), emit: ncvs - path("ncvs_segfree_log.log") , emit: ncvs_log - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/ncvs.loom"), emit: ncvs + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -21,12 +20,17 @@ process BAYSOR_SEGFREE { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "BAYSOR_SEGFREE module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" """ + mkdir -p ${prefix} + baysor segfree \\ ${transcripts} \\ --config ${config} \\ + --output=${prefix}/ncvs.loom \\ ${args} cat <<-END_VERSIONS > versions.yml @@ -41,9 +45,11 @@ process BAYSOR_SEGFREE { error "BAYSOR_SEGFREE module does not support Conda. Please use Docker / Singularity / Podman instead." } + prefix = task.ext.prefix ?: "${meta.id}" + """ - touch ncvs.loom - touch ncvs_segfree_log.log + mkdir -p ${prefix} + touch "${prefix}/ncvs.loom" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/proseg/preset/main.nf b/modules/local/proseg/preset/main.nf index 8cedfe82..3c7e1441 100644 --- a/modules/local/proseg/preset/main.nf +++ b/modules/local/proseg/preset/main.nf @@ -8,16 +8,10 @@ process PROSEG { tuple val(meta), path(transcripts) output: - tuple val(meta), path("cell-polygons.geojson.gz"), emit: cell_polygons_2d - path("transcript-metadata.csv.gz") , emit: transcript_metadata - path("expected-counts.csv.gz") , emit: expected_counts - path("cell-metadata.csv.gz") , emit: cell_metadata - path("gene-metadata.csv.gz") , emit: gene_metadata - path("rates.csv.gz") , emit: rates - path("cell-polygons-layers.geojson.gz") , emit: cell_polygons_layers - path("cell-hulls.geojson.gz") , emit: cell_hulls - path("union-cell-polygons.geojson.gz") , emit: union_cell_polygons - path("versions.yml") , emit: versions + tuple val(meta), + path("${prefix}/cell-polygons.geojson.gz"), + path("${prefix}/transcript-metadata.csv.gz"), emit: seg_outs + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -27,8 +21,9 @@ process PROSEG { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "PROSEG module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" // check for platform values if ( !(params.format in ['xenium', 'cosmx', 'merscope']) ) { @@ -36,18 +31,20 @@ process PROSEG { } """ + mkdir -p ${prefix} + proseg \\ --${params.format} \\ ${transcripts} \\ --nthreads ${task.cpus} \\ - --output-expected-counts expected-counts.csv.gz \\ - --output-cell-metadata cell-metadata.csv.gz \\ - --output-transcript-metadata transcript-metadata.csv.gz \\ - --output-gene-metadata gene-metadata.csv.gz \\ - --output-rates rates.csv.gz \\ - --output-cell-polygons cell-polygons.geojson.gz \\ - --output-cell-polygon-layers cell-polygons-layers.geojson.gz \\ - --output-cell-hulls cell-hulls.geojson.gz \\ + --output-expected-counts "${prefix}/expected-counts.csv.gz" \\ + --output-cell-metadata "${prefix}/cell-metadata.csv.gz" \\ + --output-transcript-metadata "${prefix}/transcript-metadata.csv.gz" \\ + --output-gene-metadata "${prefix}/gene-metadata.csv.gz" \\ + --output-rates "${prefix}/rates.csv.gz" \\ + --output-cell-polygons "${prefix}/cell-polygons.geojson.gz" \\ + --output-cell-polygon-layers "${prefix}/cell-polygons-layers.geojson.gz" \\ + --output-cell-hulls "${prefix}/cell-hulls.geojson.gz" \\ ${args} cat <<-END_VERSIONS > versions.yml @@ -61,19 +58,20 @@ process PROSEG { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "PROSEG module does not support Conda. Please use Docker / Singularity / Podman instead." } - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + + prefix = task.ext.prefix ?: "${meta.id}" """ - touch expected-counts.csv.gz - touch cell-metadata.csv.gz - touch transcript-metadata.csv.gz - touch gene-metadata.csv.gz - touch rates.csv.gz - touch cell-polygons.geojson.gz - touch cell-polygons-layers.geojson.gz - touch cell-hulls.geojson.gz - touch union-cell-polygons.geojson.gz + mkdir -p ${prefix}/ + touch "${prefix}/expected-counts.csv.gz" + touch "${prefix}/cell-metadata.csv.gz" + touch "${prefix}/transcript-metadata.csv.gz" + touch "${prefix}/gene-metadata.csv.gz" + touch "${prefix}/rates.csv.gz" + touch "${prefix}/cell-polygons.geojson.gz" + touch "${prefix}/cell-polygons-layers.geojson.gz" + touch "${prefix}/cell-hulls.geojson.gz" + touch "${prefix}/union-cell-polygons.geojson.gz" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/proseg/proseg2baysor/main.nf b/modules/local/proseg/proseg2baysor/main.nf index e841f966..d9a9b404 100644 --- a/modules/local/proseg/proseg2baysor/main.nf +++ b/modules/local/proseg/proseg2baysor/main.nf @@ -5,26 +5,31 @@ process PROSEG2BAYSOR { container "khersameesh24/proseg:2.0.0" input: - tuple val(meta), path(cell_polygons) - path(transcript_metadata) + tuple val(meta), path(cell_polygons), path(transcript_metadata) output: - tuple val(meta), path("xr-cell-polygons.geojson"), emit: xr_polygons - path("xr-transcript-metadata.csv") , emit: xr_metadata - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/cell-polygons.geojson") , emit: xr_polygons + tuple val(meta), path("${prefix}/transcript-metadata.csv"), emit: xr_metadata + path("versions.yml") , emit: versions script: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "PROSEG2BAYSOR (preprocess) module does not support Conda. Please use Docker / Singularity / Podman instead." + error "PROSEG2BAYSOR module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + """ - proseg-to-baysor \ - ${transcript_metadata} \ - ${cell_polygons} \ - --output-transcript-metadata xr-transcript-metadata.csv \ - --output-cell-polygons xr-cell-polygons.geojson + mkdir -p ${prefix} + + proseg-to-baysor \\ + ${transcript_metadata} \\ + ${cell_polygons} \\ + --output-transcript-metadata ${prefix}/transcript-metadata.csv \\ + --output-cell-polygons ${prefix}/cell-polygons.geojson \\ + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -36,14 +41,15 @@ process PROSEG2BAYSOR { stub: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "PROSEG module does not support Conda. Please use Docker / Singularity / Podman instead." + error "PROSEG2BAYSOR module does not support Conda. Please use Docker / Singularity / Podman instead." } - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + + prefix = task.ext.prefix ?: "${meta.id}" """ - touch xr-transcript-metadata.csv - touch xr-cell-polygons.geojson + mkdir -p ${prefix} + touch "${prefix}/transcript-metadata.csv" + touch "${prefix}/cell-polygons.geojson" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/resolift/main.nf b/modules/local/resolift/main.nf index c6faaf36..be75100a 100644 --- a/modules/local/resolift/main.nf +++ b/modules/local/resolift/main.nf @@ -5,11 +5,12 @@ process RESOLIFT { container "quay.io/khersameesh24/resolift:1.0.0" input: - tuple val(meta), path(input) + tuple val(meta), path(morphology_tiff) output: - tuple val(meta), path("*.tiff"), emit: enhanced_tiff - path("versions.yml") , emit: versions + tuple val(meta), + path("${prefix}/morphology.ome.enhanced.tiff"), emit: enhanced_tiff + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -17,40 +18,40 @@ process RESOLIFT { script: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "ResoLift module does not support Conda. Please use Docker / Singularity / Podman instead." + error "RESOLIFT module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - if ("$input" == "${prefix}.tiff") error "Input and output names are the same, set prefix in module configuration to disambiguate!" - def VERSION = '1.0.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + prefix = task.ext.prefix ?: "${meta.id}" """ + mkdir -p ${prefix} + resolift \\ - -i $input \\ - -o ${prefix}.tiff \\ + -i ${morphology_tiff} \\ + -o ${prefix}/morphology.ome.enhanced.tiff \\ ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": - resolift: ${VERSION} + resolift: v1.0.0 END_VERSIONS """ stub: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "ResoLift module does not support Conda. Please use Docker / Singularity / Podman instead." + error "RESOLIFT module does not support Conda. Please use Docker / Singularity / Podman instead." } - def prefix = task.ext.prefix ?: "${meta.id}" - if ("$input" == "${prefix}.tiff") error "Input and output names are the same, set prefix in module configuration to disambiguate!" - def VERSION = '1.0.0' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + prefix = task.ext.prefix ?: "${meta.id}" """ - touch ${prefix}.tiff + mkdir -p ${prefix} + touch "${prefix}/morphology.ome.enhanced.tiff" cat <<-END_VERSIONS > versions.yml "${task.process}": - resolift: ${VERSION} + resolift: v1.0.0 END_VERSIONS """ } diff --git a/modules/local/segger/create_dataset/main.nf b/modules/local/segger/create_dataset/main.nf index c2d5c3a6..29057344 100644 --- a/modules/local/segger/create_dataset/main.nf +++ b/modules/local/segger/create_dataset/main.nf @@ -8,24 +8,25 @@ process SEGGER_CREATE_DATASET { tuple val(meta), path(base_dir) output: - tuple val(meta), path("${meta.id}") , emit: datasetdir - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/"), emit: datasetdir + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "SEGGER_CREATE_DATASET module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" def script_path = "/workspace/segger_dev/src/segger/cli/create_dataset_fast.py" + prefix = task.ext.prefix ?: "${meta.id}" // check for platform values if ( !(params.format in ['xenium']) ) { - error "${params.format} is an invalid platform type. Please specify xenium, cosmx, or merscope" + error "${params.format} is an invalid platform type." } """ @@ -45,9 +46,16 @@ process SEGGER_CREATE_DATASET { """ stub: - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "SEGGER_CREATE_DATASET module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" + """ mkdir -p ${prefix}/ + touch "${prefix}/fake_file.txt" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/segger/predict/main.nf b/modules/local/segger/predict/main.nf index ca5c0e2f..3e08a803 100644 --- a/modules/local/segger/predict/main.nf +++ b/modules/local/segger/predict/main.nf @@ -9,30 +9,30 @@ process SEGGER_PREDICT { path(models_dir) path(transcripts) - output: - tuple val(meta), path("${meta.id}_benchmarks_dir") , emit: benchmarks - tuple val(meta), path("${meta.id}_benchmarks_dir/*/segger_transcripts.parquet"), emit: transcripts - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/benchmarks_dir") , emit: benchmarks + tuple val(meta), path("${prefix}/benchmarks_dir/*/segger_transcripts.parquet"), emit: transcripts + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "SEGGER_PREDICT module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" def script_path = "/workspace/segger_dev/src/segger/cli/predict_fast.py" + prefix = task.ext.prefix ?: "${meta.id}" """ python3 ${script_path} \\ --models_dir ${models_dir} \\ --segger_data_dir ${segger_dataset} \\ --transcripts_file ${transcripts} \\ - --benchmarks_dir ${prefix}_benchmarks_dir \\ + --benchmarks_dir ${prefix}/benchmarks_dir \\ --batch_size ${params.batch_size_predict} \\ --use_cc ${params.cc_analysis} \\ --knn_method ${params.segger_knn_method} \\ @@ -46,9 +46,16 @@ process SEGGER_PREDICT { """ stub: - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "SEGGER_PREDICT module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p ${prefix}_benchmarks_dir/ + mkdir -p "${prefix}/benchmarks_dir" + touch "${prefix}/fake_file.txt" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/segger/train/main.nf b/modules/local/segger/train/main.nf index 18d4dc0d..70448258 100644 --- a/modules/local/segger/train/main.nf +++ b/modules/local/segger/train/main.nf @@ -8,25 +8,26 @@ process SEGGER_TRAIN { tuple val(meta), path(dataset_dir) output: - tuple val(meta), path("${meta.id}_trained_models"), emit: trained_models - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/trained_models"), emit: trained_models + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "SEGGER_TRAIN module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" def script_path = "/workspace/segger_dev/src/segger/cli/train_model.py" + prefix = task.ext.prefix ?: "${meta.id}" """ python3 ${script_path} \\ --dataset_dir ${dataset_dir} \\ - --models_dir ${prefix}_trained_models \\ + --models_dir ${prefix}/trained_models \\ --sample_tag ${prefix} \\ --batch_size ${params.batch_size_train} \\ --max_epochs ${params.max_epochs} \\ @@ -42,10 +43,16 @@ process SEGGER_TRAIN { """ stub: - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "SEGGER_TRAIN module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p ${prefix}_trained_models/ - touch ${prefix}_trained_models/fakefile.txt + mkdir -p ${prefix}/trained_models/ + touch ${prefix}/trained_models/fakefile.txt cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/spatialdata/merge/main.nf b/modules/local/spatialdata/merge/main.nf index 8f524734..fc552b75 100644 --- a/modules/local/spatialdata/merge/main.nf +++ b/modules/local/spatialdata/merge/main.nf @@ -5,12 +5,11 @@ process SPATIALDATA_MERGE { container "heylf/spatialdata:0.2.6" input: - tuple val(meta), path(ref_bundle, stageAs: "*") - path(add_bundle, stageAs: "*") + tuple val(meta), path(raw_bundle, stageAs: "*"), path(redefined_bundle, stageAs: "*") output: - tuple val(meta), path("spatialdata_merged"), emit: merged_bundle - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/spatialdata_merged"), emit: merged_bundle + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -22,13 +21,21 @@ process SPATIALDATA_MERGE { } def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" template 'merge.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + exit 1, "SPATIALDATA_WRITE module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p "spatialdata_merged/" - touch spatialdata_merged/fake_file.txt + mkdir -p "${prefix}/spatialdata_merged/" + touch "${prefix}/spatialdata_merged/fake_file.txt" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/spatialdata/merge/templates/merge.py b/modules/local/spatialdata/merge/templates/merge.py index 6bf908ce..4802b40a 100755 --- a/modules/local/spatialdata/merge/templates/merge.py +++ b/modules/local/spatialdata/merge/templates/merge.py @@ -10,26 +10,27 @@ def main(): print("[START]") - reference_bundle = "${ref_bundle}" - add_bundle = "${add_bundle}" - output_folder = "./spatialdata_merged" + raw_bundle = "${raw_bundle}" + redefined_bundle = "${redefined_bundle}" + output_path = "${meta.id}" + output_folder = "spatialdata_merged" # Ensure the output folder exists - if os.path.exists(output_folder): - shutil.rmtree(output_folder) - os.makedirs(output_folder) + if os.path.exists(f"{output_path}/{output_folder}"): + shutil.rmtree(f"{output_path}/{output_folder}") + os.makedirs(f"{output_path}/{output_folder}") # Copy the entire reference bundle as is - for root, _, files in os.walk(reference_bundle): - rel_path = os.path.relpath(root, reference_bundle) - target_path = os.path.join(output_folder, rel_path) + for root, _, files in os.walk(raw_bundle): + rel_path = os.path.relpath(root, raw_bundle) + target_path = os.path.join(f"{output_path}/{output_folder}", rel_path) os.makedirs(target_path, exist_ok=True) for file in files: shutil.copy(os.path.join(root, file), os.path.join(target_path, file)) # Rename folders in Points, Shapes, and Tables to raw_* for category in ["points", "shapes", "tables"]: - category_path = os.path.join(output_folder, category) + category_path = os.path.join(f"{output_path}/{output_folder}", category) if os.path.exists(category_path): for folder in next(os.walk(category_path))[1]: #os.listdir(category_path): old_path = os.path.join(category_path, folder) @@ -37,16 +38,16 @@ def main(): new_path = os.path.join(category_path, f"raw_{folder}") os.rename(old_path, new_path) - # Copy folders from add_bundle and rename them as reference_* + # Copy folders from redefined_bundle and rename them as redefined_* for category in ["points", "shapes", "tables"]: - add_category_path = os.path.join(add_bundle, category) - output_category_path = os.path.join(output_folder, category) + add_category_path = os.path.join(redefined_bundle, category) + output_category_path = os.path.join(f"{output_path}/{output_folder}", category) os.makedirs(output_category_path, exist_ok=True) if os.path.exists(add_category_path): for folder in next(os.walk(add_category_path))[1]: src_folder = os.path.join(add_category_path, folder) - dest_folder = os.path.join(output_category_path, f"reference_{folder}") + dest_folder = os.path.join(output_category_path, f"redefined_{folder}") shutil.copytree(src_folder, dest_folder) #Output version information diff --git a/modules/local/spatialdata/meta/main.nf b/modules/local/spatialdata/meta/main.nf index ac3c9278..b197bc1c 100644 --- a/modules/local/spatialdata/meta/main.nf +++ b/modules/local/spatialdata/meta/main.nf @@ -5,12 +5,11 @@ process SPATIALDATA_META { container "heylf/spatialdata:0.2.6" input: - tuple val(meta), path(spatialdata_bundle, stageAs: "*") - path(xenium_bundle, stageAs: "*") + tuple val(meta), path(spatialdata_bundle, stageAs: "*"), path(xenium_bundle, stageAs: "*") output: - tuple val(meta), path("spatialdata_meta"), emit: metadata - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/spatialdata_meta"), emit: metadata + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -21,15 +20,21 @@ process SPATIALDATA_META { exit 1, "SPATIALDATA_META module does not support Conda. Please use Docker / Singularity / Podman instead." } - def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" template 'meta.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + exit 1, "SPATIALDATA_META module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" """ - mkdir -p "spatialdata_meta/" - touch "spatialdata_meta/fake_file.txt" + mkdir -p "${prefix}/spatialdata_meta/" + touch "${prefix}/spatialdata_meta/fake_file.txt" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/spatialdata/meta/templates/meta.py b/modules/local/spatialdata/meta/templates/meta.py index 5c181505..fca9ac88 100755 --- a/modules/local/spatialdata/meta/templates/meta.py +++ b/modules/local/spatialdata/meta/templates/meta.py @@ -11,6 +11,7 @@ def main(): spatialdata_bundle = "${spatialdata_bundle}" xenium_bundle = "${xenium_bundle}" + output_path = "${meta.id}" metadata = "${meta}" output = "spatialdata_meta" @@ -44,7 +45,7 @@ def main(): metadata_gene_panel = json.load(f) sdata['raw_table'].uns['gene_panel'] = json.dumps(metadata_gene_panel) - sdata.write(f"./{output}", overwrite=True, consolidate_metadata=True, format=None) + sdata.write(f"{output_path}/{output}", overwrite=True, consolidate_metadata=True, format=None) #Output version information with open("versions.yml", "w") as f: diff --git a/modules/local/spatialdata/write/main.nf b/modules/local/spatialdata/write/main.nf index f9bac411..7816ba68 100644 --- a/modules/local/spatialdata/write/main.nf +++ b/modules/local/spatialdata/write/main.nf @@ -11,8 +11,8 @@ process SPATIALDATA_WRITE { val(coordinate_space) output: - tuple val(meta), path("${outputfolder}"), emit: spatialdata - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/${outputfolder}"), emit: spatialdata + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -23,16 +23,22 @@ process SPATIALDATA_WRITE { exit 1, "SPATIALDATA_WRITE module does not support Conda. Please use Docker / Singularity / Podman instead." } - def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" template 'write.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + exit 1, "SPATIALDATA_WRITE module does not support Conda. Please use Docker / Singularity / Podman instead." + } def outdir = "${outputfolder}" + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p "${outdir}/" - touch "${outdir}/fake_file.txt" + mkdir -p "${prefix}/${outdir}" + touch "${prefix}/${outdir}/fake_file.txt" cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/spatialdata/write/templates/write.py b/modules/local/spatialdata/write/templates/write.py index dd9e9d1e..ca70dba8 100755 --- a/modules/local/spatialdata/write/templates/write.py +++ b/modules/local/spatialdata/write/templates/write.py @@ -10,7 +10,7 @@ def main(): print("[START]") input_path = "${bundle}" - output_path = "." + output_path = "${meta.id}" outputfolder = "${outputfolder}" segmented_object = "${segmented_object}" coordinate_space = "${coordinate_space}" diff --git a/modules/local/utility/Dockerfile b/modules/local/utility/Dockerfile new file mode 100644 index 00000000..79213ebb --- /dev/null +++ b/modules/local/utility/Dockerfile @@ -0,0 +1,11 @@ +FROM mambaorg/micromamba:1.5.10-noble +COPY --chown=$MAMBA_USER:$MAMBA_USER conda.yml /tmp/conda.yml +RUN micromamba install -y -n base -f /tmp/conda.yml \ + && micromamba install -y -n base conda-forge::procps-ng \ + && micromamba env export --name base --explicit > environment.lock \ + && echo ">> CONDA_LOCK_START" \ + && cat environment.lock \ + && echo "<< CONDA_LOCK_END" \ + && micromamba clean -a -y +USER root +ENV PATH="$MAMBA_ROOT_PREFIX/bin:$PATH" diff --git a/modules/local/utility/segger2xr/main.nf b/modules/local/utility/segger2xr/main.nf index cba9173d..15e50835 100644 --- a/modules/local/utility/segger2xr/main.nf +++ b/modules/local/utility/segger2xr/main.nf @@ -2,19 +2,20 @@ process SEGGER2XR { tag "$meta.id" label 'process_low' - container "ghcr.io/scverse/spatialdata:spatialdata0.3.0_spatialdata-io0.1.7_spatialdata-plot0.2.9" + container "community.wave.seqera.io/library/pip_pandas:5c59aaec7d5d4750" input: tuple val(meta), path(transcripts) output: - tuple val(meta), path("transcripts.parquet"), emit: transcripts_parquet - path("versions.yml") , emit: versions + tuple val(meta), path("${meta.id}/transcripts.parquet"), emit: transcripts_parquet + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "SEGGER2XR module does not support Conda. Please use Docker / Singularity / Podman instead." } @@ -22,8 +23,17 @@ process SEGGER2XR { template 'segger2xr.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "SEGGER2XR module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + def prefix = task.ext.prefix ?: "${meta.id}" + """ - touch ${transcripts}.parquet + mkdir -p ${prefix} + touch "${prefix}/transcripts.parquet" + cat <<-END_VERSIONS > versions.yml "${task.process}": segger2xr: "${task.version}" diff --git a/modules/local/utility/segger2xr/templates/segger2xr.py b/modules/local/utility/segger2xr/templates/segger2xr.py index cffd9f07..f34c3291 100644 --- a/modules/local/utility/segger2xr/templates/segger2xr.py +++ b/modules/local/utility/segger2xr/templates/segger2xr.py @@ -44,16 +44,17 @@ def refine_transcripts(parquet_path: str) -> pd.DataFrame: return df -def main(input_file: str) -> None: +def main(input_file: str, prefix: str) -> None: transcripts = refine_transcripts(input_file) - transcripts.to_parquet("transcripts.parquet", engine="pyarrow") + transcripts.to_parquet(f"{prefix}/transcripts.parquet", engine="pyarrow") if __name__ == "__main__": - transcripts = "${transcripts}" + transcripts: str = "${transcripts}" + prefix: str = "${meta.id}" - main(input_file=transcripts) + main(input_file=transcripts, prefix=prefix) #Output versions.yml with open("versions.yml", "w") as f: diff --git a/modules/local/utility/spatialconverter/parquet_to_csv/main.nf b/modules/local/utility/spatialconverter/parquet_to_csv/main.nf index a18019cd..1637717e 100644 --- a/modules/local/utility/spatialconverter/parquet_to_csv/main.nf +++ b/modules/local/utility/spatialconverter/parquet_to_csv/main.nf @@ -2,29 +2,40 @@ process PARQUET_TO_CSV { tag "$meta.id" label 'process_low' - container "heylf/spatialdata:0.2.6" + container "community.wave.seqera.io/library/pip_pandas:5c59aaec7d5d4750" input: tuple val(meta), path(transcripts) val(extension) output: - tuple val(meta), path("*.csv*"), emit: transcripts_csv - path("versions.yml") , emit: versions + tuple val(meta), path("${meta.id}/*.csv*"), emit: transcripts_csv + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "PARQUET_TO_CSV module does not support Conda. Please use Docker / Singularity / Podman instead." } + def prefix = task.ext.prefix ?: "${meta.id}" template 'parquet_to_csv.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "PARQUET_TO_CSV module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + def prefix = task.ext.prefix ?: "${meta.id}" + """ - touch ${transcripts}.csv + mkdir -p ${prefix} + touch "${prefix}/${transcripts}.csv" + cat <<-END_VERSIONS > versions.yml "${task.process}": spatialconverter: "${task.version}" diff --git a/modules/local/utility/spatialconverter/parquet_to_csv/templates/parquet_to_csv.py b/modules/local/utility/spatialconverter/parquet_to_csv/templates/parquet_to_csv.py index f13ba23d..9ca97dcb 100755 --- a/modules/local/utility/spatialconverter/parquet_to_csv/templates/parquet_to_csv.py +++ b/modules/local/utility/spatialconverter/parquet_to_csv/templates/parquet_to_csv.py @@ -6,17 +6,20 @@ def convert_parquet ( transcripts: Path, - extension: str = '.csv' + extension: str = '.csv', + prefix: str = "" ) -> None: df = pd.read_parquet(transcripts, engine = 'pyarrow') + prefix.mkdir(parents=True, exist_ok=True) + if extension == ".gz": output = transcripts.replace(".parquet", ".csv.gz") - df.to_csv(f"{output}", compression='gzip', index=False) + df.to_csv(f"{prefix}/{output}", compression='gzip', index=False) else: output = transcripts.replace(".parquet", ".csv") - df.to_csv(f"{output}", index=False) + df.to_csv(f"{prefix}/{output}", index=False) return None @@ -25,11 +28,13 @@ def convert_parquet ( transcripts: str = "${transcripts}" extension: str = "${extension}" + prefix: str = "${meta.id}" # generate transcripts.csv(.gz) convert_parquet ( transcripts=transcripts, - extension=extension + extension=extension, + prefix=prefix ) #Output versions.yml diff --git a/modules/local/utility/split_transcripts/main.nf b/modules/local/utility/split_transcripts/main.nf index f24773d4..5f89d9d1 100644 --- a/modules/local/utility/split_transcripts/main.nf +++ b/modules/local/utility/split_transcripts/main.nf @@ -2,7 +2,7 @@ process SPLIT_TRANSCRIPTS { tag "$meta.id" label 'process_low' - container "ghcr.io/scverse/spatialdata:spatialdata0.3.0_spatialdata-io0.1.7_spatialdata-plot0.2.9" + container "community.wave.seqera.io/library/pip_pandas:5c59aaec7d5d4750" input: tuple val(meta), path(transcripts) @@ -10,22 +10,31 @@ process SPLIT_TRANSCRIPTS { val(y_bins) output: - tuple val(meta), path("splits.csv"), emit: splits_csv - path("versions.yml") , emit: versions + tuple val(meta), path("${meta.id}/splits.csv"), emit: splits_csv + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when script: + // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "SPLIT_TRANSCRIPTS module does not support Conda. Please use Docker / Singularity / Podman instead." } + def prefix = task.ext.prefix ?: "${meta.id}" template 'split_transcripts.py' stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "SPLIT_TRANSCRIPTS module does not support Conda. Please use Docker / Singularity / Podman instead." + } + def prefix = task.ext.prefix ?: "${meta.id}" """ - touch ${transcripts}.parquet + mkdir -p ${prefix} + touch "${prefix}/${transcripts}.parquet" + cat <<-END_VERSIONS > versions.yml "${task.process}": baysor_split_parquet: "1.0.0" diff --git a/modules/local/utility/split_transcripts/templates/split_transcripts.py b/modules/local/utility/split_transcripts/templates/split_transcripts.py index 9392b16f..dea42502 100644 --- a/modules/local/utility/split_transcripts/templates/split_transcripts.py +++ b/modules/local/utility/split_transcripts/templates/split_transcripts.py @@ -49,7 +49,8 @@ def generate_version_yml() -> None: def main( transcripts: str, x_bins: int = 10, - y_bins: int = 10 + y_bins: int = 10, + prefix: str = "" ) -> None: """ Generate splits @@ -61,7 +62,7 @@ def main( tiles_df = make_tiles(df, x_bins, y_bins) # save parquet file - tiles_df.to_csv("splits.csv", index=False) + tiles_df.to_csv(f"{prefix}/splits.csv", index=False) # generate version yml generate_version_yml() @@ -74,5 +75,6 @@ def main( transcripts: str = "${transcripts}" x_bins: int = "${x_bins}" y_bins: int = "${y_bins}" + prefix: str = "${prefix}" main(transcripts=transcripts, x_bins=x_bins, y_bins=y_bins) diff --git a/modules/nf-core/cellpose/cellpose.diff b/modules/nf-core/cellpose/cellpose.diff index 0267f42c..b4f73e87 100644 --- a/modules/nf-core/cellpose/cellpose.diff +++ b/modules/nf-core/cellpose/cellpose.diff @@ -2,7 +2,7 @@ Changes in component 'nf-core/cellpose' Changes in 'cellpose/main.nf': --- modules/nf-core/cellpose/main.nf +++ modules/nf-core/cellpose/main.nf -@@ -6,11 +6,13 @@ +@@ -6,12 +6,14 @@ input: tuple val(meta), path(image) @@ -11,23 +11,72 @@ Changes in 'cellpose/main.nf': + val(maskname) output: - tuple val(meta), path("*masks.tif") , emit: mask - tuple val(meta), path("*flows.tif") , emit: flows, optional: true -+ tuple val(meta), path("*seg.npy") , emit: cells, optional: true - path "versions.yml" , emit: versions +- tuple val(meta), path("*masks.tif") , emit: mask +- tuple val(meta), path("*flows.tif") , emit: flows, optional: true +- path "versions.yml" , emit: versions ++ tuple val(meta), path("${prefix}/*masks.tif"), emit: mask ++ tuple val(meta), path("${prefix}/*flows.tif"), emit: flows, optional: true ++ tuple val(meta), path("${prefix}/*seg.npy") , emit: cells, optional: true ++ path "versions.yml" , emit: versions when: -@@ -32,6 +34,7 @@ + task.ext.when == null || task.ext.when +@@ -19,37 +21,47 @@ + script: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { +- error "I did not manage to create a cellpose module in Conda that works in all OSes. Please use Docker / Singularity / Podman instead." ++ error "CELLPOSE module does not support conda. Please use Docker / Singularity / Podman instead." + } + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" + def model_command = model ? "--pretrained_model $model" : "" ++ prefix = task.ext.prefix ?: "${meta.id}" + """ + export OMP_NUM_THREADS=${task.cpus} + export MKL_NUM_THREADS=${task.cpus} ++ export NPY_PROMOTION_STATE=legacy + cellpose \\ + --image_path $image \\ --save_tif \\ $model_command \\ $args -+ mv *masks.tif morphology.ome_${maskname}_masks.tif ++ mkdir -p ${prefix} ++ mv *masks.tif ${prefix}/morphology.ome_${maskname}_masks.tif ++ cat <<-END_VERSIONS > versions.yml "${task.process}": + cellpose: \$(cellpose --version | awk 'NR==2 {print \$3}') + END_VERSIONS + """ ++ + stub: + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { +- error "I did not manage to create a cellpose module in Conda that works in all OSes. Please use Docker / Singularity / Podman instead." ++ error "CELLPOSE module does not support conda. Please use Docker / Singularity / Podman instead." + } +- def prefix = task.ext.prefix ?: "${meta.id}" ++ + def name = image.name + def base = name.lastIndexOf('.') != -1 ? name[0..name.lastIndexOf('.') - 1] : name ++ prefix = task.ext.prefix ?: "${meta.id}" ++ + """ ++ mkdir -p ${prefix} ++ touch ${prefix}/morphology.ome_${maskname}_masks.tif ++ touch ${prefix}/morphology.ome_${maskname}_seg.npy + touch ${base}_cp_masks.tif + +- cat <<-END_VERSIONS > versions.yml ++ cat <<-END_VERSIONS > versions.yml + "${task.process}": + cellpose: \$(cellpose --version | awk 'NR==2 {print \$3}') + END_VERSIONS 'modules/nf-core/cellpose/meta.yml' is unchanged 'modules/nf-core/cellpose/tests/main.nf.test' is unchanged -'modules/nf-core/cellpose/tests/nextflow_wflows.config' is unchanged 'modules/nf-core/cellpose/tests/main.nf.test.snap' is unchanged +'modules/nf-core/cellpose/tests/nextflow_wflows.config' is unchanged ************************************************************ diff --git a/modules/nf-core/cellpose/main.nf b/modules/nf-core/cellpose/main.nf index e19bd00f..272f3475 100644 --- a/modules/nf-core/cellpose/main.nf +++ b/modules/nf-core/cellpose/main.nf @@ -10,10 +10,10 @@ process CELLPOSE { val(maskname) output: - tuple val(meta), path("*masks.tif") , emit: mask - tuple val(meta), path("*flows.tif") , emit: flows, optional: true - tuple val(meta), path("*seg.npy") , emit: cells, optional: true - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}/*masks.tif"), emit: mask + tuple val(meta), path("${prefix}/*flows.tif"), emit: flows, optional: true + tuple val(meta), path("${prefix}/*seg.npy") , emit: cells, optional: true + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -21,38 +21,47 @@ process CELLPOSE { script: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "I did not manage to create a cellpose module in Conda that works in all OSes. Please use Docker / Singularity / Podman instead." + error "CELLPOSE module does not support conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" def model_command = model ? "--pretrained_model $model" : "" + prefix = task.ext.prefix ?: "${meta.id}" """ export OMP_NUM_THREADS=${task.cpus} export MKL_NUM_THREADS=${task.cpus} + export NPY_PROMOTION_STATE=legacy cellpose \\ --image_path $image \\ --save_tif \\ $model_command \\ $args - mv *masks.tif morphology.ome_${maskname}_masks.tif + + mkdir -p ${prefix} + mv *masks.tif ${prefix}/morphology.ome_${maskname}_masks.tif cat <<-END_VERSIONS > versions.yml "${task.process}": cellpose: \$(cellpose --version | awk 'NR==2 {print \$3}') END_VERSIONS """ + stub: // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "I did not manage to create a cellpose module in Conda that works in all OSes. Please use Docker / Singularity / Podman instead." + error "CELLPOSE module does not support conda. Please use Docker / Singularity / Podman instead." } - def prefix = task.ext.prefix ?: "${meta.id}" + def name = image.name def base = name.lastIndexOf('.') != -1 ? name[0..name.lastIndexOf('.') - 1] : name + prefix = task.ext.prefix ?: "${meta.id}" + """ + mkdir -p ${prefix} + touch ${prefix}/morphology.ome_${maskname}_masks.tif + touch ${prefix}/morphology.ome_${maskname}_seg.npy touch ${base}_cp_masks.tif - cat <<-END_VERSIONS > versions.yml + cat <<-END_VERSIONS > versions.yml "${task.process}": cellpose: \$(cellpose --version | awk 'NR==2 {print \$3}') END_VERSIONS diff --git a/modules/nf-core/opt/flip/main.nf b/modules/nf-core/opt/flip/main.nf index fa98c4f0..66be07d0 100644 --- a/modules/nf-core/opt/flip/main.nf +++ b/modules/nf-core/opt/flip/main.nf @@ -9,8 +9,8 @@ process OPT_FLIP { tuple val(meta2), path(ref_annot_gff), path(ref_annot_fa) output: - tuple val(meta), path("${meta.id}/fwd_oriented.fa"), emit: fwd_oriented_fa - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}/fwd_oriented.fa"), emit: fwd_oriented_fa + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -20,8 +20,9 @@ process OPT_FLIP { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "OPT_FLIP module does not support Conda. Please use Docker / Singularity / Podman instead." } + def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" """ opt \\ @@ -40,8 +41,12 @@ process OPT_FLIP { """ stub: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "OPT_FLIP module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" """ mkdir -p ${prefix} diff --git a/modules/nf-core/opt/flip/opt-flip.diff b/modules/nf-core/opt/flip/opt-flip.diff new file mode 100644 index 00000000..f1ac73fb --- /dev/null +++ b/modules/nf-core/opt/flip/opt-flip.diff @@ -0,0 +1,46 @@ +Changes in component 'nf-core/opt/flip' +Changes in 'opt/flip/main.nf': +--- modules/nf-core/opt/flip/main.nf ++++ modules/nf-core/opt/flip/main.nf +@@ -9,8 +9,8 @@ + tuple val(meta2), path(ref_annot_gff), path(ref_annot_fa) + + output: +- tuple val(meta), path("${meta.id}/fwd_oriented.fa"), emit: fwd_oriented_fa +- path "versions.yml" , emit: versions ++ tuple val(meta), path("${prefix}/fwd_oriented.fa"), emit: fwd_oriented_fa ++ path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when +@@ -20,8 +20,9 @@ + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "OPT_FLIP module does not support Conda. Please use Docker / Singularity / Podman instead." + } ++ + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + opt \\ +@@ -40,8 +41,12 @@ + """ + + stub: +- def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ // Exit if running this module with -profile conda / -profile mamba ++ if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { ++ error "OPT_FLIP module does not support Conda. Please use Docker / Singularity / Podman instead." ++ } ++ ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + mkdir -p ${prefix} + +'modules/nf-core/opt/flip/meta.yml' is unchanged +'modules/nf-core/opt/flip/tests/main.nf.test' is unchanged +'modules/nf-core/opt/flip/tests/main.nf.test.snap' is unchanged +************************************************************ diff --git a/modules/nf-core/opt/stat/main.nf b/modules/nf-core/opt/stat/main.nf index b4e2875e..e8de5860 100644 --- a/modules/nf-core/opt/stat/main.nf +++ b/modules/nf-core/opt/stat/main.nf @@ -10,8 +10,8 @@ process OPT_STAT { path(gene_synonyms) output: - tuple val(meta), path("${meta.id}/collapsed_summary.tsv"), emit: summary - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}/collapsed_summary.tsv"), emit: summary + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -22,8 +22,8 @@ process OPT_STAT { error "OPT_STAT module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" def synonyms = gene_synonyms ? "-s ${gene_synonyms}": "" + prefix = task.ext.prefix ?: "${meta.id}" """ opt \\ @@ -41,8 +41,12 @@ process OPT_STAT { """ stub: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "OPT_STAT module does not support Conda. Please use Docker / Singularity / Podman instead." + } + + prefix = task.ext.prefix ?: "${meta.id}" """ mkdir -p ${prefix} diff --git a/modules/nf-core/opt/stat/opt-stat.diff b/modules/nf-core/opt/stat/opt-stat.diff new file mode 100644 index 00000000..63a743ba --- /dev/null +++ b/modules/nf-core/opt/stat/opt-stat.diff @@ -0,0 +1,45 @@ +Changes in component 'nf-core/opt/stat' +Changes in 'opt/stat/main.nf': +--- modules/nf-core/opt/stat/main.nf ++++ modules/nf-core/opt/stat/main.nf +@@ -10,8 +10,8 @@ + path(gene_synonyms) + + output: +- tuple val(meta), path("${meta.id}/collapsed_summary.tsv"), emit: summary +- path "versions.yml" , emit: versions ++ tuple val(meta), path("${prefix}/collapsed_summary.tsv"), emit: summary ++ path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when +@@ -22,8 +22,8 @@ + error "OPT_STAT module does not support Conda. Please use Docker / Singularity / Podman instead." + } + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" + def synonyms = gene_synonyms ? "-s ${gene_synonyms}": "" ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + opt \\ +@@ -41,8 +41,12 @@ + """ + + stub: +- def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ // Exit if running this module with -profile conda / -profile mamba ++ if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { ++ error "OPT_STAT module does not support Conda. Please use Docker / Singularity / Podman instead." ++ } ++ ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + mkdir -p ${prefix} + +'modules/nf-core/opt/stat/meta.yml' is unchanged +'modules/nf-core/opt/stat/tests/main.nf.test' is unchanged +'modules/nf-core/opt/stat/tests/main.nf.test.snap' is unchanged +************************************************************ diff --git a/modules/nf-core/opt/track/main.nf b/modules/nf-core/opt/track/main.nf index fc99a3e5..ff92645e 100644 --- a/modules/nf-core/opt/track/main.nf +++ b/modules/nf-core/opt/track/main.nf @@ -9,8 +9,8 @@ process OPT_TRACK { tuple val(meta2), path(ref_annot_gff), path(ref_annot_fa) output: - tuple val(meta), path("${meta.id}/probe2targets.tsv"), emit: probes2target - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}/probe2targets.tsv"), emit: probes2target + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -21,7 +21,7 @@ process OPT_TRACK { error "OPT_TRACK module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" """ opt \\ @@ -41,8 +41,11 @@ process OPT_TRACK { """ stub: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + // Exit if running this module with -profile conda / -profile mamba + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "OPT_TRACK module does not support Conda. Please use Docker / Singularity / Podman instead." + } + prefix = task.ext.prefix ?: "${meta.id}" """ mkdir -p ${prefix} diff --git a/modules/nf-core/opt/track/opt-track.diff b/modules/nf-core/opt/track/opt-track.diff new file mode 100644 index 00000000..5f8d9420 --- /dev/null +++ b/modules/nf-core/opt/track/opt-track.diff @@ -0,0 +1,43 @@ +Changes in component 'nf-core/opt/track' +Changes in 'opt/track/main.nf': +--- modules/nf-core/opt/track/main.nf ++++ modules/nf-core/opt/track/main.nf +@@ -9,8 +9,8 @@ + tuple val(meta2), path(ref_annot_gff), path(ref_annot_fa) + + output: +- tuple val(meta), path("${meta.id}/probe2targets.tsv"), emit: probes2target +- path "versions.yml" , emit: versions ++ tuple val(meta), path("${prefix}/probe2targets.tsv"), emit: probes2target ++ path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when +@@ -21,7 +21,7 @@ + error "OPT_TRACK module does not support Conda. Please use Docker / Singularity / Podman instead." + } + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + opt \\ +@@ -41,8 +41,11 @@ + """ + + stub: +- def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ // Exit if running this module with -profile conda / -profile mamba ++ if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { ++ error "OPT_TRACK module does not support Conda. Please use Docker / Singularity / Podman instead." ++ } ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + mkdir -p ${prefix} + +'modules/nf-core/opt/track/meta.yml' is unchanged +'modules/nf-core/opt/track/tests/main.nf.test' is unchanged +'modules/nf-core/opt/track/tests/main.nf.test.snap' is unchanged +************************************************************ diff --git a/modules/nf-core/xeniumranger/import-segmentation/main.nf b/modules/nf-core/xeniumranger/import-segmentation/main.nf index 1dc7702b..985a709e 100644 --- a/modules/nf-core/xeniumranger/import-segmentation/main.nf +++ b/modules/nf-core/xeniumranger/import-segmentation/main.nf @@ -5,17 +5,18 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { container "nf-core/xeniumranger:3.1.1" input: - tuple val(meta), path(xenium_bundle) - path(coordinate_transform) - path(nuclei) - path(cells) - path(transcript_assignment) - path(viz_polygons) - val(units) + tuple val(meta), + path(xenium_bundle), + path(coordinate_transform), + path(nuclei), + path(cells), + path(transcript_assignment), + path(viz_polygons), + val(units) output: - tuple val(meta), path("${meta.id}/outs"), emit: bundle - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/outs"), emit: bundle + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -26,7 +27,7 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { error "XENIUMRANGER_IMPORT-SEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" // image based segmentation options def coord_transform = coordinate_transform ? "--coordinate-transform=\"${coordinate_transform}\"": "" @@ -69,9 +70,11 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "XENIUMRANGER_IMPORT-SEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." } - def prefix = task.ext.prefix ?: "${meta.id}" + + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p "${prefix}/outs/" + mkdir -p "${prefix}/outs" touch "${prefix}/outs/fake_file.txt" cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/xeniumranger/import-segmentation/xeniumranger-import-segmentation.diff b/modules/nf-core/xeniumranger/import-segmentation/xeniumranger-import-segmentation.diff index 18f220fe..57550231 100644 --- a/modules/nf-core/xeniumranger/import-segmentation/xeniumranger-import-segmentation.diff +++ b/modules/nf-core/xeniumranger/import-segmentation/xeniumranger-import-segmentation.diff @@ -2,7 +2,7 @@ Changes in component 'nf-core/xeniumranger/import-segmentation' Changes in 'xeniumranger/import-segmentation/main.nf': --- modules/nf-core/xeniumranger/import-segmentation/main.nf +++ modules/nf-core/xeniumranger/import-segmentation/main.nf -@@ -2,20 +2,20 @@ +@@ -2,20 +2,21 @@ tag "$meta.id" label 'process_high' @@ -10,25 +10,36 @@ Changes in 'xeniumranger/import-segmentation/main.nf': + container "nf-core/xeniumranger:3.1.1" input: - tuple val(meta), path(xenium_bundle) +- tuple val(meta), path(xenium_bundle) - val(expansion_distance) - path(coordinate_transform) - path(nuclei) - path(cells) - path(transcript_assignment) - path(viz_polygons) -+ val(units) +- path(coordinate_transform) +- path(nuclei) +- path(cells) +- path(transcript_assignment) +- path(viz_polygons) ++ tuple val(meta), ++ path(xenium_bundle), ++ path(coordinate_transform), ++ path(nuclei), ++ path(cells), ++ path(transcript_assignment), ++ path(viz_polygons), ++ val(units) output: - tuple val(meta), path("**/outs/**"), emit: outs - path "versions.yml", emit: versions -+ tuple val(meta), path("${meta.id}/outs"), emit: bundle -+ path("versions.yml") , emit: versions ++ tuple val(meta), path("${prefix}/outs"), emit: bundle ++ path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when -@@ -29,32 +29,33 @@ - def prefix = task.ext.prefix ?: "${meta.id}" +@@ -26,35 +27,36 @@ + error "XENIUMRANGER_IMPORT-SEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." + } + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" // image based segmentation options - def expansion_distance = expansion_distance ? "--expansion-distance=\"${expansion_distance}\"": "" // expansion distance (default - 5, range - 0 - 100) @@ -75,6 +86,20 @@ Changes in 'xeniumranger/import-segmentation/main.nf': ${args} cat <<-END_VERSIONS > versions.yml +@@ -68,9 +70,11 @@ + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "XENIUMRANGER_IMPORT-SEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." + } +- def prefix = task.ext.prefix ?: "${meta.id}" ++ ++ prefix = task.ext.prefix ?: "${meta.id}" ++ + """ +- mkdir -p "${prefix}/outs/" ++ mkdir -p "${prefix}/outs" + touch "${prefix}/outs/fake_file.txt" + + cat <<-END_VERSIONS > versions.yml Changes in 'xeniumranger/import-segmentation/meta.yml': --- modules/nf-core/xeniumranger/import-segmentation/meta.yml diff --git a/modules/nf-core/xeniumranger/relabel/main.nf b/modules/nf-core/xeniumranger/relabel/main.nf index 40459f8e..03d55d9f 100644 --- a/modules/nf-core/xeniumranger/relabel/main.nf +++ b/modules/nf-core/xeniumranger/relabel/main.nf @@ -9,8 +9,8 @@ process XENIUMRANGER_RELABEL { path(gene_panel) output: - tuple val(meta), path("${meta.id}/outs"), emit: bundle - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/outs"), emit: bundle + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -21,7 +21,7 @@ process XENIUMRANGER_RELABEL { error "XENIUMRANGER_RELABEL module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" """ xeniumranger relabel \\ @@ -43,9 +43,10 @@ process XENIUMRANGER_RELABEL { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "XENIUMRANGER_RELABEL module does not support Conda. Please use Docker / Singularity / Podman instead." } - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p "${prefix}/outs/" + mkdir -p "${prefix}/outs" touch "${prefix}/outs/fake_file.txt" cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/xeniumranger/relabel/xeniumranger-relabel.diff b/modules/nf-core/xeniumranger/relabel/xeniumranger-relabel.diff index 33766acc..3e7f3adb 100644 --- a/modules/nf-core/xeniumranger/relabel/xeniumranger-relabel.diff +++ b/modules/nf-core/xeniumranger/relabel/xeniumranger-relabel.diff @@ -1,5 +1,4 @@ Changes in component 'nf-core/xeniumranger/relabel' -'modules/nf-core/xeniumranger/relabel/meta.yml' is unchanged Changes in 'xeniumranger/relabel/main.nf': --- modules/nf-core/xeniumranger/relabel/main.nf +++ modules/nf-core/xeniumranger/relabel/main.nf @@ -17,14 +16,37 @@ Changes in 'xeniumranger/relabel/main.nf': output: - tuple val(meta), path("**/outs/**"), emit: outs - path "versions.yml", emit: versions -+ tuple val(meta), path("${meta.id}/outs"), emit: bundle -+ path("versions.yml") , emit: versions ++ tuple val(meta), path("${prefix}/outs"), emit: bundle ++ path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when +@@ -21,7 +21,7 @@ + error "XENIUMRANGER_RELABEL module does not support Conda. Please use Docker / Singularity / Podman instead." + } + def args = task.ext.args ?: '' +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" + + """ + xeniumranger relabel \\ +@@ -43,9 +43,10 @@ + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "XENIUMRANGER_RELABEL module does not support Conda. Please use Docker / Singularity / Podman instead." + } +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" ++ + """ +- mkdir -p "${prefix}/outs/" ++ mkdir -p "${prefix}/outs" + touch "${prefix}/outs/fake_file.txt" + + cat <<-END_VERSIONS > versions.yml +'modules/nf-core/xeniumranger/relabel/meta.yml' is unchanged +'modules/nf-core/xeniumranger/relabel/tests/main.nf.test' is unchanged 'modules/nf-core/xeniumranger/relabel/tests/main.nf.test.snap' is unchanged -'modules/nf-core/xeniumranger/relabel/tests/tags.yml' is unchanged 'modules/nf-core/xeniumranger/relabel/tests/nextflow.config' is unchanged -'modules/nf-core/xeniumranger/relabel/tests/main.nf.test' is unchanged +'modules/nf-core/xeniumranger/relabel/tests/tags.yml' is unchanged ************************************************************ diff --git a/modules/nf-core/xeniumranger/resegment/main.nf b/modules/nf-core/xeniumranger/resegment/main.nf index 4d035cde..839aacaf 100644 --- a/modules/nf-core/xeniumranger/resegment/main.nf +++ b/modules/nf-core/xeniumranger/resegment/main.nf @@ -8,8 +8,8 @@ process XENIUMRANGER_RESEGMENT { tuple val(meta), path(xenium_bundle) output: - tuple val(meta), path("${meta.id}/outs"), emit: bundle - path("versions.yml") , emit: versions + tuple val(meta), path("${prefix}/outs"), emit: bundle + path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when @@ -20,7 +20,7 @@ process XENIUMRANGER_RESEGMENT { error "XENIUMRANGER_RESEGMENT module does not support Conda. Please use Docker / Singularity / Podman instead." } def args = task.ext.args ?: "" - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" // Do not use boundary stain in analysis, but keep default interior stain and DAPI def boundary_stain = "${params.boundary_stain}" ? "": "--boundary-stain=disable" @@ -50,9 +50,10 @@ process XENIUMRANGER_RESEGMENT { if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { error "XENIUMRANGER_RESEGMENT module does not support Conda. Please use Docker / Singularity / Podman instead." } - def prefix = task.ext.prefix ?: "${meta.id}" + prefix = task.ext.prefix ?: "${meta.id}" + """ - mkdir -p "${prefix}/outs/" + mkdir -p "${prefix}/outs" touch "${prefix}/outs/fake_file.txt" cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/xeniumranger/resegment/xeniumranger-resegment.diff b/modules/nf-core/xeniumranger/resegment/xeniumranger-resegment.diff index 4d7dc325..9fffaf5a 100644 --- a/modules/nf-core/xeniumranger/resegment/xeniumranger-resegment.diff +++ b/modules/nf-core/xeniumranger/resegment/xeniumranger-resegment.diff @@ -1,5 +1,4 @@ Changes in component 'nf-core/xeniumranger/resegment' -'modules/nf-core/xeniumranger/resegment/meta.yml' is unchanged Changes in 'xeniumranger/resegment/main.nf': --- modules/nf-core/xeniumranger/resegment/main.nf +++ modules/nf-core/xeniumranger/resegment/main.nf @@ -20,18 +19,21 @@ Changes in 'xeniumranger/resegment/main.nf': output: - tuple val(meta), path("**/outs/**"), emit: outs - path "versions.yml", emit: versions -+ tuple val(meta), path("${meta.id}/outs"), emit: bundle -+ path("versions.yml") , emit: versions ++ tuple val(meta), path("${prefix}/outs"), emit: bundle ++ path("versions.yml") , emit: versions when: task.ext.when == null || task.ext.when -@@ -26,20 +22,17 @@ +@@ -24,22 +20,19 @@ + error "XENIUMRANGER_RESEGMENT module does not support Conda. Please use Docker / Singularity / Podman instead." + } def args = task.ext.args ?: "" - def prefix = task.ext.prefix ?: "${meta.id}" - +- def prefix = task.ext.prefix ?: "${meta.id}" +- - def expansion_distance = expansion_distance ? "--expansion-distance=\"${expansion_distance}\"": "" - def dapi_filter = dapi_filter ? "--dapi-filter=\"${dapi_filter}\"": "" -- ++ prefix = task.ext.prefix ?: "${meta.id}" + // Do not use boundary stain in analysis, but keep default interior stain and DAPI - def boundary_stain = boundary_stain ? "--boundary-stain=disable": "" + def boundary_stain = "${params.boundary_stain}" ? "": "--boundary-stain=disable" @@ -50,9 +52,23 @@ Changes in 'xeniumranger/resegment/main.nf': ${boundary_stain} \\ ${interior_stain} \\ --localcores=${task.cpus} \\ +@@ -57,9 +50,10 @@ + if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { + error "XENIUMRANGER_RESEGMENT module does not support Conda. Please use Docker / Singularity / Podman instead." + } +- def prefix = task.ext.prefix ?: "${meta.id}" ++ prefix = task.ext.prefix ?: "${meta.id}" ++ + """ +- mkdir -p "${prefix}/outs/" ++ mkdir -p "${prefix}/outs" + touch "${prefix}/outs/fake_file.txt" + + cat <<-END_VERSIONS > versions.yml +'modules/nf-core/xeniumranger/resegment/meta.yml' is unchanged +'modules/nf-core/xeniumranger/resegment/tests/main.nf.test' is unchanged 'modules/nf-core/xeniumranger/resegment/tests/main.nf.test.snap' is unchanged -'modules/nf-core/xeniumranger/resegment/tests/tags.yml' is unchanged 'modules/nf-core/xeniumranger/resegment/tests/nextflow.config' is unchanged -'modules/nf-core/xeniumranger/resegment/tests/main.nf.test' is unchanged +'modules/nf-core/xeniumranger/resegment/tests/tags.yml' is unchanged ************************************************************ diff --git a/nextflow.config b/nextflow.config index 31579809..69554676 100644 --- a/nextflow.config +++ b/nextflow.config @@ -15,10 +15,13 @@ params { mode = null // run the pipeline either in `image` or `coordinate` or `segfree` or `preview` modes method = null // name of the method to run for image or coordinate or segfree approaches gene_panel = null // path to gene panel json file if `relabel_genes` is true - qupath_polygons = null // polygon segmentation results in GeoJSON format + qupath_polygons = null // Path to qupath segmentation results in GeoJSON format alignment_csv = null // image alignment file format a 3x3 transformation matrix, where the last row is [0,0,1] cellpose_model = null // custom cellpose model to use for running or starting training segmentation_mask = null // prior segmentation mask + probes_fasta = null // Fasta file for the probe sequences used in the xenium experiment + reference_annotations = null // Path to the genomic features (.gff) and fasta (.fa) files used as reference annotations + gene_synonyms = null // Gene synonyms that may have been counted as off-targets but simply differ in name // execution specific sharpen_tiff = false // wether to sharpen the morphology-focus tiff @@ -78,6 +81,11 @@ params { max_multiqc_email_size = '25.MB' multiqc_methods_description = null + // pipeline dev and testing option + buffer_samples = false // process one sample at a time from the multi-sample samplesheet + buffer_size = 1 // buffer size 0 means no buffering of samples + restrict_concurrency = false // restrict running certain process in parallel + // Boilerplate options outdir = null publish_dir_mode = 'copy' @@ -209,13 +217,6 @@ profiles { executor.name = 'local' executor.cpus = 8 executor.memory = 16.GB - process { - resourceLimits = [ - memory: 16.GB, - cpus : 8, - time : 1.h - ] - } } gpu { docker.runOptions = '-u $(id -u):$(id -g) --gpus all' diff --git a/nextflow_schema.json b/nextflow_schema.json index ee9641ef..f52d053d 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -46,7 +46,8 @@ }, "qupath_polygons": { "type": "string", - "description": "Polygon segmentation results in GeoJSON format from QuPath." + "description": "Path to qupath segmentation file in GeoJSON format.", + "format": "file-path" }, "alignment_csv": { "type": "string", @@ -63,6 +64,21 @@ "description": "Prior segmentation mask from other segmentation methods.", "format": "file-path" }, + "probes_fasta": { + "type": "string", + "description": "Fasta file for the probe sequences used in the xenium experiment.", + "format": "file-path" + }, + "reference_annotations": { + "type": "string", + "description": "Path to the directory containing genomic features (.gff) and fasta (.fa) files used as reference annotations.", + "format": "file-path" + }, + "gene_synonyms": { + "type": "string", + "description": "Gene synonyms that may have been counted as off-targets but simply differ in name.", + "format": "file-path" + }, "email": { "type": "string", "description": "Email address for completion summary.", @@ -243,6 +259,21 @@ "type": "boolean", "description": "Whether to use connected components for grouping transcripts without direct nucleus association", "default": false + }, + "buffer_samples": { + "type": "boolean", + "description": "Process only one sample at a time from a multi-sample samplesheet.", + "default": false + }, + "buffer_size": { + "type": "integer", + "description": "Number of sample(s) to process at a time from a multi-sample samplesheet. Works if buffered_samples is true.", + "default": 1 + }, + "restrict_concurrency": { + "type": "boolean", + "description": "Restrict parallelizing a process. Eg. restrict running cellpose cell and nuclei segmentation together if the resources are limited.", + "default": false } } }, diff --git a/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf b/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf index 6c958417..9aa4c94c 100644 --- a/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf +++ b/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf @@ -22,12 +22,8 @@ workflow BAYSOR_RUN_PRIOR_SEGMENTATION_MASK { ch_transcripts = Channel.empty() - ch_segmentation = Channel.empty() - ch_polygons2d = Channel.empty() - ch_htmls = Channel.empty() - ch_redefined_bundle = Channel.empty() - ch_coordinate_space = Channel.value("microns") + ch_coordinate_space = Channel.value("pixels") // filter transcripts.parquet based on thresholds if ( params.filter_transcripts ) { @@ -49,42 +45,49 @@ workflow BAYSOR_RUN_PRIOR_SEGMENTATION_MASK { ch_transcripts = ch_transcripts_parquet } - // run baysor with morphology.tiff - BAYSOR_RUN ( - ch_transcripts, - ch_segmentation_mask, - ch_config, - 30 - ) + + // run baysor with prior segmentation mask + ch_baysor_input = ch_transcripts + .combine(ch_segmentation_mask) + .combine(ch_config) + .map { meta, transcripts, mask, config -> + tuple ( + meta, // meta + transcripts, // transcripts + mask, // prior_segmentation + config, // config + 30 // scale + ) + } + BAYSOR_RUN ( ch_baysor_input ) ch_versions = ch_versions.mix( BAYSOR_RUN.out.versions ) - ch_segmentation = BAYSOR_RUN.out.segmentation - ch_just_segmentation = ch_segmentation.map { - _meta, segmentation -> return [ segmentation ] - } - ch_polygons2d = BAYSOR_RUN.out.polygons2d - ch_htmls = BAYSOR_RUN.out.htmls - // run xeniumranger import-segmentation + // run import-segmentation with baysor outs + ch_imp_seg_inputs = ch_bundle_path + .combine(BAYSOR_RUN.out.segmentation, by: 0) + .map { + meta, bundle, _segmentation_csv, polygons2d -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + polygons2d, // nuclei + polygons2d, // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - [], - [], - ch_just_segmentation, - ch_polygons2d, - ch_coordinate_space + ch_imp_seg_inputs ) - ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) + ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle emit: - segmentation = ch_segmentation // channel: [ val(meta), ["segmentation.csv"] ] - polygons2d = ch_polygons2d // channel: [ ["segmentation_polygons_2d.json"] ] - htmls = ch_htmls // channel: [ ["*.html"] ] - coordinate_space = ch_coordinate_space // channel: [ "microns" ] redefined_bundle = ch_redefined_bundle // channel: [ val(meta), ["redefined-xenium-bundle"] ] diff --git a/subworkflows/local/baysor_run_transcripts_parquet/main.nf b/subworkflows/local/baysor_run_transcripts_parquet/main.nf index 2c4f6294..d09827bb 100644 --- a/subworkflows/local/baysor_run_transcripts_parquet/main.nf +++ b/subworkflows/local/baysor_run_transcripts_parquet/main.nf @@ -13,7 +13,7 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { take: ch_bundle_path // channel: [ val(meta), ["xenium-bundle"] ] - ch_transcripts_parquet // channel: [ val(meta), ["transcripts.csv.parquet"] ] + ch_transcripts_parquet // channel: [ val(meta), ["transcripts.parquet"] ] ch_config // channel: ["path-to-xenium.toml"] main: @@ -23,39 +23,10 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { ch_transcripts = Channel.empty() // ch_splits_csv = Channel.empty() - ch_segmentation = Channel.empty() - ch_polygons2d = Channel.empty() - ch_htmls = Channel.empty() - ch_redefined_bundle = Channel.empty() ch_coordinate_space = Channel.value("microns") - - // generate splits - // SPLIT_TRANSCRIPTS ( - // ch_transcripts_parquet, - // params.x_bins, - // params.y_bins - // ) - // ch_versions = ch_versions.mix ( SPLIT_TRANSCRIPTS.out.versions ) - - // ch_splits_csv = SPLIT_TRANSCRIPTS.out.splits_csv - - - // Set splits.csv into tuple queue channel - // Channel - // ch_splits_csv - // .flatMap { meta, splits_file -> - // splits_file.splitCsv(header: true).collect { row -> - // tuple(meta, row.tile_id, row.x_min, row.x_max, row.y_min, row.y_max) - // } - // } - // .set { ch_splits } // channel: [ val(tile_id), val(x_min), val(x_max), val(y_min), val(y_max) ] - - - //Add in sample path for each split value - // transcripts_input = ch_transcripts_parquet.combine(ch_splits, by: 0) - + // TODO: run baysor in parallel - next release issue // filter transcripts.parquet based on thresholds if ( params.filter_transcripts ) { @@ -79,31 +50,40 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { // run baysor with the filtered transcripts.parquet - BAYSOR_RUN ( - ch_transcripts, - [], - ch_config, - 30 - ) + ch_baysor_input = ch_transcripts + .combine(ch_config) + .map { meta, transcripts, config -> + tuple ( + meta, // meta + transcripts, // transcripts + [], // prior_segmentation + config, // config + 30 // scale + ) + } + BAYSOR_RUN ( ch_baysor_input ) ch_versions = ch_versions.mix ( BAYSOR_RUN.out.versions ) - ch_segmentation = BAYSOR_RUN.out.segmentation - ch_segmentation_csv = ch_segmentation.map { - _meta, segmentation -> return [ segmentation ] - } - ch_polygons2d = BAYSOR_RUN.out.polygons2d - ch_htmls = BAYSOR_RUN.out.htmls - // run xeniumranger import-segmentation + ch_imp_seg_inputs = ch_bundle_path + .combine(BAYSOR_RUN.out.segmentation, by: 0) + .map { + meta, bundle, segmentation_csv, polygons2d -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + [], // nuclei + [], // cells + segmentation_csv, // transcript_assignment + polygons2d, // viz_polygons + ch_coordinate_space.val // units + ) + } + XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - [], - [], - ch_segmentation_csv, - ch_polygons2d, - ch_coordinate_space + ch_imp_seg_inputs ) ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) @@ -111,10 +91,6 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { emit: - segmentation = ch_segmentation // channel: [ val(meta), ["segmentation.csv"] ] - polygons2d = ch_polygons2d // channel: [ ["segmentation_polygons_2d.json"] ] - htmls = ch_htmls // channel: [ ["*.html"] ] - coordinate_space = ch_coordinate_space // channel: [ ["microns"] ] redefined_bundle = ch_redefined_bundle // channel: [ val(meta), "redefined-xenium-bundle" ] diff --git a/subworkflows/local/cellpose_baysor_import_segmentation/main.nf b/subworkflows/local/cellpose_baysor_import_segmentation/main.nf index f0cb4871..dda337e5 100644 --- a/subworkflows/local/cellpose_baysor_import_segmentation/main.nf +++ b/subworkflows/local/cellpose_baysor_import_segmentation/main.nf @@ -3,9 +3,9 @@ // include { RESOLIFT } from '../../../modules/local/resolift/main' +include { BAYSOR_RUN } from '../../../modules/local/baysor/run/main' include { CELLPOSE as CELLPOSE_CELLS } from '../../../modules/nf-core/cellpose/main' include { CELLPOSE as CELLPOSE_NUCLEI } from '../../../modules/nf-core/cellpose/main' -include { BAYSOR_RUN } from '../../../modules/local/baysor/run/main' include { BAYSOR_PREPROCESS_TRANSCRIPTS } from '../../../modules/local/baysor/preprocess/main' include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' @@ -21,17 +21,9 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { main: ch_versions = Channel.empty() - ch_image = Channel.empty() - ch_polygons = Channel.empty() - ch_segmentation = Channel.empty() ch_transcripts = Channel.empty() + ch_imp_seg_inputs = Channel.empty() ch_filtered_transcripts = Channel.empty() - ch_cellpose_cells_mask = Channel.empty() - ch_cellpose_nuclei_mask = Channel.empty() - ch_cellpose_cells_cells = Channel.empty() - ch_cellpose_nuclei_cells = Channel.empty() - ch_cellpose_cells_flows = Channel.empty() - ch_cellpose_nuclei_flows = Channel.empty() ch_coordinate_space = Channel.value("microns") cellpose_model = params.cellpose_model ? (Channel.fromPath(params.cellpose_model, checkIfExists: true)) : [] @@ -50,22 +42,13 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { } - // run cellpose on the enhanced tiff + + // run cellpose on the morphology (enhanced) tiff if ( params.cell_segmentation_only ) { CELLPOSE_CELLS ( ch_image, cellpose_model, 'cells' ) ch_versions = ch_versions.mix( CELLPOSE_CELLS.out.versions ) - ch_cellpose_cells_cells = CELLPOSE_CELLS.out.cells.map { - _meta, cells -> return [ cells ] - } - ch_cellpose_cells_mask = CELLPOSE_CELLS.out.mask.map { - _meta, mask -> return [ mask ] - } - ch_cellpose_cells_flows = CELLPOSE_CELLS.out.flows.map { - _meta, flows -> return [ flows ] - } - } if ( params.nucleus_segmentation_only ) { @@ -73,16 +56,6 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { CELLPOSE_NUCLEI ( ch_image, 'nuclei', 'nuclei' ) ch_versions = ch_versions.mix( CELLPOSE_NUCLEI.out.versions ) - ch_cellpose_nuclei_cells = CELLPOSE_NUCLEI.out.cells.map { - _meta, cells -> return [ cells ] - } - ch_cellpose_nuclei_mask = CELLPOSE_NUCLEI.out.mask.map { - _meta, mask -> return [ mask ] - } - ch_cellpose_nuclei_flows = CELLPOSE_NUCLEI.out.flows.map { - _meta, flows -> return [ flows ] - } - } @@ -111,54 +84,81 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { if ( params.nucleus_segmentation_only ) { // run baysor with nuclei mask - BAYSOR_RUN ( ch_transcripts, ch_cellpose_nuclei_mask, ch_config, 30 ) + ch_baysor_input = ch_transcripts + .combine(CELLPOSE_NUCLEI.out.mask, by: 0) + .combine(ch_config) + .map { meta, transcripts, mask, config -> + tuple ( + meta, // meta + transcripts, // transcripts + mask, // prior_segmentation + config, // config + 30 // scale + ) + } + BAYSOR_RUN ( ch_baysor_input ) ch_versions = ch_versions.mix ( BAYSOR_RUN.out.versions ) } else if ( params.cell_segmentation_only ) { // run baysor with cell mask - BAYSOR_RUN ( ch_transcripts, ch_cellpose_cells_mask, ch_config, 30 ) + ch_baysor_input = ch_transcripts + .combine(CELLPOSE_CELLS.out.mask, by: 0) + .combine(ch_config) + .map { meta, transcripts, mask, config -> + tuple ( + meta, // meta + transcripts, // transcripts + mask, // prior_segmentation + config, // config + 30 // scale + ) + } + BAYSOR_RUN ( ch_baysor_input ) ch_versions = ch_versions.mix ( BAYSOR_RUN.out.versions ) } else { - // run baysor with cell mask - BAYSOR_RUN ( ch_transcripts, [], ch_config, 30 ) + // run baysor without cell/nuclei mask + ch_baysor_input = ch_transcripts + .combine(ch_config) + .map { meta, transcripts, config -> + tuple ( + meta, // meta + transcripts, // transcripts + [], // prior_segmentation + config, // config + 30 // scale + ) + } + BAYSOR_RUN ( ch_baysor_input ) ch_versions = ch_versions.mix ( BAYSOR_RUN.out.versions ) } // run import-segmentation with baysor outs - ch_segmentation = BAYSOR_RUN.out.segmentation.map { - _meta, segmentation -> return [ segmentation ] - } - ch_polygons = BAYSOR_RUN.out.polygons2d - - - XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - [], - [], - ch_segmentation, - ch_polygons, - ch_coordinate_space - ) + ch_imp_seg_inputs = ch_bundle_path + .combine(BAYSOR_RUN.out.segmentation, by: 0) + .map { + meta, bundle, segmentation_csv, polygons2d -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + [], // nuclei + [], // cells + segmentation_csv, // transcript_assignment + polygons2d, // viz_polygons + ch_coordinate_space.val // units + ) + } + + XENIUMRANGER_IMPORT_SEGMENTATION ( ch_imp_seg_inputs ) ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) emit: - cells_mask = ch_cellpose_cells_mask // channel: [ val(meta), [ "*masks.tif" ] ] - cells_flows = ch_cellpose_cells_flows // channel: [ val(meta), [ "*flows.tif" ] ] - cells_cells = ch_cellpose_cells_cells // channel: [ val(meta), [ "*seg.npy" ] ] - nuclei_mask = ch_cellpose_nuclei_mask // channel: [ val(meta), [ "*masks.tif" ] ] - nuclei_flows = ch_cellpose_nuclei_flows // channel: [ val(meta), [ "*flows.tif" ] ] - nuclei_cells = ch_cellpose_nuclei_cells // channel: [ val(meta), [ "*seg.npy" ] ] - - segmentation = ch_segmentation // channel: [ val(meta), [ *segmentation.csv ] ] - polygons2d = ch_polygons // channel: [ val(meta), [ *segmentation_polygons_2d.json ] ] - coordinate_space = ch_coordinate_space // channel: [ val("microns") ] redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle // channel: [ val(meta), ["redefined-xenium-bundle"] ] diff --git a/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf b/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf index b41bc334..5d5216aa 100644 --- a/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf +++ b/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf @@ -17,13 +17,7 @@ workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { main: ch_versions = Channel.empty() - ch_image = Channel.empty() - ch_cellpose_cells_mask = Channel.empty() - ch_cellpose_nuclei_mask = Channel.empty() - ch_cellpose_cells_cells = Channel.empty() - ch_cellpose_nuclei_cells = Channel.empty() - ch_cellpose_cells_flows = Channel.empty() - ch_cellpose_nuclei_flows = Channel.empty() + ch_imp_seg_inputs = Channel.empty() ch_coordinate_space = Channel.value("pixels") cellpose_model = params.cellpose_model ? (Channel.fromPath(params.cellpose_model, checkIfExists: true)) : [] @@ -43,73 +37,62 @@ workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { } // run cellpose on morphology tiff - if ( !params.nucleus_segmentation_only ) { - - CELLPOSE_CELLS ( ch_image, cellpose_model, 'cells' ) - ch_versions = ch_versions.mix( CELLPOSE_CELLS.out.versions ) - - ch_cellpose_cells_cells = CELLPOSE_CELLS.out.cells.map { - _meta, cells -> return [ cells ] - } - ch_cellpose_cells_mask = CELLPOSE_CELLS.out.mask.map { - _meta, mask -> return [ mask ] - } - ch_cellpose_cells_flows = CELLPOSE_CELLS.out.flows.map { - _meta, flows -> return [ flows ] - } - - } + CELLPOSE_CELLS ( ch_image, cellpose_model, 'cells' ) + ch_versions = ch_versions.mix( CELLPOSE_CELLS.out.versions ) CELLPOSE_NUCLEI ( ch_image, 'nuclei', 'nuclei' ) ch_versions = ch_versions.mix( CELLPOSE_NUCLEI.out.versions ) - ch_cellpose_nuclei_cells = CELLPOSE_NUCLEI.out.cells.map { - _meta, cells -> return [ cells ] - } - ch_cellpose_nuclei_mask = CELLPOSE_NUCLEI.out.mask.map { - _meta, mask -> return [ mask ] - } - ch_cellpose_nuclei_flows = CELLPOSE_NUCLEI.out.flows.map { - _meta, flows -> return [ flows ] - } // run import-segmentation with cellpose results if ( params.nucleus_segmentation_only ) { + ch_imp_seg_inputs = ch_bundle_path + .combine(CELLPOSE_NUCLEI.out.cells, by: 0) + .map { + meta, bundle, nuclei_seg -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + nuclei_seg, // nuclei + [], // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - ch_cellpose_nuclei_mask, - [], - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) } else { + ch_imp_seg_inputs = ch_bundle_path + .combine(CELLPOSE_CELLS.out.cells, by:0) + .combine(CELLPOSE_NUCLEI.out.cells, by:0) + .map { + meta, bundle, cells_seg, nuclei_seg -> tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + nuclei_seg, // nuclei + cells_seg, // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } + ch_imp_seg_inputs.view() XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - ch_cellpose_nuclei_mask, - ch_cellpose_cells_mask, - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) } emit: - cells_mask = ch_cellpose_cells_mask // channel: [ val(meta), [ "*masks.tif" ] ] - cells_flows = ch_cellpose_cells_flows // channel: [ val(meta), [ "*flows.tif" ] ] - cells_cells = ch_cellpose_cells_cells // channel: [ val(meta), [ "*seg.npy" ] ] - nuclei_mask = ch_cellpose_nuclei_mask // channel: [ val(meta), [ "*masks.tif" ] ] - nuclei_flows = ch_cellpose_nuclei_flows // channel: [ val(meta), [ "*flows.tif" ] ] - nuclei_cells = ch_cellpose_nuclei_cells // channel: [ val(meta), [ "*seg.npy" ] ] - coordinate_space = ch_coordinate_space // channel: [ ["pixels"] ] redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle // channel: [ val(meta), ["redefined-xenium-bundle"] ] diff --git a/subworkflows/local/proseg_preset_proseg2baysor/main.nf b/subworkflows/local/proseg_preset_proseg2baysor/main.nf index 90448094..d0314e26 100644 --- a/subworkflows/local/proseg_preset_proseg2baysor/main.nf +++ b/subworkflows/local/proseg_preset_proseg2baysor/main.nf @@ -20,35 +20,38 @@ workflow PROSEG_PRESET_PROSEG2BAYSOR { // run proseg with the xenium format PROSEG ( ch_transcripts_parquet ) - ch_versions = ch_versions.mix( PROSEG.out.versions ) + ch_versions = ch_versions.mix ( PROSEG.out.versions ) + // run proseg-to-baysor on the data generated with the proseg run - PROSEG2BAYSOR ( PROSEG.out.cell_polygons_2d, PROSEG.out.transcript_metadata ) - ch_versions = ch_versions.mix( PROSEG2BAYSOR.out.versions ) + PROSEG2BAYSOR ( PROSEG.out.seg_outs ) + ch_versions = ch_versions.mix ( PROSEG2BAYSOR.out.versions ) - ch_metadata = PROSEG2BAYSOR.out.xr_metadata - ch_polygons = PROSEG2BAYSOR.out.xr_polygons.map { - _meta, polygons -> return [ polygons ] - } // run xeniumranger import-segmentation + ch_imp_seg_inputs = ch_bundle_path + .combine( PROSEG2BAYSOR.out.xr_metadata, by: 0 ) + .combine( PROSEG2BAYSOR.out.xr_polygons, by: 0 ) + .map { + meta, bundle, metadata, polygons2d -> tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + [], // nuclei + [], // cells + metadata, // transcript_assignment + polygons2d, // viz_polygons + ch_coordinate_space.val // units + ) + } + XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_bundle_path, - [], - [], - [], - ch_metadata, - ch_polygons, - ch_coordinate_space + ch_imp_seg_inputs ) - ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) + ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) emit: - cell_polygons_2d = PROSEG.out.cell_polygons_2d // channel: [ val(meta), [ "cell-polygons.geojson.gz" ] ] - - xr_polygons = PROSEG2BAYSOR.out.xr_polygons // channel: [ val(meta), [ "xr-cell-polygons.geojson" ] ] - xr_metadata = PROSEG2BAYSOR.out.xr_metadata // channel: [ [ "xr-transcript-metadata.csv" ] ] coordinate_space = ch_coordinate_space // channel: [ "microns" ] redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle // channel: [ val(meta), ["redefined-xenium-bundle"] ] diff --git a/subworkflows/local/segger_create_train_predict/main.nf b/subworkflows/local/segger_create_train_predict/main.nf index f0a95414..52423e0d 100644 --- a/subworkflows/local/segger_create_train_predict/main.nf +++ b/subworkflows/local/segger_create_train_predict/main.nf @@ -63,41 +63,41 @@ workflow SEGGER_CREATE_TRAIN_PREDICT { // run xeniumranger import-segmentation - cells = ch_updated_bundle.map { _meta, bundle -> - return [ bundle + "/cells.zarr.zip" ] - } - - if ( params.nucleus_segmentation_only ) { - - XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_updated_bundle, - [], - cells, - cells, - [], - [], - ch_coordinate_space - ) - ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle - - ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) - - } else { - - XENIUMRANGER_IMPORT_SEGMENTATION ( - ch_updated_bundle, - [], - [], - cells, - [], - [], - ch_coordinate_space - ) - ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle - - ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) - - } + // cells = ch_updated_bundle.map { _meta, bundle -> + // return [ bundle + "/cells.zarr.zip" ] + // } + + // if ( params.nucleus_segmentation_only ) { + + // XENIUMRANGER_IMPORT_SEGMENTATION ( + // ch_updated_bundle, + // [], + // cells, + // cells, + // [], + // [], + // ch_coordinate_space + // ) + // ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle + + // ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) + + // } else { + + // XENIUMRANGER_IMPORT_SEGMENTATION ( + // ch_updated_bundle, + // [], + // [], + // cells, + // [], + // [], + // ch_coordinate_space + // ) + // ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.bundle + + // ch_versions = ch_versions.mix ( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) + + // } emit: diff --git a/subworkflows/local/spatialdata_write_meta_merge/main.nf b/subworkflows/local/spatialdata_write_meta_merge/main.nf index 47bc385b..fc53a282 100644 --- a/subworkflows/local/spatialdata_write_meta_merge/main.nf +++ b/subworkflows/local/spatialdata_write_meta_merge/main.nf @@ -61,23 +61,15 @@ workflow SPATIALDATA_WRITE_META_MERGE { // merge raw & redefined spatialdata objects - ch_just_redefined_bundle = SPATIALDATA_WRITE_REDEFINED_BUNDLE.out.spatialdata.map { - _meta, bundle -> return [ bundle ] - } SPATIALDATA_MERGE_RAW_REDEFINED ( - SPATIALDATA_WRITE_RAW_BUNDLE.out.spatialdata, - ch_just_redefined_bundle + SPATIALDATA_WRITE_RAW_BUNDLE.out.spatialdata.combine( ch_redefined_bundle, by: 0 ) ) ch_versions = ch_versions.mix ( SPATIALDATA_MERGE_RAW_REDEFINED.out.versions ) // write metadata with spatialdata object - ch_just_bundle_path = ch_bundle_path.map { - _meta, bundle -> return [ bundle ] - } SPATIALDATA_META ( - SPATIALDATA_MERGE_RAW_REDEFINED.out.merged_bundle, - ch_just_bundle_path + SPATIALDATA_MERGE_RAW_REDEFINED.out.merged_bundle.combine( ch_bundle_path, by: 0 ) ) ch_versions = ch_versions.mix ( SPATIALDATA_META.out.versions ) diff --git a/subworkflows/local/utils_nfcore_spatialxe_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialxe_pipeline/main.nf index aff748f2..184f4ab0 100644 --- a/subworkflows/local/utils_nfcore_spatialxe_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialxe_pipeline/main.nf @@ -164,6 +164,12 @@ workflow PIPELINE_COMPLETION { // def validateInputParameters() { + // check if the samplesheet provided with the test config is assets/samplesheet.csv + if ( workflow.profile.contains('test') && params.input != "${projectDir}/assets/samplesheet.csv" ) { + log.error "❌ Error: Use the samplesheet at: ${projectDir}/assets/samplesheet.csv with `--input` when running the pipeline in test profile." + exit 1 + } + // check if the segmentation method provided is valid for a mode if ( params.mode == 'image' && params.method ) { if ( !params.image_seg_methods.contains(params.method) ) { diff --git a/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf b/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf index e0c9df09..fd534b3b 100644 --- a/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf +++ b/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf @@ -17,23 +17,33 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { ch_versions = Channel.empty() ch_redefined_bundle = Channel.empty() - ch_coordinate_space = Channel.empty("pixels") + ch_coordinate_space = Channel.empty() cells = ch_bundle_path.map { - _meta, bundle -> return [ bundle + "/cells.zarr.zip" ] + meta, bundle -> return [ meta, bundle + "/cells.zarr.zip" ] } // scenario - 1 change nuclear expansion distance / create a nucleus-only count matrix(--expansion_distance=0) if ( params.expansion_distance == 0 || params.expansion_distance != 5 ) { + ch_coordinate_space = "microns" + ch_imp_seg_inputs = ch_bundle_path + .combine(cells, by:0) + .map { + meta, bundle, cells_zarr -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + cells_zarr, // nuclei + [], // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } IMP_SEG_COUNT_MATRIX_EXP_DISTANCE ( - ch_bundle_path, - [], - cells, - [], - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_redefined_bundle = IMP_SEG_COUNT_MATRIX_EXP_DISTANCE.out.bundle @@ -43,14 +53,25 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { // scenario - 2 polygon input - geojson format (from QuPath) if ( params.qupath_polygons && params.nucleus_segmentation_only ) { + ch_coordinate_space = "microns" + ch_imp_seg_inputs = ch_bundle_path + .combine(params.qupath_polygons) + .map { + meta, bundle, polygons_geojson -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + polygons_geojson, // nuclei + [], // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } + IMP_SEG_POLYGON_GEOJSON_INPUT ( - ch_bundle_path, - [], - params.qupath_polygons, - [], - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_redefined_bundle = IMP_SEG_POLYGON_GEOJSON_INPUT.out.bundle @@ -58,14 +79,25 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { } else if ( params.qupath_polygons ) { + ch_coordinate_space = "microns" + ch_imp_seg_inputs = ch_bundle_path + .combine(params.qupath_polygons) + .map { + meta, bundle, polygons_geojson -> + tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + polygons_geojson, // nuclei + polygons_geojson, // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } + IMP_SEG_POLYGON_GEOJSON_INPUT ( - ch_bundle_path, - [], - params.qupath_polygons, - params.qupath_polygons, - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_redefined_bundle = IMP_SEG_POLYGON_GEOJSON_INPUT.out.bundle @@ -80,14 +112,25 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { // scenario 5 - transformation matrix input if ( params.qupath_polygons && params.alignment_csv ) { + ch_imp_seg_inputs = ch_bundle_path + .combine(params.qupath_polygins) + .combine(params.alignment_csv) + .map { + meta, bundle, polygons_geojson, alignment_csv -> + tuple ( + meta, // meta + bundle, // bundle + alignment_csv, // coordinate_transform + polygons_geojson, // nuclei + polygons_geojson, // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } + IMP_SEG_TRANS_MATRIX_INPUT ( - ch_bundle_path, - params.alignment_csv, - params.qupath_polygons, - params.qupath_polygons, - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_redefined_bundle = IMP_SEG_TRANS_MATRIX_INPUT.out.bundle diff --git a/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf b/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf index ba81cc51..bea27214 100644 --- a/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf +++ b/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf @@ -30,14 +30,24 @@ workflow XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF { // adjust the nuclear expansion distance without altering nuclei detection if ( params.nucleus_segmentation_only ) { + ch_imp_seg_inputs = ch_bundle_path + .combine(XENIUMRANGER_RESEGMENT.out.bundle, by:0) + .combine(cells) + .map { + meta, bundle, cells_zarr -> tuple ( + meta, // meta + bundle, // bundle + [], // coordinate_transform + cells_zarr, // nuclei + [], // cells + [], // transcript_assignment + [], // viz_polygons + ch_coordinate_space.val // units + ) + } + XENIUMRANGER_IMPORT_SEGMENTATION ( - XENIUMRANGER_RESEGMENT.out.bundle, - [], - cells, - [], - [], - [], - ch_coordinate_space + ch_imp_seg_inputs ) ch_versions = ch_versions.mix( XENIUMRANGER_IMPORT_SEGMENTATION.out.versions ) diff --git a/workflows/spatialxe.nf b/workflows/spatialxe.nf index d062aa1b..c3e83be5 100644 --- a/workflows/spatialxe.nf +++ b/workflows/spatialxe.nf @@ -40,7 +40,7 @@ include { XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE } from '../subworkflo include { SPATIALDATA_WRITE_META_MERGE } from '../subworkflows/local/spatialdata_write_meta_merge/main' // TODO qc layer subworkflows - +include { OPT_FLIP_TRACK_STAT } from '../subworkflows/local/opt_flip_track_stat/main' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,21 +61,24 @@ workflow SPATIALXE { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - ch_versions = Channel.empty() - - ch_input = Channel.empty() - ch_bundle = Channel.empty() - ch_config = Channel.empty() - ch_features = Channel.empty() - ch_raw_bundle = Channel.empty() - ch_gene_panel = Channel.empty() - ch_bundle_path = Channel.empty() - ch_multiqc_files = Channel.empty() - ch_morphology_image = Channel.empty() - ch_redefined_bundle = Channel.empty() - ch_coordinate_space = Channel.empty() - ch_transcripts_parquet = Channel.empty() - + ch_versions = Channel.empty() + + ch_input = Channel.empty() + ch_bundle = Channel.empty() + ch_config = Channel.empty() + ch_features = Channel.empty() + ch_raw_bundle = Channel.empty() + ch_gene_panel = Channel.empty() + ch_bundle_path = Channel.empty() + ch_qupath_polygons = Channel.empty() + ch_gene_synonyms = Channel.empty() + ch_multiqc_files = Channel.empty() + ch_morphology_image = Channel.empty() + ch_redefined_bundle = Channel.empty() + ch_coordinate_space = Channel.empty() + ch_panel_probes_fasta = Channel.empty() + ch_transcripts_parquet = Channel.empty() + ch_reference_annotations = Channel.empty() /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -104,10 +107,19 @@ workflow SPATIALXE { } .set { ch_input } - } else { - - // for all other profile runs - ch_input = ch_samplesheet + } else { // for all other profile runs + + // check if samples are buffered + if ( params.buffer_samples ) { + ch_input = ch_samplesheet.buffer ( size: params.buffer_size ) + .map + { buffered_sample -> + def (meta, bundle, tif) = buffered_sample[0] + tuple(meta, bundle, tif) + } + } else { + ch_input = ch_samplesheet + } } // path to bundle input @@ -137,13 +149,13 @@ workflow SPATIALXE { ch_config = Channel.fromPath ( "${projectDir}/assets/config/xenium.toml", checkIfExists: true - ) + ).flatten() // get segmentation mask if provided with --segmentation_mask for the baysor method if ( params.segmentation_mask ) { ch_segmentation_mask = Channel.fromPath ( params.segmentation_mask, checkIfExists: true - ) + ).flatten() } // get a list of features if provided with the --features for the ficture method @@ -151,7 +163,7 @@ workflow SPATIALXE { ch_features = Channel.fromPath ( params.features, checkIfExists: true - ) + ).flatten() } // get custom cellpose model if provided with the --cellpose_model for the cellpose method @@ -159,7 +171,39 @@ workflow SPATIALXE { ch_features = Channel.fromPath ( params.cellpose_model, checkIfExists: true - ) + ).flatten() + } + + // get panel probes fasta for off-target-probe tracking + if ( params.probes_fasta ) { + ch_panel_probes_fasta = Channel.fromPath ( + params.probes_fasta, + checkIfExists: true + ).flatten() + } + + // get reference annotation files (gff,fa) for off-target-probe tracking + if ( params.reference_annotations ) { + ch_reference_annotations = Channel.fromPath ( + "${params.reference_annotations}/*.{fa,gff}".toString(), + checkIfExists: true + ).flatten() + } + + // get gene synonyms for off-target-probe tracking + if ( params.gene_synonyms ) { + ch_gene_synonyms = Channel.fromPath ( + params.gene_synonyms, + checkIfExists: true + ).flatten() + } + + // get qupath ploygons + if ( params.qupath_polygons ) { + ch_qupath_polygons = Channel.fromPath ( + "${params.qupath_polygons}/*.geojson", + checkIfExists: true + ).flatten() } // get gene_panel.json if provided with --gene_panel, sets relabel_genes to true @@ -169,7 +213,7 @@ workflow SPATIALXE { ch_gene_panel = Channel.fromPath ( params.gene_panel, checkIfExists: true - ) + ).flatten() } else { @@ -216,7 +260,6 @@ workflow SPATIALXE { ) } - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SPATIALXE - XENIUMRANGER LAYER @@ -253,52 +296,40 @@ workflow SPATIALXE { ch_coordinate_space = CELLPOSE_BAYSOR_IMPORT_SEGMENTATION.out.coordinate_space } - // check it the provided method is part of the methods list - if ( params.method in params.image_seg_methods ) { - - // run xeniumranger resegment with morphology_ome.tif - if ( params.method == 'xeniumranger' ) { + // run xeniumranger resegment with morphology_ome.tif + if ( params.method == 'xeniumranger' ) { - XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF ( - ch_bundle_path - ) - ch_redefined_bundle = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.redefined_bundle - ch_coordinate_space = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.coordinate_space - } - - // run baysor run with morphology_ome.tif - if ( params.method == 'baysor' ) { - - if ( params.segmentation_mask ) { - BAYSOR_RUN_PRIOR_SEGMENTATION_MASK ( - ch_bundle_path, - ch_transcripts_parquet, - ch_segmentation_mask, - ch_config - ) - } else { - BAYSOR_RUN_PRIOR_SEGMENTATION_MASK ( - ch_bundle_path, - ch_transcripts_parquet, - [], - ch_config - ) - } - ch_redefined_bundle = BAYSOR_RUN_PRIOR_SEGMENTATION_MASK.out.redefined_bundle - ch_coordinate_space = BAYSOR_RUN_PRIOR_SEGMENTATION_MASK.out.coordinate_space - } + XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF ( + ch_bundle_path + ) + ch_redefined_bundle = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.redefined_bundle + ch_coordinate_space = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.coordinate_space + } - // run cellpose on the morphology_ome.tif - if ( params.method == 'cellpose' ) { + // run baysor run with morphology_ome.tif + if ( params.method == 'baysor' ) { - CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF ( - ch_morphology_image, - ch_bundle_path + if ( params.segmentation_mask ) { + BAYSOR_RUN_PRIOR_SEGMENTATION_MASK ( + ch_bundle_path, + ch_transcripts_parquet, + ch_segmentation_mask, + ch_config ) - ch_redefined_bundle = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.redefined_bundle - ch_coordinate_space = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.coordinate_space } + ch_redefined_bundle = BAYSOR_RUN_PRIOR_SEGMENTATION_MASK.out.redefined_bundle + ch_coordinate_space = BAYSOR_RUN_PRIOR_SEGMENTATION_MASK.out.coordinate_space + } + // run cellpose on the morphology_ome.tif + if ( params.method == 'cellpose' ) { + + CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF ( + ch_morphology_image, + ch_bundle_path + ) + ch_redefined_bundle = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.redefined_bundle + ch_coordinate_space = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.coordinate_space } } @@ -309,8 +340,8 @@ workflow SPATIALXE { */ if ( params.mode == 'coordinate' ) { - // trigger the default transcripts-based workflow if no method is specified - if ( !params.method ) { + // run proseg with transcripts.parquet if method = proseg or is not provided (default workflow) + if ( !params.method || params.method == 'proseg') { PROSEG_PRESET_PROSEG2BAYSOR ( ch_bundle_path, @@ -321,49 +352,34 @@ workflow SPATIALXE { } - // check it the provided method is part of the methods list - if ( params.method in params.transcript_seg_methods ) { + // run segger with transcripts.parquet + if ( params.method == 'segger' ) { - // run proseg with transcripts.parquet - if ( params.method == 'proseg') { - - PROSEG_PRESET_PROSEG2BAYSOR ( - ch_bundle_path, - ch_transcripts_parquet - ) - ch_redefined_bundle = PROSEG_PRESET_PROSEG2BAYSOR.out.redefined_bundle - ch_coordinate_space = PROSEG_PRESET_PROSEG2BAYSOR.out.coordinate_space - - } - - // run segger with transcripts.parquet - if ( params.method == 'segger' ) { - - SEGGER_CREATE_TRAIN_PREDICT ( - ch_bundle_path, - ch_transcripts_parquet - ) - ch_redefined_bundle = SEGGER_CREATE_TRAIN_PREDICT.out.redefined_bundle - ch_coordinate_space = SEGGER_CREATE_TRAIN_PREDICT.out.coordinate_space - - } + SEGGER_CREATE_TRAIN_PREDICT ( + ch_bundle_path, + ch_transcripts_parquet + ) + ch_redefined_bundle = SEGGER_CREATE_TRAIN_PREDICT.out.redefined_bundle + ch_coordinate_space = SEGGER_CREATE_TRAIN_PREDICT.out.coordinate_space - // run baysor with transcripts.parquet - if ( params.method == 'baysor' ) { + } - BAYSOR_RUN_TRANSCRIPTS_PARQUET ( - ch_bundle_path, - ch_transcripts_parquet, - ch_config - ) - ch_redefined_bundle = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.redefined_bundle - ch_coordinate_space = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.coordinate_space - } + // run baysor with transcripts.parquet + if ( params.method == 'baysor' ) { + BAYSOR_RUN_TRANSCRIPTS_PARQUET ( + ch_bundle_path, + ch_transcripts_parquet, + ch_config + ) + ch_redefined_bundle = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.redefined_bundle + ch_coordinate_space = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.coordinate_space } + } + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SPATIALXE - SPATIALDATA / METADATA LAYER @@ -395,8 +411,8 @@ workflow SPATIALXE { */ if ( params.mode == 'segfree' ) { - // trigger the default segfree workflow if no method is specified - if ( !params.method ) { + // trigger the default segfree workflow if no method or if the method is baysor + if ( !params.method || params.method == 'baysor' ) { BAYSOR_GENERATE_SEGFREE ( ch_transcripts_parquet, @@ -404,26 +420,13 @@ workflow SPATIALXE { ) } - // check it the provided method is part of the methods list - if ( params.method in params.segfree_methods ) { - - // run baysor with transcripts.parquet - if ( params.method == 'baysor' ) { + // run ficture with transcripts.parquet + if ( params.method == 'ficture' ) { - BAYSOR_GENERATE_SEGFREE ( - ch_transcripts_parquet, - ch_config - ) - } - - // run ficture with transcripts.parquet - if ( params.method == 'ficture' ) { - - FICTURE_PREPROCESS_MODEL ( - ch_transcripts_parquet, - ch_features - ) - } + FICTURE_PREPROCESS_MODEL ( + ch_transcripts_parquet, + ch_features + ) } } @@ -448,7 +451,7 @@ workflow SPATIALXE { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ ch_multiqc_config = Channel.fromPath ( - "$projectDir/assets/multiqc_config.yml", + "${projectDir}/assets/multiqc_config.yml", checkIfExists: true ) @@ -472,7 +475,7 @@ workflow SPATIALXE { ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file( params.multiqc_methods_description, checkIfExists: true ) : - file( "$projectDir/assets/methods_description_template.yml", checkIfExists: true ) + file( "${projectDir}/assets/methods_description_template.yml", checkIfExists: true ) ch_methods_description = Channel.value ( methodsDescriptionText ( ch_multiqc_custom_methods_description ) @@ -492,7 +495,7 @@ workflow SPATIALXE { file("${path}/*") } - ch_multiqc_files = ch_multiqc_files.mix ( ch_redefined_bundle_files.collect() ) + ch_multiqc_files = ch_multiqc_files.mix ( ch_redefined_bundle_files.flatten() ) MULTIQC ( ch_multiqc_files.collect(),